diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp index 62084e1c8d30..ab64c3eb4f66 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp @@ -338,19 +338,26 @@ namespace MonitorUtils void OpenWindowOnActiveMonitor(HWND window, HMONITOR monitor) noexcept { - // By default Windows opens new window on primary monitor. - // Try to preserve window width and height, adjust top-left corner if needed. - HMONITOR origin = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); - if (origin == monitor) - { - // Certain applications by design open in last known position, regardless of FancyZones. - // If that position is on currently active monitor, skip custom positioning. - return; - } - WINDOWPLACEMENT placement{}; if (GetWindowPlacement(window, &placement)) { + // By default Windows opens new window on primary monitor. + // Try to preserve window width and height, adjust top-left corner if needed. + HMONITOR origin = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); + if (FancyZonesWindowUtils::IsAlwaysMaximize(window)) + { + // Skip if current window is always maximize by user setting + // and already SW_SHOWMAXIMIZED on currently active monitor + if (!FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(window) || (placement.showCmd == SW_SHOWMAXIMIZED && origin == monitor)) + { + return; + } + } else if (origin == monitor) + { + // Certain applications by design open in last known position, regardless of FancyZones. + // If that position is on currently active monitor, skip custom positioning. + return; + } MONITORINFOEX originMi; originMi.cbSize = sizeof(originMi); if (GetMonitorInfo(origin, &originMi)) diff --git a/src/modules/fancyzones/FancyZonesLib/Settings.cpp b/src/modules/fancyzones/FancyZonesLib/Settings.cpp index 9cc886fe5cd4..458c297b5fab 100644 --- a/src/modules/fancyzones/FancyZonesLib/Settings.cpp +++ b/src/modules/fancyzones/FancyZonesLib/Settings.cpp @@ -45,6 +45,7 @@ namespace NonLocalizable const wchar_t WindowSwitchingToggleID[] = L"fancyzones_windowSwitching"; const wchar_t NextTabHotkeyID[] = L"fancyzones_nextTab_hotkey"; const wchar_t PrevTabHotkeyID[] = L"fancyzones_prevTab_hotkey"; + const wchar_t AlwaysMaximizeID[] = L"fancyzones_always_maximize"; const wchar_t ExcludedAppsID[] = L"fancyzones_excluded_apps"; const wchar_t ZoneHighlightOpacityID[] = L"fancyzones_highlight_opacity"; const wchar_t ShowZoneNumberID[] = L"fancyzones_showZoneNumber"; @@ -206,6 +207,32 @@ void FancyZonesSettings::LoadSettings() } } + // always maximize apps + if (auto val = values.get_string_value(NonLocalizable::AlwaysMaximizeID)) + { + std::wstring apps = std::move(*val); + std::vector alwaysMaximize; + auto alwaysMaximizeUppercase = apps; + CharUpperBuffW(alwaysMaximizeUppercase.data(), static_cast(alwaysMaximizeUppercase.length())); + std::wstring_view view(alwaysMaximizeUppercase); + view = left_trim(trim(view)); + + while (!view.empty()) + { + auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length()); + alwaysMaximize.emplace_back(view.substr(0, pos)); + view.remove_prefix(pos); + view = left_trim(trim(view)); + } + + if (m_settings.alwaysMaximizeArray != alwaysMaximize) + { + m_settings.alwaysMaximize = apps; + m_settings.alwaysMaximizeArray = alwaysMaximize; + NotifyObservers(SettingId::AlwaysMaximize); + } + } + // excluded apps if (auto val = values.get_string_value(NonLocalizable::ExcludedAppsID)) { diff --git a/src/modules/fancyzones/FancyZonesLib/Settings.h b/src/modules/fancyzones/FancyZonesLib/Settings.h index a9bcbeaa172f..45004707d963 100644 --- a/src/modules/fancyzones/FancyZonesLib/Settings.h +++ b/src/modules/fancyzones/FancyZonesLib/Settings.h @@ -56,6 +56,8 @@ struct Settings bool windowSwitching = true; PowerToysSettings::HotkeyObject nextTabHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_NEXT); PowerToysSettings::HotkeyObject prevTabHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_PRIOR); + std::wstring alwaysMaximize = L""; + std::vector alwaysMaximizeArray; std::wstring excludedApps = L""; std::vector excludedAppsArray; }; diff --git a/src/modules/fancyzones/FancyZonesLib/SettingsConstants.h b/src/modules/fancyzones/FancyZonesLib/SettingsConstants.h index a789d864848d..56cbd868e584 100644 --- a/src/modules/fancyzones/FancyZonesLib/SettingsConstants.h +++ b/src/modules/fancyzones/FancyZonesLib/SettingsConstants.h @@ -31,6 +31,7 @@ enum class SettingId WindowSwitching, NextTabHotkey, PrevTabHotkey, + AlwaysMaximize, ExcludedApps, AllowSnapChildWindows, DisableRoundCornersOnSnapping, diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp index 77bad34683a7..ae554123138e 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp @@ -185,6 +185,24 @@ bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window) return false; } +bool FancyZonesWindowUtils::IsAlwaysMaximize(HWND window) +{ + std::wstring processPath = get_process_path_waiting_uwp(window); + CharUpperBuffW(const_cast(processPath).data(), static_cast(processPath.length())); + if (find_app_name_in_path(processPath, FancyZonesSettings::settings().alwaysMaximizeArray)) + { + return true; + } + + return false; +} + +bool FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept +{ + auto style = GetWindowLong(window, GWL_STYLE); + return ((style & WS_THICKFRAME) == WS_THICKFRAME && (style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX && (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX); +} + bool FancyZonesWindowUtils::IsExcluded(HWND window) { std::wstring processPath = get_process_path_waiting_uwp(window); @@ -269,9 +287,16 @@ void FancyZonesWindowUtils::SizeWindowToRect(HWND window, RECT rect, BOOL snapZo if (IsWindowVisible(window)) { // If is not snap zone then need keep maximize state (move to active monitor) - if (!snapZone && placement.showCmd == SW_SHOWMAXIMIZED) + if (!snapZone) { - maximizeLater = true; + if (HasThickFrameAndMinimizeMaximizeButtons(window) && IsAlwaysMaximize(window)) + { + placement.showCmd = SW_SHOWMAXIMIZED; + } + if (placement.showCmd == SW_SHOWMAXIMIZED) + { + maximizeLater = true; + } } // Do not restore minimized windows. We change their placement though so they restore to the correct zone. diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h index 9382bccf0b25..1c7d59717209 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h @@ -26,6 +26,8 @@ namespace FancyZonesWindowUtils bool IsProcessOfWindowElevated(HWND window); // If HWND is already dead, we assume it wasn't elevated + bool IsAlwaysMaximize(HWND window); + bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept; bool IsExcluded(HWND window); bool IsExcludedByUser(const HWND& hwnd, const std::wstring& processPath) noexcept; bool IsExcludedByDefault(const HWND& hwnd, const std::wstring& processPath) noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/trace.cpp b/src/modules/fancyzones/FancyZonesLib/trace.cpp index 4c6024fe5aeb..d00e5f137847 100644 --- a/src/modules/fancyzones/FancyZonesLib/trace.cpp +++ b/src/modules/fancyzones/FancyZonesLib/trace.cpp @@ -66,6 +66,7 @@ #define WindowSwitchingToggleKey "WindowSwitchingToggle" #define NextTabHotkey "NextTabHotkey" #define PrevTabHotkey "PrevTabHotkey" +#define AlwaysMaximizeCountKey "AlwaysMaximizeCount" #define ExcludedAppsCountKey "ExcludedAppsCount" #define KeyboardValueKey "KeyboardValue" #define ActiveSetKey "ActiveSet" @@ -332,6 +333,7 @@ void Trace::SettingsTelemetry(const Settings& settings) noexcept TraceLoggingBoolean(settings.windowSwitching, WindowSwitchingToggleKey), TraceLoggingWideString(nextTabHotkeyStr.c_str(), NextTabHotkey), TraceLoggingWideString(prevTabHotkeyStr.c_str(), PrevTabHotkey), + TraceLoggingInt32(static_cast(settings.excludedAppsArray.size()), AlwaysMaximizeCountKey), TraceLoggingInt32(static_cast(settings.excludedAppsArray.size()), ExcludedAppsCountKey)); } diff --git a/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs b/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs index 4b3fe2f33d9a..9370bac085ab 100644 --- a/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs +++ b/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs @@ -47,6 +47,7 @@ public FZConfigProperties() FancyzonesAllowPopupWindowSnap = new BoolProperty(); FancyzonesAllowChildWindowSnap = new BoolProperty(); FancyzonesDisableRoundCornersOnSnap = new BoolProperty(); + FancyzonesALwaysMaximize = new StringProperty(); FancyzonesExcludedApps = new StringProperty(); FancyzonesInActiveColor = new StringProperty(ConfigDefaults.DefaultFancyZonesInActiveColor); FancyzonesBorderColor = new StringProperty(ConfigDefaults.DefaultFancyzonesBorderColor); @@ -136,6 +137,9 @@ public FZConfigProperties() [JsonPropertyName("fancyzones_prevTab_hotkey")] public KeyboardKeysProperty FancyzonesPrevTabHotkey { get; set; } + [JsonPropertyName("fancyzones_always_maximize")] + public StringProperty FancyzonesALwaysMaximize { get; set; } + [JsonPropertyName("fancyzones_excluded_apps")] public StringProperty FancyzonesExcludedApps { get; set; } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml index ce02fd168cff..69724aa7ff3d 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml @@ -159,6 +159,25 @@ + + + + + + + + Excludes an application from snapping to zones and will only react to Windows Snap - add one application name per line + + Always maximize apps + + + Require (Move newly created windows to the current active monitor) - add one application name per line + + + Example: outlook.exe + Don't translate outlook.exe + Opacity (%) diff --git a/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs index 4280d086cf65..f8d5cfacc199 100644 --- a/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs @@ -85,6 +85,7 @@ public FancyZonesViewModel(SettingsUtils settingsUtils, ISettingsRepository