diff --git a/src/modules/fancyzones/dll/dllmain.cpp b/src/modules/fancyzones/dll/dllmain.cpp index 41c3e62677..a5d1a63158 100644 --- a/src/modules/fancyzones/dll/dllmain.cpp +++ b/src/modules/fancyzones/dll/dllmain.cpp @@ -32,22 +32,32 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser // This function is exported and called from FancyZonesEditor.exe to save a layout from the editor. STDAPI PersistZoneSet( PCWSTR activeKey, // Registry key holding ActiveZoneSet - PCWSTR resolutionKey, // Registry key for screen resolution + HMONITOR monitor, WORD layoutId, // LayoutModel Id int zoneCount, // Number of zones in zones int zones[]) // Array of zones serialized in left/top/right/bottom chunks { // See if we have already persisted this layout we can update. - UUID id{GUID_NULL}; - if (wil::unique_hkey key{ RegistryHelpers::OpenKey(resolutionKey) }) - { - ZoneSetPersistedData data{}; - DWORD dataSize = sizeof(data); - wchar_t value[256]{}; - DWORD valueLength = ARRAYSIZE(value); - DWORD i = 0; - while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast(&data), &dataSize) == ERROR_SUCCESS) - { + std::wstringstream stream; + MONITORINFOEX mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(monitor, &mi)) + { + stream << (mi.rcMonitor.right - mi.rcMonitor.left) << "_"; + stream << (mi.rcMonitor.bottom - mi.rcMonitor.top); + } + + std::wstring resolutionKey(stream.str()); + UUID id{GUID_NULL}; + if (wil::unique_hkey key{ RegistryHelpers::OpenKey(resolutionKey.c_str()) }) + { + ZoneSetPersistedData data{}; + DWORD dataSize = sizeof(data); + wchar_t value[256]{}; + DWORD valueLength = ARRAYSIZE(value); + DWORD i = 0; + while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast(&data), &dataSize) == ERROR_SUCCESS) + { if (data.LayoutId == layoutId) { if (data.ZoneCount == zoneCount) @@ -73,8 +83,8 @@ STDAPI PersistZoneSet( ZoneSetConfig( id, layoutId, - MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), - resolutionKey, + reinterpret_cast(monitor), + resolutionKey.c_str(), ZoneSetLayout::Custom, 0, 0, 0)); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs index 6115581a74..9d2b719cad 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs @@ -20,7 +20,6 @@ namespace FancyZonesEditor private ushort _idInitial = 0; public App() { - //init settings _settings = new Settings(); } @@ -64,8 +63,7 @@ namespace FancyZonesEditor } foundModel.IsSelected = true; - // TODO: multimon - // Pass in the correct args to show on the desired monitor + EditorOverlay overlay = new EditorOverlay(); overlay.Show(); overlay.DataContext = foundModel; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs index ba3b26b82d..c0e74cdfde 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs @@ -41,7 +41,7 @@ namespace FancyZonesEditor } else if (xDelta > 0) { - xDelta = Math.Min(xDelta, c_workArea.Width - rect.Width - rect.X); + xDelta = Math.Min(xDelta, _settings.WorkArea.Width - rect.Width - rect.X); } if (yDelta < 0) @@ -50,7 +50,7 @@ namespace FancyZonesEditor } else if (yDelta > 0) { - yDelta = Math.Min(yDelta, c_workArea.Height - rect.Height - rect.Y); + yDelta = Math.Min(yDelta, _settings.WorkArea.Height - rect.Height - rect.Y); } rect.X += (int) xDelta; @@ -69,13 +69,13 @@ namespace FancyZonesEditor { int newWidth = rect.Width + (int) xDelta; - if (newWidth < 48) + if (newWidth < c_minZoneSize) { - newWidth = 48; + newWidth = c_minZoneSize; } - else if (newWidth > (c_workArea.Width - rect.X)) + else if (newWidth > (_settings.WorkArea.Width - rect.X)) { - newWidth = (int) c_workArea.Width - rect.X; + newWidth = (int) _settings.WorkArea.Width - rect.X; } MinWidth = rect.Width = newWidth; } @@ -84,13 +84,13 @@ namespace FancyZonesEditor { int newHeight = rect.Height + (int)yDelta; - if (newHeight < 48) + if (newHeight < c_minZoneSize) { - newHeight = 48; + newHeight = c_minZoneSize; } - else if (newHeight > (c_workArea.Height - rect.Y)) + else if (newHeight > (_settings.WorkArea.Height - rect.Y)) { - newHeight = (int)c_workArea.Height - rect.Y; + newHeight = (int)_settings.WorkArea.Height - rect.Y; } MinHeight = rect.Height = newHeight; } @@ -98,10 +98,7 @@ namespace FancyZonesEditor } private static int c_zIndex = 0; - - // TODO: multimon - // This needs to be the work area of the monitor we get launched on - private static Rect c_workArea = System.Windows.SystemParameters.WorkArea; + private static int c_minZoneSize = 48; protected override void OnPreviewMouseDown(MouseButtonEventArgs e) { @@ -163,5 +160,7 @@ namespace FancyZonesEditor ((Panel)Parent).Children.Remove(this); Model.RemoveZoneAt(ZoneIndex); } + + private Settings _settings = ((App)Application.Current).ZoneSettings; } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml.cs index e537e9b523..9a83516ae1 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml.cs @@ -1,138 +1,134 @@ -using FancyZonesEditor.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.NetworkInformation; -using System.Security.RightsManagement; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; - -namespace FancyZonesEditor -{ - /// - /// Interaction logic for Window1.xaml - /// - public partial class EditorOverlay : Window - { - public Int32Rect[] GetZoneRects() - { - // TODO: the ideal here is that the ArrangeRects logic is entirely inside the model, so we don't have to walk the UIElement children to get the rect info - Panel previewPanel = null; - - if (_editor != null) - { - GridEditor gridEditor = _editor as GridEditor; - if (gridEditor != null) - { - previewPanel = gridEditor.PreviewPanel; - } - else - { - //CanvasEditor - previewPanel = ((CanvasEditor)_editor).Preview; - } - } - else - { - previewPanel = _layoutPreview.PreviewPanel; - } - - var count = previewPanel.Children.Count; - Int32Rect[] zones = new Int32Rect[count]; - - int i = 0; - foreach (FrameworkElement child in previewPanel.Children) - { - Point topLeft = child.TransformToAncestor(previewPanel).Transform(new Point()); - - var right = topLeft.X + child.ActualWidth; - var bottom = topLeft.Y + child.ActualHeight; - zones[i].X = (int)topLeft.X; - zones[i].Y = (int)topLeft.Y; - zones[i].Width = (int)child.ActualWidth; - zones[i].Height = (int)child.ActualHeight; - i++; - } - - return zones; - } - - public static EditorOverlay Current; - public EditorOverlay() - { - InitializeComponent(); - Current = this; +using FancyZonesEditor.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Security.RightsManagement; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; - // TODO: multimon - // Need to set Left and Top to the correct monitor based on the - // foreground window passed in the command line arguments - Rect workArea = System.Windows.SystemParameters.WorkArea; - Left = workArea.Left; - Top = workArea.Top; - Width = workArea.Width; - Height = workArea.Height; +namespace FancyZonesEditor +{ + /// + /// Interaction logic for Window1.xaml + /// + public partial class EditorOverlay : Window + { + public Int32Rect[] GetZoneRects() + { + // TODO: the ideal here is that the ArrangeRects logic is entirely inside the model, so we don't have to walk the UIElement children to get the rect info + Panel previewPanel = null; + + if (_editor != null) + { + GridEditor gridEditor = _editor as GridEditor; + if (gridEditor != null) + { + previewPanel = gridEditor.PreviewPanel; + } + else + { + //CanvasEditor + previewPanel = ((CanvasEditor)_editor).Preview; + } + } + else + { + previewPanel = _layoutPreview.PreviewPanel; + } + + var count = previewPanel.Children.Count; + Int32Rect[] zones = new Int32Rect[count]; + + int i = 0; + foreach (FrameworkElement child in previewPanel.Children) + { + Point topLeft = child.TransformToAncestor(previewPanel).Transform(new Point()); + + var right = topLeft.X + child.ActualWidth; + var bottom = topLeft.Y + child.ActualHeight; + zones[i].X = (int)topLeft.X; + zones[i].Y = (int)topLeft.Y; + zones[i].Width = (int)child.ActualWidth; + zones[i].Height = (int)child.ActualHeight; + i++; + } + + return zones; } - void onLoad(object sender, RoutedEventArgs e) - { - ShowLayoutPicker(); - } - - public void ShowLayoutPicker() - { - DataContext = null; - - _editor = null; - _layoutPreview = new LayoutPreview(); - _layoutPreview.IsActualSize = true; - _layoutPreview.Opacity = 0.5; - Content = _layoutPreview; - - MainWindow window = new MainWindow(); - window.Owner = this; - window.Show(); - } - - // These event handlers are used to track the current state of the Shift and Ctrl keys on the keyboard - // They reflect that current state into properties on the Settings object, which the Zone view will listen to in editing mode - protected override void OnPreviewKeyDown(KeyEventArgs e) - { - _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift); - _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control); - base.OnPreviewKeyDown(e); - } - - protected override void OnPreviewKeyUp(KeyEventArgs e) - { - _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift); - _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control); - base.OnPreviewKeyUp(e); - } - - public void Edit() - { - _layoutPreview = null; - if (DataContext is GridLayoutModel) - { - _editor = new GridEditor(); - } - else if (DataContext is CanvasLayoutModel) - { - _editor = new CanvasEditor(); - } - Content = _editor; - } - - private Settings _settings = ((App)Application.Current).ZoneSettings; - private LayoutPreview _layoutPreview; - private UserControl _editor; - } -} + public static EditorOverlay Current; + public EditorOverlay() + { + InitializeComponent(); + Current = this; + + Left = _settings.WorkArea.Left; + Top = _settings.WorkArea.Top; + Width = _settings.WorkArea.Width; + Height = _settings.WorkArea.Height; + } + + void onLoad(object sender, RoutedEventArgs e) + { + ShowLayoutPicker(); + } + + public void ShowLayoutPicker() + { + DataContext = null; + + _editor = null; + _layoutPreview = new LayoutPreview(); + _layoutPreview.IsActualSize = true; + _layoutPreview.Opacity = 0.5; + Content = _layoutPreview; + + MainWindow window = new MainWindow(); + window.Owner = this; + window.Show(); + } + + // These event handlers are used to track the current state of the Shift and Ctrl keys on the keyboard + // They reflect that current state into properties on the Settings object, which the Zone view will listen to in editing mode + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift); + _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control); + base.OnPreviewKeyDown(e); + } + + protected override void OnPreviewKeyUp(KeyEventArgs e) + { + _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift); + _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control); + base.OnPreviewKeyUp(e); + } + + public void Edit() + { + _layoutPreview = null; + if (DataContext is GridLayoutModel) + { + _editor = new GridEditor(); + } + else if (DataContext is CanvasLayoutModel) + { + _editor = new CanvasEditor(); + } + Content = _editor; + } + + private Settings _settings = ((App)Application.Current).ZoneSettings; + private LayoutPreview _layoutPreview; + private UserControl _editor; + } +} diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs index 6a612d958b..8448677b84 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs @@ -155,7 +155,7 @@ namespace FancyZonesEditor.Models internal delegate int PersistZoneSet( [MarshalAs(UnmanagedType.LPWStr)] string activeKey, - [MarshalAs(UnmanagedType.LPWStr)] string key, + uint monitor, ushort layoutId, int zoneCount, [MarshalAs(UnmanagedType.LPArray)] int[] zoneArray); @@ -205,18 +205,15 @@ namespace FancyZonesEditor.Models string[] args = Environment.GetCommandLineArgs(); if (args.Length > 1) { - // args[1] = registry key value of currently active ZoneSet - // args[2] = id of layout to load at startup - string uniqueId = args[1]; - - // TODO: multimon - double height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; - double width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; - var key = width.ToString() + "_" + height.ToString(); + uint monitor = 0; + if (args.Length > 3) + { + monitor = uint.Parse(args[4]); + } var persistZoneSet = Marshal.GetDelegateForFunctionPointer(pfn); - persistZoneSet(uniqueId, key, _id, zoneCount, zoneArray); + persistZoneSet(uniqueId, monitor, _id, zoneCount, zoneArray); } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Settings.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Settings.cs index 0f10102924..6445554e66 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Settings.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Settings.cs @@ -23,11 +23,25 @@ namespace FancyZonesEditor { public Settings() { - Rect workArea = System.Windows.SystemParameters.WorkArea; + _workArea = System.Windows.SystemParameters.WorkArea; + string[] args = Environment.GetCommandLineArgs(); + if (args.Length > 2) + { + var foregroundWindow = uint.Parse(args[3]); + var screen = System.Windows.Forms.Screen.FromHandle(new IntPtr(foregroundWindow)); + + var graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero); + float dpi = graphics.DpiX / 96; + _workArea = new Rect( + screen.WorkingArea.X / dpi, + screen.WorkingArea.Y / dpi, + screen.WorkingArea.Width / dpi, + screen.WorkingArea.Height / dpi); + } // Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid _defaultModels = new List(5); - _focusModel = new CanvasLayoutModel("Focus", c_focusModelId, (int)workArea.Width, (int)workArea.Height); + _focusModel = new CanvasLayoutModel("Focus", c_focusModelId, (int)_workArea.Width, (int)_workArea.Height); _defaultModels.Add(_focusModel); _columnsModel = new GridLayoutModel("Columns", c_columnsModelId); @@ -46,7 +60,7 @@ namespace FancyZonesEditor _priorityGridModel = new GridLayoutModel("Priority Grid", c_priorityGridModelId); _defaultModels.Add(_priorityGridModel); - _blankCustomModel = new CanvasLayoutModel("Create new custom", c_blankCustomModelId, (int)workArea.Width, (int)workArea.Height); + _blankCustomModel = new CanvasLayoutModel("Create new custom", c_blankCustomModelId, (int)_workArea.Width, (int)_workArea.Height); _zoneCount = (int)Registry.GetValue(FullRegistryPath, "ZoneCount", 3); _spacing = (int)Registry.GetValue(FullRegistryPath, "Spacing", 16); @@ -134,6 +148,12 @@ namespace FancyZonesEditor } private bool _isCtrlKeyPressed; + public Rect WorkArea + { + get { return _workArea; } + } + private Rect _workArea; + // UpdateLayoutModels // Update the five default layouts based on the new ZoneCount private void UpdateLayoutModels() diff --git a/src/modules/fancyzones/lib/FancyZones.cpp b/src/modules/fancyzones/lib/FancyZones.cpp index cd1266bc2b..372eeb5718 100644 --- a/src/modules/fancyzones/lib/FancyZones.cpp +++ b/src/modules/fancyzones/lib/FancyZones.cpp @@ -237,18 +237,19 @@ void FancyZones::ToggleEditor() noexcept m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr)); } - // TODO: multimon support - // Pass in args so that the editor shows up on the correct monitor - // This can be an HWND, HMONITOR, or the X/Y/Width/Height of the monitor's work area, (whichever works best). - if (const HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY)) + const HWND foregroundWindow = GetForegroundWindow(); + if (const HMONITOR monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY)) { std::shared_lock readLock(m_lock); auto iter = m_zoneWindowMap.find(monitor); if (iter != m_zoneWindowMap.end()) { - // Pass command line args to the editor to tell it which layout it should pick by default - auto activeZoneSet = iter->second->ActiveZoneSet(); - std::wstring params = iter->second->UniqueId() + L" " + std::to_wstring(activeZoneSet->LayoutId()); + const std::wstring params = + iter->second->UniqueId() + L" " + + std::to_wstring(iter->second->ActiveZoneSet()->LayoutId()) + L" " + + std::to_wstring(reinterpret_cast(foregroundWindow)) + L" " + + std::to_wstring(reinterpret_cast(monitor)); + SHELLEXECUTEINFO sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; sei.lpFile = L"modules\\FancyZonesEditor.exe";