// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using FancyZonesEditor.Logs; using FancyZonesEditor.Models; namespace FancyZonesEditor { public class Overlay { private MainWindow _mainWindow; private LayoutPreview _layoutPreview; private UserControl _editorLayout; private EditorWindow _editorWindow; public List Monitors { get; private set; } public Rect WorkArea { get { if (Monitors.Count > 0 && CurrentDesktop < Monitors.Count) { return Monitors[CurrentDesktop].Device.WorkAreaRect; } return default; } } public LayoutSettings CurrentLayoutSettings { get { if (Monitors.Count > 0 && CurrentDesktop < Monitors.Count) { return Monitors[CurrentDesktop].Settings; } return new LayoutSettings(); } } public Window CurrentLayoutWindow { get { if (Monitors.Count > 0 && CurrentDesktop < Monitors.Count) { return Monitors[CurrentDesktop].Window; } return default; } } public List WorkAreas { get; private set; } public object CurrentDataContext { get { return _dataContext; } set { _dataContext = value; CurrentLayoutWindow.DataContext = value; } } private object _dataContext; public int DesktopsCount { get { return Monitors.Count; } } public int CurrentDesktop { get { return _currentDesktop; } set { if (value != _currentDesktop) { if (value < 0 || value >= DesktopsCount) { return; } _currentDesktop = value; MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings; if (settings != null) { settings.SetAppliedModel(null); settings.UpdateDefaultModels(); } Update(); } } } private int _currentDesktop; public bool SpanZonesAcrossMonitors { get; set; } public bool MultiMonitorMode { get { return DesktopsCount > 1 && !SpanZonesAcrossMonitors; } } public Overlay() { WorkAreas = new List(); Monitors = new List(); } public void Show() { Logger.LogTrace(); var mainWindowSettings = ((App)Application.Current).MainWindowSettings; if (_layoutPreview != null) { mainWindowSettings.PropertyChanged -= _layoutPreview.ZoneSettings_PropertyChanged; } _layoutPreview = new LayoutPreview { IsActualSize = true, Opacity = 1, }; mainWindowSettings.PropertyChanged += _layoutPreview.ZoneSettings_PropertyChanged; ShowLayout(); OpenMainWindow(); } public void ShowLayout() { Logger.LogTrace(); MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings; CurrentDataContext = settings.UpdateSelectedLayoutModel(); var window = CurrentLayoutWindow; window.Content = _layoutPreview; window.DataContext = CurrentDataContext; if (_layoutPreview != null) { _layoutPreview.UpdatePreview(); } for (int i = 0; i < DesktopsCount; i++) { if (!Monitors[i].Window.IsVisible) { Monitors[i].Window.Show(); } } } public void SetLayoutSettings(Monitor monitor, LayoutModel model) { if (model == null) { return; } monitor.Settings.ZonesetUuid = model.Uuid; monitor.Settings.Type = model.Type; monitor.Settings.SensitivityRadius = model.SensitivityRadius; monitor.Settings.ZoneCount = model.TemplateZoneCount; if (model is GridLayoutModel grid) { monitor.Settings.ShowSpacing = grid.ShowSpacing; monitor.Settings.Spacing = grid.Spacing; } else { monitor.Settings.ShowSpacing = false; monitor.Settings.Spacing = 0; } } public void OpenEditor(LayoutModel model) { Logger.LogTrace(); _layoutPreview = null; if (CurrentDataContext is GridLayoutModel) { _editorLayout = new GridEditor(); } else if (CurrentDataContext is CanvasLayoutModel) { _editorLayout = new CanvasEditor(); } CurrentLayoutWindow.Content = _editorLayout; if (model is GridLayoutModel) { _editorWindow = new GridEditorWindow(); } else { _editorWindow = new CanvasEditorWindow(); } _editorWindow.Owner = Monitors[App.Overlay.CurrentDesktop].Window; _editorWindow.DataContext = model; _editorWindow.Show(); } public void CloseEditor() { Logger.LogTrace(); var mainWindowSettings = ((App)Application.Current).MainWindowSettings; _editorLayout = null; if (_layoutPreview != null) { mainWindowSettings.PropertyChanged -= _layoutPreview.ZoneSettings_PropertyChanged; } _layoutPreview = new LayoutPreview { IsActualSize = true, Opacity = 1, }; mainWindowSettings.PropertyChanged += _layoutPreview.ZoneSettings_PropertyChanged; CurrentLayoutWindow.Content = _layoutPreview; OpenMainWindow(); } public void FocusEditor() { if (_editorLayout == null) { return; } if (_editorLayout is CanvasEditor canvasEditor) { canvasEditor.FocusZone(); } else if (_editorLayout is GridEditor gridEditor) { gridEditor.FocusZone(); } } public void FocusEditorWindow() { if (_editorWindow != null) { _editorWindow.Focus(); } } public void CloseLayoutWindow() { for (int i = 0; i < DesktopsCount; i++) { Monitors[i].Window.Close(); } } public double ScaleCoordinateWithCurrentMonitorDpi(double coordinate) { if (Monitors.Count == 0) { return coordinate; } double minimalDpi = Monitors[0].Device.Dpi; foreach (Monitor monitor in Monitors) { if (minimalDpi > monitor.Device.Dpi) { minimalDpi = monitor.Device.Dpi; } } if (minimalDpi == 0 || Monitors[CurrentDesktop].Device.Dpi == 0) { return coordinate; } double scaleFactor = minimalDpi / Monitors[CurrentDesktop].Device.Dpi; return Math.Round(coordinate * scaleFactor); } private void Update() { CloseLayout(); if (_mainWindow != null) { _mainWindow.Update(); } ShowLayout(); } private void CloseLayout() { var window = CurrentLayoutWindow; window.Content = null; window.DataContext = null; } private void OpenMainWindow() { if (_mainWindow == null) { _mainWindow = new MainWindow(SpanZonesAcrossMonitors, WorkArea); } // reset main window owner to keep it on the top _mainWindow.Owner = CurrentLayoutWindow; _mainWindow.ShowActivated = true; _mainWindow.Topmost = true; _mainWindow.Show(); // window is set to topmost to make sure it shows on top of PowerToys settings page // we can reset topmost flag now _mainWindow.Topmost = false; } public void AddMonitor(Monitor monitor) { bool inserted = false; var workAreaRect = monitor.Device.WorkAreaRect; for (int i = 0; i < Monitors.Count && !inserted; i++) { var rect = Monitors[i].Device.WorkAreaRect; if (workAreaRect.Left < rect.Left && (workAreaRect.Top <= rect.Top || workAreaRect.Top == 0)) { Monitors.Insert(i, monitor); inserted = true; } else if (workAreaRect.Left == rect.Left && workAreaRect.Top < rect.Top) { Monitors.Insert(i, monitor); inserted = true; } } if (!inserted) { Monitors.Add(monitor); } } } }