Editor should come up on the monitor with the foreground window. Defaults to primary monitor if there is no foreground window.

This commit is contained in:
Bret Anderson 2019-09-08 01:47:12 -07:00
parent 5f5402aa0a
commit e562b29ecd
7 changed files with 206 additions and 185 deletions

View File

@ -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. // This function is exported and called from FancyZonesEditor.exe to save a layout from the editor.
STDAPI PersistZoneSet( STDAPI PersistZoneSet(
PCWSTR activeKey, // Registry key holding ActiveZoneSet PCWSTR activeKey, // Registry key holding ActiveZoneSet
PCWSTR resolutionKey, // Registry key for screen resolution HMONITOR monitor,
WORD layoutId, // LayoutModel Id WORD layoutId, // LayoutModel Id
int zoneCount, // Number of zones in zones int zoneCount, // Number of zones in zones
int zones[]) // Array of zones serialized in left/top/right/bottom chunks int zones[]) // Array of zones serialized in left/top/right/bottom chunks
{ {
// See if we have already persisted this layout we can update. // See if we have already persisted this layout we can update.
UUID id{GUID_NULL}; std::wstringstream stream;
if (wil::unique_hkey key{ RegistryHelpers::OpenKey(resolutionKey) }) MONITORINFOEX mi;
{ mi.cbSize = sizeof(mi);
ZoneSetPersistedData data{}; if (GetMonitorInfo(monitor, &mi))
DWORD dataSize = sizeof(data); {
wchar_t value[256]{}; stream << (mi.rcMonitor.right - mi.rcMonitor.left) << "_";
DWORD valueLength = ARRAYSIZE(value); stream << (mi.rcMonitor.bottom - mi.rcMonitor.top);
DWORD i = 0; }
while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
{ 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<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
{
if (data.LayoutId == layoutId) if (data.LayoutId == layoutId)
{ {
if (data.ZoneCount == zoneCount) if (data.ZoneCount == zoneCount)
@ -73,8 +83,8 @@ STDAPI PersistZoneSet(
ZoneSetConfig( ZoneSetConfig(
id, id,
layoutId, layoutId,
MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), reinterpret_cast<HMONITOR>(monitor),
resolutionKey, resolutionKey.c_str(),
ZoneSetLayout::Custom, ZoneSetLayout::Custom,
0, 0, 0)); 0, 0, 0));

View File

@ -20,7 +20,6 @@ namespace FancyZonesEditor
private ushort _idInitial = 0; private ushort _idInitial = 0;
public App() public App()
{ {
//init settings
_settings = new Settings(); _settings = new Settings();
} }
@ -64,8 +63,7 @@ namespace FancyZonesEditor
} }
foundModel.IsSelected = true; foundModel.IsSelected = true;
// TODO: multimon
// Pass in the correct args to show on the desired monitor
EditorOverlay overlay = new EditorOverlay(); EditorOverlay overlay = new EditorOverlay();
overlay.Show(); overlay.Show();
overlay.DataContext = foundModel; overlay.DataContext = foundModel;

View File

@ -41,7 +41,7 @@ namespace FancyZonesEditor
} }
else if (xDelta > 0) 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) if (yDelta < 0)
@ -50,7 +50,7 @@ namespace FancyZonesEditor
} }
else if (yDelta > 0) 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; rect.X += (int) xDelta;
@ -69,13 +69,13 @@ namespace FancyZonesEditor
{ {
int newWidth = rect.Width + (int) xDelta; 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; MinWidth = rect.Width = newWidth;
} }
@ -84,13 +84,13 @@ namespace FancyZonesEditor
{ {
int newHeight = rect.Height + (int)yDelta; 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; MinHeight = rect.Height = newHeight;
} }
@ -98,10 +98,7 @@ namespace FancyZonesEditor
} }
private static int c_zIndex = 0; private static int c_zIndex = 0;
private static int c_minZoneSize = 48;
// 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;
protected override void OnPreviewMouseDown(MouseButtonEventArgs e) protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{ {
@ -163,5 +160,7 @@ namespace FancyZonesEditor
((Panel)Parent).Children.Remove(this); ((Panel)Parent).Children.Remove(this);
Model.RemoveZoneAt(ZoneIndex); Model.RemoveZoneAt(ZoneIndex);
} }
private Settings _settings = ((App)Application.Current).ZoneSettings;
} }
} }

View File

@ -1,138 +1,134 @@
using FancyZonesEditor.Models; using FancyZonesEditor.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Security.RightsManagement; using System.Security.RightsManagement;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Shapes; using System.Windows.Shapes;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
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;
// TODO: multimon namespace FancyZonesEditor
// Need to set Left and Top to the correct monitor based on the {
// foreground window passed in the command line arguments /// <summary>
Rect workArea = System.Windows.SystemParameters.WorkArea; /// Interaction logic for Window1.xaml
Left = workArea.Left; /// </summary>
Top = workArea.Top; public partial class EditorOverlay : Window
Width = workArea.Width; {
Height = workArea.Height; 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) public static EditorOverlay Current;
{ public EditorOverlay()
ShowLayoutPicker(); {
} InitializeComponent();
Current = this;
public void ShowLayoutPicker()
{ Left = _settings.WorkArea.Left;
DataContext = null; Top = _settings.WorkArea.Top;
Width = _settings.WorkArea.Width;
_editor = null; Height = _settings.WorkArea.Height;
_layoutPreview = new LayoutPreview(); }
_layoutPreview.IsActualSize = true;
_layoutPreview.Opacity = 0.5; void onLoad(object sender, RoutedEventArgs e)
Content = _layoutPreview; {
ShowLayoutPicker();
MainWindow window = new MainWindow(); }
window.Owner = this;
window.Show(); public void ShowLayoutPicker()
} {
DataContext = null;
// 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 _editor = null;
protected override void OnPreviewKeyDown(KeyEventArgs e) _layoutPreview = new LayoutPreview();
{ _layoutPreview.IsActualSize = true;
_settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift); _layoutPreview.Opacity = 0.5;
_settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control); Content = _layoutPreview;
base.OnPreviewKeyDown(e);
} MainWindow window = new MainWindow();
window.Owner = this;
protected override void OnPreviewKeyUp(KeyEventArgs e) window.Show();
{ }
_settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
_settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control); // These event handlers are used to track the current state of the Shift and Ctrl keys on the keyboard
base.OnPreviewKeyUp(e); // 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)
{
public void Edit() _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
{ _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
_layoutPreview = null; base.OnPreviewKeyDown(e);
if (DataContext is GridLayoutModel) }
{
_editor = new GridEditor(); protected override void OnPreviewKeyUp(KeyEventArgs e)
} {
else if (DataContext is CanvasLayoutModel) _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
{ _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
_editor = new CanvasEditor(); base.OnPreviewKeyUp(e);
} }
Content = _editor;
} public void Edit()
{
private Settings _settings = ((App)Application.Current).ZoneSettings; _layoutPreview = null;
private LayoutPreview _layoutPreview; if (DataContext is GridLayoutModel)
private UserControl _editor; {
} _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;
}
}

View File

@ -155,7 +155,7 @@ namespace FancyZonesEditor.Models
internal delegate int PersistZoneSet( internal delegate int PersistZoneSet(
[MarshalAs(UnmanagedType.LPWStr)] string activeKey, [MarshalAs(UnmanagedType.LPWStr)] string activeKey,
[MarshalAs(UnmanagedType.LPWStr)] string key, uint monitor,
ushort layoutId, ushort layoutId,
int zoneCount, int zoneCount,
[MarshalAs(UnmanagedType.LPArray)] int[] zoneArray); [MarshalAs(UnmanagedType.LPArray)] int[] zoneArray);
@ -205,18 +205,15 @@ namespace FancyZonesEditor.Models
string[] args = Environment.GetCommandLineArgs(); string[] args = Environment.GetCommandLineArgs();
if (args.Length > 1) 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]; string uniqueId = args[1];
uint monitor = 0;
// TODO: multimon if (args.Length > 3)
double height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; {
double width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; monitor = uint.Parse(args[4]);
var key = width.ToString() + "_" + height.ToString(); }
var persistZoneSet = Marshal.GetDelegateForFunctionPointer<Native.PersistZoneSet>(pfn); var persistZoneSet = Marshal.GetDelegateForFunctionPointer<Native.PersistZoneSet>(pfn);
persistZoneSet(uniqueId, key, _id, zoneCount, zoneArray); persistZoneSet(uniqueId, monitor, _id, zoneCount, zoneArray);
} }
} }

View File

@ -23,11 +23,25 @@ namespace FancyZonesEditor
{ {
public Settings() 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 // Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
_defaultModels = new List<LayoutModel>(5); _defaultModels = new List<LayoutModel>(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); _defaultModels.Add(_focusModel);
_columnsModel = new GridLayoutModel("Columns", c_columnsModelId); _columnsModel = new GridLayoutModel("Columns", c_columnsModelId);
@ -46,7 +60,7 @@ namespace FancyZonesEditor
_priorityGridModel = new GridLayoutModel("Priority Grid", c_priorityGridModelId); _priorityGridModel = new GridLayoutModel("Priority Grid", c_priorityGridModelId);
_defaultModels.Add(_priorityGridModel); _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); _zoneCount = (int)Registry.GetValue(FullRegistryPath, "ZoneCount", 3);
_spacing = (int)Registry.GetValue(FullRegistryPath, "Spacing", 16); _spacing = (int)Registry.GetValue(FullRegistryPath, "Spacing", 16);
@ -134,6 +148,12 @@ namespace FancyZonesEditor
} }
private bool _isCtrlKeyPressed; private bool _isCtrlKeyPressed;
public Rect WorkArea
{
get { return _workArea; }
}
private Rect _workArea;
// UpdateLayoutModels // UpdateLayoutModels
// Update the five default layouts based on the new ZoneCount // Update the five default layouts based on the new ZoneCount
private void UpdateLayoutModels() private void UpdateLayoutModels()

View File

@ -237,18 +237,19 @@ void FancyZones::ToggleEditor() noexcept
m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr)); m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr));
} }
// TODO: multimon support const HWND foregroundWindow = GetForegroundWindow();
// Pass in args so that the editor shows up on the correct monitor if (const HMONITOR monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY))
// 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))
{ {
std::shared_lock readLock(m_lock); std::shared_lock readLock(m_lock);
auto iter = m_zoneWindowMap.find(monitor); auto iter = m_zoneWindowMap.find(monitor);
if (iter != m_zoneWindowMap.end()) if (iter != m_zoneWindowMap.end())
{ {
// Pass command line args to the editor to tell it which layout it should pick by default const std::wstring params =
auto activeZoneSet = iter->second->ActiveZoneSet(); iter->second->UniqueId() + L" " +
std::wstring params = iter->second->UniqueId() + L" " + std::to_wstring(activeZoneSet->LayoutId()); std::to_wstring(iter->second->ActiveZoneSet()->LayoutId()) + L" " +
std::to_wstring(reinterpret_cast<UINT_PTR>(foregroundWindow)) + L" " +
std::to_wstring(reinterpret_cast<UINT_PTR>(monitor));
SHELLEXECUTEINFO sei{ sizeof(sei) }; SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"modules\\FancyZonesEditor.exe"; sei.lpFile = L"modules\\FancyZonesEditor.exe";