diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 6017c2aa9add..3eee435af6da 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -2374,6 +2374,7 @@ Whichdoes whitespaces WIC Wifi +wifstream wih wiki wikipedia diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index b3b41e9320ae..483d1e6aae2c 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -374,8 +374,9 @@ - + + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml index 8baea2aed95a..c372a74c8bb0 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml @@ -2,20 +2,29 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:FancyZonesEditor" + xmlns:ui="http://schemas.modernwpf.com/2019" Startup="OnStartup"> - - - - - + + + + - - - + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs index e1f07e19a039..ba939028d57d 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs @@ -9,6 +9,8 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; using System.Windows; using FancyZonesEditor.Utils; using ManagedCommon; @@ -41,6 +43,9 @@ public partial class App : Application private const string CrashReportDynamicAssemblyTag = "dynamic assembly doesn't have location"; private const string CrashReportLocationNullTag = "location is null or empty"; + private const string ParsingErrorReportTag = "Settings parsing error"; + private const string ParsingErrorDataTag = "Data: "; + public MainWindowSettingsModel MainWindowSettings { get; } public static FancyZonesEditorIO FancyZonesEditorIO { get; private set; } @@ -49,6 +54,8 @@ public partial class App : Application public static int PowerToysPID { get; set; } + private ThemeManager _themeManager; + public static bool DebugMode { get @@ -67,7 +74,7 @@ private void DebugModeCheck() public App() { - DebugModeCheck(); + // DebugModeCheck(); FancyZonesEditorIO = new FancyZonesEditorIO(); Overlay = new Overlay(); MainWindowSettings = new MainWindowSettingsModel(); @@ -82,8 +89,61 @@ private void OnStartup(object sender, StartupEventArgs e) Environment.Exit(0); }); - FancyZonesEditorIO.ParseCommandLineArguments(); - FancyZonesEditorIO.ParseDeviceInfoData(); + _themeManager = new ThemeManager(this); + + if (!FancyZonesEditorIO.ParseParams().Result) + { + FancyZonesEditorIO.ParseCommandLineArguments(); + } + + var parseResult = FancyZonesEditorIO.ParseZoneSettings(); + + // 10ms retry loop with 1 second timeout + if (!parseResult.Result) + { + CancellationTokenSource ts = new CancellationTokenSource(); + Task t = Task.Run(() => + { + while (!parseResult.Result && !ts.IsCancellationRequested) + { + Task.Delay(10).Wait(); + parseResult = FancyZonesEditorIO.ParseZoneSettings(); + } + }); + + try + { + bool result = t.Wait(1000, ts.Token); + ts.Cancel(); + } + catch (OperationCanceledException) + { + ts.Dispose(); + } + } + + // Error message if parsing failed + if (!parseResult.Result) + { + var sb = new StringBuilder(); + sb.AppendLine(); + sb.AppendLine("## " + ParsingErrorReportTag); + sb.AppendLine(); + sb.AppendLine(parseResult.Message); + sb.AppendLine(); + sb.AppendLine(ParsingErrorDataTag); + sb.AppendLine(parseResult.MalformedData); + + string message = parseResult.Message + Environment.NewLine + Environment.NewLine + FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_User_Choice; + if (MessageBox.Show(message, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Title, MessageBoxButton.YesNo) == MessageBoxResult.No) + { + // TODO: log error + ShowExceptionReportMessageBox(sb.ToString()); + Environment.Exit(0); + } + + ShowExceptionReportMessageBox(sb.ToString()); + } MainWindowSettingsModel settings = ((App)Current).MainWindowSettings; settings.UpdateSelectedLayoutModel(); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml index a7eb95c12efb..2cc908d9adb8 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml @@ -1,202 +1,69 @@  + AutomationProperties.Name="{x:Static props:Resources.Canvas_Layout_Editor}" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="clr-namespace:FancyZonesEditor" + xmlns:props="clr-namespace:FancyZonesEditor.Properties" + mc:Ignorable="d" + Title="" + Width="320" + BorderThickness="0" + xmlns:ui="http://schemas.modernwpf.com/2019" + ui:WindowHelper.UseModernWindowStyle="True" + ui:TitleBar.IsIconVisible="True" + SizeToContent="Height" + Background="{DynamicResource PrimaryBackgroundBrush}" + ResizeMode="NoResize" + WindowStartupLocation="CenterOwner" + Closed="OnClosed"> + + + + + + - - - - - + + + + + + + + + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs index eb931ad114b0..6c5f6475a4b7 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs @@ -8,7 +8,6 @@ using System.Windows.Controls; using System.Windows.Input; using FancyZonesEditor.Models; -using FancyZonesEditor.Utils; namespace FancyZonesEditor { @@ -40,27 +39,27 @@ public GridEditor() private void GridEditor_Loaded(object sender, RoutedEventArgs e) { GridLayoutModel model = (GridLayoutModel)DataContext; - if (model != null) + if (model == null) { - _data = new GridData(model); - _dragHandles = new GridDragHandles(AdornerLayer.Children, Resizer_DragDelta, Resizer_DragCompleted); - - int zoneCount = _data.ZoneCount; - for (int i = 0; i <= zoneCount; i++) - { - AddZone(); - } + return; } + _data = new GridData(model); + _dragHandles = new GridDragHandles(AdornerLayer.Children, Resizer_DragDelta, Resizer_DragCompleted); + _dragHandles.InitDragHandles(model); + Model = model; - if (Model == null) + Model.PropertyChanged += OnGridDimensionsChanged; + + int zoneCount = _data.ZoneCount; + for (int i = 0; i < zoneCount; i++) { - Model = new GridLayoutModel(); - DataContext = Model; + AddZone(); } - Model.PropertyChanged += OnGridDimensionsChanged; - _dragHandles.InitDragHandles(model); + Rect workingArea = App.Overlay.WorkArea; + Size actualSize = new Size(workingArea.Width, workingArea.Height); + ArrangeGridRects(actualSize); } private void GridEditor_Unloaded(object sender, RoutedEventArgs e) @@ -330,15 +329,17 @@ private int AddZone() zone.Visibility = Visibility.Visible; return freeIndex; } + + zone = new GridZone(Model.ShowSpacing ? Model.Spacing : 0); + zone.Split += OnSplit; + zone.MergeDrag += OnMergeDrag; + zone.MergeComplete += OnMergeComplete; + zone.FullSplit += OnFullSplit; + Preview.Children.Add(zone); + return Preview.Children.Count - 1; } - zone = new GridZone(); - zone.Split += OnSplit; - zone.MergeDrag += OnMergeDrag; - zone.MergeComplete += OnMergeComplete; - zone.FullSplit += OnFullSplit; - Preview.Children.Add(zone); - return Preview.Children.Count - 1; + return 0; } private void OnGridDimensionsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) @@ -372,7 +373,7 @@ private void ArrangeGridRects(Size arrangeSize) Preview.Height = workArea.Height; GridLayoutModel model = Model; - if (model == null) + if (model == null || _data == null) { return; } @@ -383,9 +384,7 @@ private void ArrangeGridRects(Size arrangeSize) return; } - MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings; - - int spacing = settings.ShowSpacing ? settings.Spacing : 0; + int spacing = model.ShowSpacing ? model.Spacing : 0; _data.RecalculateZones(spacing, arrangeSize); _data.ArrangeZones(Preview.Children, spacing); @@ -411,10 +410,10 @@ private void Resizer_DragDelta(object sender, System.Windows.Controls.Primitives if (_dragHandles.HasSnappedNonAdjacentResizers(resizer)) { double spacing = 0; - MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings; - if (settings.ShowSpacing) + GridLayoutModel model = Model; + if (model.ShowSpacing) { - spacing = settings.Spacing; + spacing = model.Spacing; } _data.SplitOnDrag(resizer, delta, spacing); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml index 5a35cf349b8a..eba84aadcc84 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml +++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml @@ -1,196 +1,62 @@  + AutomationProperties.Name="{x:Static props:Resources.Grid_Layout_Editor}" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="clr-namespace:FancyZonesEditor" + xmlns:props="clr-namespace:FancyZonesEditor.Properties" + mc:Ignorable="d" + Title="" + Width="320" + BorderThickness="0" + xmlns:ui="http://schemas.modernwpf.com/2019" + ui:WindowHelper.UseModernWindowStyle="True" + ui:TitleBar.IsIconVisible="True" + SizeToContent="Height" + Background="{DynamicResource PrimaryBackgroundBrush}" + ResizeMode="NoResize" + WindowStartupLocation="CenterOwner" + Closed="OnClosed"> + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs index 82e363dd324b..2326e21d0c63 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs @@ -3,40 +3,32 @@ // 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 System.Windows.Input; using FancyZonesEditor.Models; -using MahApps.Metro.Controls; +using FancyZonesEditor.Utils; +using ModernWpf.Controls; namespace FancyZonesEditor { /// /// Interaction logic for MainWindow.xaml /// - public partial class MainWindow : MetroWindow + public partial class MainWindow : Window { - // TODO: share the constants b/w C# Editor and FancyZoneLib - public const int MaxZones = 40; - private const int DefaultWrapPanelItemSize = 262; - private const int SmallWrapPanelItemSize = 180; + private const int DefaultWrapPanelItemSize = 164; + private const int SmallWrapPanelItemSize = 164; private const int MinimalForDefaultWrapPanelsHeight = 900; private readonly MainWindowSettingsModel _settings = ((App)Application.Current).MainWindowSettings; + private LayoutModel _backup = null; - // Localizable string - private static readonly string _defaultNamePrefix = "Custom Layout "; + private ContentDialog _openedDialog = null; public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize; - public double SettingsTextMaxWidth - { - get - { - return (Width / 2) - 60; - } - } - public MainWindow(bool spanZonesAcrossMonitors, Rect workArea) { InitializeComponent(); @@ -51,59 +43,84 @@ public MainWindow(bool spanZonesAcrossMonitors, Rect workArea) if (workArea.Height < MinimalForDefaultWrapPanelsHeight || App.Overlay.MultiMonitorMode) { - SizeToContent = SizeToContent.WidthAndHeight; WrapPanelItemSize = SmallWrapPanelItemSize; } + + SizeToContent = SizeToContent.WidthAndHeight; } public void Update() { DataContext = _settings; - SetSelectedItem(); } private void MainWindow_KeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Escape) { - OnClosing(sender, null); + if (_openedDialog != null) + { + _openedDialog.Hide(); + } + else + { + OnClosing(sender, null); + } } } private void DecrementZones_Click(object sender, RoutedEventArgs e) { - if (_settings.ZoneCount > 1) + var mainEditor = App.Overlay; + if (!(mainEditor.CurrentDataContext is LayoutModel model)) + { + return; + } + + if (model.TemplateZoneCount > 1) { - _settings.ZoneCount--; + model.TemplateZoneCount--; } } private void IncrementZones_Click(object sender, RoutedEventArgs e) { - if (_settings.ZoneCount < MaxZones) + var mainEditor = App.Overlay; + if (!(mainEditor.CurrentDataContext is LayoutModel model)) + { + return; + } + + if (model.TemplateZoneCount < LayoutSettings.MaxZones) { - _settings.ZoneCount++; + model.TemplateZoneCount++; } } - private void NewCustomLayoutButton_Click(object sender, RoutedEventArgs e) + private void LayoutItem_MouseEnter(object sender, MouseEventArgs e) { - WindowLayout window = new WindowLayout(); - window.Show(); - Hide(); + // Select(((Grid)sender).DataContext as LayoutModel); } private void LayoutItem_Click(object sender, MouseButtonEventArgs e) { - Select(((Border)sender).DataContext as LayoutModel); + LayoutModel selectedLayoutModel = ((Grid)sender).DataContext as LayoutModel; + Select(selectedLayoutModel); + Apply(); } private void LayoutItem_Focused(object sender, RoutedEventArgs e) { + // Ignore focus on Edit button click + if (e.Source is Button) + { + return; + } + Select(((Border)sender).DataContext as LayoutModel); } - private void LayoutItem_Apply(object sender, KeyEventArgs e) + private void LayoutItem_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Return || e.Key == Key.Space) { @@ -115,17 +132,39 @@ private void LayoutItem_Apply(object sender, KeyEventArgs e) private void Select(LayoutModel newSelection) { - if (App.Overlay.CurrentDataContext is LayoutModel currentSelection) + _settings.SetSelectedModel(newSelection); + App.Overlay.CurrentDataContext = newSelection; + } + + private async void NewLayoutButton_Click(object sender, RoutedEventArgs e) + { + string defaultNamePrefix = FancyZonesEditor.Properties.Resources.Default_Custom_Layout_Name; + int maxCustomIndex = 0; + foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels) { - currentSelection.IsSelected = false; + string name = customModel.Name; + if (name.StartsWith(defaultNamePrefix)) + { + if (int.TryParse(name.Substring(defaultNamePrefix.Length), out int i)) + { + if (maxCustomIndex < i) + { + maxCustomIndex = i; + } + } + } } - newSelection.IsSelected = true; - App.Overlay.CurrentDataContext = newSelection; + LayoutNameText.Text = defaultNamePrefix + " " + (++maxCustomIndex); + GridLayoutRadioButton.IsChecked = true; + GridLayoutRadioButton.Focus(); + await NewLayoutDialog.ShowAsync(); } - private void EditLayout_Click(object sender, RoutedEventArgs e) + private void DuplicateLayout_Click(object sender, RoutedEventArgs e) { + EditLayoutDialog.Hide(); + var mainEditor = App.Overlay; if (!(mainEditor.CurrentDataContext is LayoutModel model)) { @@ -133,26 +172,32 @@ private void EditLayout_Click(object sender, RoutedEventArgs e) } model.IsSelected = false; - Hide(); - bool isPredefinedLayout = MainWindowSettingsModel.IsPredefinedLayout(model); + // make a copy + model = model.Clone(); + mainEditor.CurrentDataContext = model; - if (!MainWindowSettingsModel.CustomModels.Contains(model) || isPredefinedLayout) + string name = model.Name; + var index = name.LastIndexOf('('); + if (index != -1) { - if (isPredefinedLayout) - { - // make a copy - model = model.Clone(); - mainEditor.CurrentDataContext = model; - } + name = name.Remove(index); + name = name.TrimEnd(); + } - int maxCustomIndex = 0; - foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels) + int maxCustomIndex = 0; + foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels) + { + string customModelName = customModel.Name; + if (customModelName.StartsWith(name)) { - string name = customModel.Name; - if (name.StartsWith(_defaultNamePrefix)) + int openBraceIndex = customModelName.LastIndexOf('('); + int closeBraceIndex = customModelName.LastIndexOf(')'); + if (openBraceIndex != -1 && closeBraceIndex != -1) { - if (int.TryParse(name.Substring(_defaultNamePrefix.Length), out int i)) + string indexSubstring = customModelName.Substring(openBraceIndex + 1, closeBraceIndex - openBraceIndex - 1); + + if (int.TryParse(indexSubstring, out int i)) { if (maxCustomIndex < i) { @@ -161,67 +206,70 @@ private void EditLayout_Click(object sender, RoutedEventArgs e) } } } - - model.Name = _defaultNamePrefix + (++maxCustomIndex); } - mainEditor.OpenEditor(model); - } + model.Name = name + " (" + (++maxCustomIndex) + ')'; - private void Apply_Click(object sender, RoutedEventArgs e) - { - Apply(); + model.Persist(); + + App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model); + App.FancyZonesEditorIO.SerializeZoneSettings(); } private void Apply() { - ((App)Application.Current).MainWindowSettings.ResetAppliedModel(); - var mainEditor = App.Overlay; if (mainEditor.CurrentDataContext is LayoutModel model) { - model.Apply(); - } - - if (!mainEditor.MultiMonitorMode) - { - Close(); + _settings.SetAppliedModel(model); + App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model); + App.FancyZonesEditorIO.SerializeZoneSettings(); } } private void OnClosing(object sender, EventArgs e) { - LayoutModel.SerializeDeletedCustomZoneSets(); + App.FancyZonesEditorIO.SerializeZoneSettings(); App.Overlay.CloseLayoutWindow(); App.Current.Shutdown(); } - private void OnInitialized(object sender, EventArgs e) + private void DeleteLayout_Click(object sender, RoutedEventArgs e) { - SetSelectedItem(); + EditLayoutDialog.Hide(); + DeleteLayout((FrameworkElement)sender); } - private void SetSelectedItem() + private async void EditLayout_Click(object sender, RoutedEventArgs e) { - foreach (LayoutModel model in MainWindowSettingsModel.CustomModels) + var dataContext = ((FrameworkElement)sender).DataContext; + Select((LayoutModel)dataContext); + + if (_settings.SelectedModel is GridLayoutModel grid) { - if (model.IsSelected) - { - TemplateTab.SelectedItem = model; - return; - } + _backup = new GridLayoutModel(grid); } + else if (_settings.SelectedModel is CanvasLayoutModel canvas) + { + _backup = new CanvasLayoutModel(canvas); + } + + await EditLayoutDialog.ShowAsync(); } - private void OnDelete(object sender, RoutedEventArgs e) + private void EditZones_Click(object sender, RoutedEventArgs e) { - LayoutModel model = ((FrameworkElement)sender).DataContext as LayoutModel; - if (model.IsSelected) + EditLayoutDialog.Hide(); + var mainEditor = App.Overlay; + if (!(mainEditor.CurrentDataContext is LayoutModel model)) { - SetSelectedItem(); + return; } - model.Delete(); + _settings.SetSelectedModel(model); + + Hide(); + mainEditor.OpenEditor(model); } private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) @@ -239,32 +287,129 @@ private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e e.Handled = true; } - private void CloseButton_Click(object sender, RoutedEventArgs e) + private void NewLayoutDialog_PrimaryButtonClick(ModernWpf.Controls.ContentDialog sender, ModernWpf.Controls.ContentDialogButtonClickEventArgs args) + { + LayoutModel selectedLayoutModel; + + if (GridLayoutRadioButton.IsChecked == true) + { + GridLayoutModel gridModel = new GridLayoutModel(LayoutNameText.Text, LayoutType.Custom) + { + Rows = 1, + RowPercents = new List(1) { GridLayoutModel.GridMultiplier }, + }; + selectedLayoutModel = gridModel; + } + else + { + selectedLayoutModel = new CanvasLayoutModel(LayoutNameText.Text, LayoutType.Custom) + { + TemplateZoneCount = 0, + }; + } + + selectedLayoutModel.InitTemplateZones(); + + App.Overlay.CurrentDataContext = selectedLayoutModel; + var mainEditor = App.Overlay; + Hide(); + mainEditor.OpenEditor(selectedLayoutModel); + } + + // This is required to fix a WPF rendering bug when using custom chrome + private void OnContentRendered(object sender, EventArgs e) + { + InvalidateVisual(); + } + + private void MonitorItem_KeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Return || e.Key == Key.Space) + { + monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext); + } + } + + private void MonitorItem_MouseDown(object sender, MouseButtonEventArgs e) { - this.Close(); + monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext); } - private void Reset_Click(object sender, RoutedEventArgs e) + // EditLayout: Cancel changes + private void EditLayoutDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { - var overlay = App.Overlay; - MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings; + // restore model properties from settings + _settings.RestoreSelectedModel(_backup); + _backup = null; + + Select(_settings.AppliedModel); + } - if (overlay.CurrentDataContext is LayoutModel model) + // EditLayout: Save changes + private void EditLayoutDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + var mainEditor = App.Overlay; + if (!(mainEditor.CurrentDataContext is LayoutModel model)) { - model.IsSelected = false; - model.IsApplied = false; + return; } - overlay.CurrentLayoutSettings.ZonesetUuid = settings.BlankModel.Uuid; - overlay.CurrentLayoutSettings.Type = LayoutType.Blank; - overlay.CurrentDataContext = settings.BlankModel; + _backup = null; - App.FancyZonesEditorIO.SerializeAppliedLayouts(); + // update current settings + if (model == _settings.AppliedModel) + { + App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model); + } - if (!overlay.MultiMonitorMode) + App.FancyZonesEditorIO.SerializeZoneSettings(); + + // reset selected model + Select(_settings.AppliedModel); + } + + private async void DeleteLayout(FrameworkElement element) + { + var dialog = new ModernWpf.Controls.ContentDialog() + { + Title = FancyZonesEditor.Properties.Resources.Are_You_Sure, + Content = FancyZonesEditor.Properties.Resources.Are_You_Sure_Description, + PrimaryButtonText = FancyZonesEditor.Properties.Resources.Delete, + SecondaryButtonText = FancyZonesEditor.Properties.Resources.Cancel, + }; + + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) { - Close(); + LayoutModel model = element.DataContext as LayoutModel; + + if (model == _settings.AppliedModel) + { + _settings.SetAppliedModel(_settings.BlankModel); + Select(_settings.BlankModel); + } + + foreach (var monitor in App.Overlay.Monitors) + { + if (monitor.Settings.ZonesetUuid == model.Uuid) + { + App.Overlay.SetLayoutSettings(monitor, _settings.BlankModel); + } + } + + App.FancyZonesEditorIO.SerializeZoneSettings(); + model.Delete(); } } + + private void Dialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + _openedDialog = sender; + } + + private void Dialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs args) + { + _openedDialog = null; + } } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs index 03ac60aaaaa3..49cc928ac020 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs @@ -2,9 +2,7 @@ // 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.Text.Json; using System.Windows; namespace FancyZonesEditor.Models @@ -14,7 +12,7 @@ namespace FancyZonesEditor.Models public class CanvasLayoutModel : LayoutModel { // Non-localizable strings - private const string ModelTypeID = "canvas"; + public const string ModelTypeID = "canvas"; public Rect CanvasRect { get; private set; } @@ -35,6 +33,17 @@ public CanvasLayoutModel(string name) { } + public CanvasLayoutModel(CanvasLayoutModel other) + : base(other) + { + CanvasRect = new Rect(other.CanvasRect.X, other.CanvasRect.Y, other.CanvasRect.Width, other.CanvasRect.Height); + + foreach (Int32Rect zone in other.Zones) + { + Zones.Add(zone); + } + } + // Zones - the list of all zones in this layout, described as independent rectangles public IList Zones { get; private set; } = new List(); @@ -54,6 +63,28 @@ public void AddZone(Int32Rect zone) UpdateLayout(); } + // InitTemplateZones + // Creates zones based on template zones count + public override void InitTemplateZones() + { + Zones.Clear(); + + var workingArea = App.Overlay.WorkArea; + int topLeftCoordinate = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(100); // TODO: replace magic numbers + int width = (int)(workingArea.Width * 0.4); + int height = (int)(workingArea.Height * 0.4); + Int32Rect focusZoneRect = new Int32Rect(topLeftCoordinate, topLeftCoordinate, width, height); + int focusRectXIncrement = (TemplateZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50); // TODO: replace magic numbers + int focusRectYIncrement = (TemplateZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50); // TODO: replace magic numbers + + for (int i = 0; i < TemplateZoneCount; i++) + { + Zones.Add(focusZoneRect); + focusZoneRect.X += focusRectXIncrement; + focusZoneRect.Y += focusRectYIncrement; + } + } + private void UpdateLayout() { FirePropertyChanged(); @@ -71,6 +102,7 @@ public override LayoutModel Clone() layout.Zones.Add(zone); } + layout.SensitivityRadius = SensitivityRadius; return layout; } @@ -81,37 +113,8 @@ public void RestoreTo(CanvasLayoutModel other) { other.Zones.Add(zone); } - } - - private struct Zone - { - public int X { get; set; } - public int Y { get; set; } - - public int Width { get; set; } - - public int Height { get; set; } - } - - private struct CanvasLayoutInfo - { - public int RefWidth { get; set; } - - public int RefHeight { get; set; } - - public Zone[] Zones { get; set; } - } - - private struct CanvasLayoutJson - { - public string Uuid { get; set; } - - public string Name { get; set; } - - public string Type { get; set; } - - public CanvasLayoutInfo Info { get; set; } + other.SensitivityRadius = SensitivityRadius; } // PersistData @@ -119,48 +122,6 @@ private struct CanvasLayoutJson protected override void PersistData() { AddCustomLayout(this); - - var canvasRect = CanvasRect; - if (canvasRect.Width == 0 || canvasRect.Height == 0) - { - canvasRect = App.Overlay.WorkArea; - } - - CanvasLayoutInfo layoutInfo = new CanvasLayoutInfo - { - RefWidth = (int)canvasRect.Width, - RefHeight = (int)canvasRect.Height, - Zones = new Zone[Zones.Count], - }; - - for (int i = 0; i < Zones.Count; ++i) - { - Zone zone = new Zone - { - X = Zones[i].X, - Y = Zones[i].Y, - Width = Zones[i].Width, - Height = Zones[i].Height, - }; - - layoutInfo.Zones[i] = zone; - } - - CanvasLayoutJson jsonObj = new CanvasLayoutJson - { - Uuid = Uuid, - Name = Name, - Type = ModelTypeID, - Info = layoutInfo, - }; - JsonSerializerOptions options = new JsonSerializerOptions - { - PropertyNamingPolicy = new DashCaseNamingPolicy(), - }; - - string jsonString = JsonSerializer.Serialize(jsonObj, options); - AddCustomLayoutJson(JsonSerializer.Deserialize(jsonString)); - SerializeCreatedCustomZonesets(); } } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs index 1dbc391e13d8..cfc2792871a9 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs @@ -1,11 +1,8 @@ -// Copyright (c) Microsoft Corporation +// 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.Text.Json; -using System.Windows; namespace FancyZonesEditor.Models { @@ -14,7 +11,25 @@ namespace FancyZonesEditor.Models public class GridLayoutModel : LayoutModel { // Non-localizable strings - private const string ModelTypeID = "grid"; + public const string ModelTypeID = "grid"; + + public const int GridMultiplier = 10000; + + // hard coded data for all the "Priority Grid" configurations that are unique to "Grid" + private static readonly byte[][] _priorityData = new byte[][] + { + new byte[] { 0, 0, 0, 0, 0, 1, 1, 39, 16, 39, 16, 0 }, + new byte[] { 0, 0, 0, 0, 0, 1, 2, 39, 16, 26, 11, 13, 5, 0, 1 }, + new byte[] { 0, 0, 0, 0, 0, 1, 3, 39, 16, 9, 196, 19, 136, 9, 196, 0, 1, 2 }, + new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3 }, + new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4 }, + new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3, 4, 1, 5 }, + new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4, 5, 1, 6 }, + new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 2, 7 }, + new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8 }, + new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 1, 8, 9 }, + new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9, 10 }, + }; // Rows - number of rows in the Grid public int Rows @@ -29,7 +44,6 @@ public int Rows if (_rows != value) { _rows = value; - FirePropertyChanged(); } } } @@ -49,7 +63,6 @@ public int Columns if (_cols != value) { _cols = value; - FirePropertyChanged(); } } } @@ -62,10 +75,50 @@ public int Columns public int[,] CellChildMap { get; set; } // RowPercents - represents the %age height of each row in the grid - public List RowPercents { get; set; } + public List RowPercents { get; set; } = new List(); // ColumnPercents - represents the %age width of each column in the grid - public List ColumnPercents { get; set; } + public List ColumnPercents { get; set; } = new List(); + + // ShowSpacing - flag if free space between cells should be presented + public bool ShowSpacing + { + get + { + return _showSpacing; + } + + set + { + if (value != _showSpacing) + { + _showSpacing = value; + FirePropertyChanged(nameof(ShowSpacing)); + } + } + } + + private bool _showSpacing = LayoutSettings.DefaultShowSpacing; + + // Spacing - free space between cells + public int Spacing + { + get + { + return _spacing; + } + + set + { + if (value != _spacing) + { + _spacing = value; + FirePropertyChanged(nameof(Spacing)); + } + } + } + + private int _spacing = LayoutSettings.DefaultSpacing; // FreeZones (not persisted) - used to keep track of child indices that are no longer in use in the CellChildMap, // making them candidates for re-use when it's needed to add another child @@ -97,13 +150,46 @@ public GridLayoutModel(string uuid, string name, LayoutType type, int rows, int CellChildMap = cellChildMap; } + public GridLayoutModel(GridLayoutModel other) + : base(other) + { + _rows = other._rows; + _cols = other._cols; + _showSpacing = other._showSpacing; + _spacing = other._spacing; + + CellChildMap = new int[_rows, _cols]; + for (int row = 0; row < _rows; row++) + { + for (int col = 0; col < _cols; col++) + { + CellChildMap[row, col] = other.CellChildMap[row, col]; + } + } + + for (int row = 0; row < _rows; row++) + { + RowPercents.Add(other.RowPercents[row]); + } + + for (int col = 0; col < _cols; col++) + { + ColumnPercents.Add(other.ColumnPercents[col]); + } + } + + public void UpdatePreview() + { + FirePropertyChanged(); + } + public void Reload(byte[] data) { // Skip version (2 bytes), id (2 bytes), and type (1 bytes) int i = 5; - Rows = data[i++]; - Columns = data[i++]; + _rows = data[i++]; + _cols = data[i++]; RowPercents = new List(Rows); for (int row = 0; row < Rows; row++) @@ -125,6 +211,8 @@ public void Reload(byte[] data) CellChildMap[row, col] = data[i++]; } } + + FirePropertyChanged(); } // Clone @@ -171,71 +259,142 @@ public void RestoreTo(GridLayoutModel layout) } layout.ColumnPercents = colPercents; + + layout.ShowSpacing = ShowSpacing; + layout.Spacing = Spacing; + layout.SensitivityRadius = SensitivityRadius; + } + + // InitTemplateZones + // Creates zones based on template zones count + public override void InitTemplateZones() + { + switch (Type) + { + case LayoutType.Rows: + InitRows(); + break; + case LayoutType.Columns: + InitColumns(); + break; + case LayoutType.Grid: + InitGrid(); + break; + case LayoutType.PriorityGrid: + InitPriorityGrid(); + break; + case LayoutType.Custom: + InitColumns(); // Custom is initialized with columns + break; + } + + FirePropertyChanged(); } - private struct GridLayoutInfo + // PersistData + // Implements the LayoutModel.PersistData abstract method + protected override void PersistData() { - public int Rows { get; set; } + AddCustomLayout(this); + } - public int Columns { get; set; } + private void InitRows() + { + CellChildMap = new int[TemplateZoneCount, 1]; + RowPercents = new List(TemplateZoneCount); - public List RowsPercentage { get; set; } + for (int i = 0; i < TemplateZoneCount; i++) + { + CellChildMap[i, 0] = i; - public List ColumnsPercentage { get; set; } + // Note: This is NOT equal to _multiplier / ZoneCount and is done like this to make + // the sum of all RowPercents exactly (_multiplier). + RowPercents.Add(((GridMultiplier * (i + 1)) / TemplateZoneCount) - ((GridMultiplier * i) / TemplateZoneCount)); + } - public int[][] CellChildMap { get; set; } + _rows = TemplateZoneCount; } - private struct GridLayoutJson + private void InitColumns() { - public string Uuid { get; set; } + CellChildMap = new int[1, TemplateZoneCount]; + ColumnPercents = new List(TemplateZoneCount); - public string Name { get; set; } + for (int i = 0; i < TemplateZoneCount; i++) + { + CellChildMap[0, i] = i; - public string Type { get; set; } + // Note: This is NOT equal to _multiplier / ZoneCount and is done like this to make + // the sum of all RowPercents exactly (_multiplier). + ColumnPercents.Add(((GridMultiplier * (i + 1)) / TemplateZoneCount) - ((GridMultiplier * i) / TemplateZoneCount)); + } - public GridLayoutInfo Info { get; set; } + _cols = TemplateZoneCount; } - // PersistData - // Implements the LayoutModel.PersistData abstract method - protected override void PersistData() + private void InitGrid() { - AddCustomLayout(this); + int rows = 1; + while (TemplateZoneCount / rows >= rows) + { + rows++; + } - GridLayoutInfo layoutInfo = new GridLayoutInfo + rows--; + int cols = TemplateZoneCount / rows; + if (TemplateZoneCount % rows == 0) { - Rows = Rows, - Columns = Columns, - RowsPercentage = RowPercents, - ColumnsPercentage = ColumnPercents, - CellChildMap = new int[Rows][], - }; + // even grid + } + else + { + cols++; + } - for (int row = 0; row < Rows; row++) + RowPercents = new List(rows); + ColumnPercents = new List(cols); + CellChildMap = new int[rows, cols]; + + // Note: The following are NOT equal to _multiplier divided by rows or columns and is + // done like this to make the sum of all RowPercents exactly (_multiplier). + for (int row = 0; row < rows; row++) { - layoutInfo.CellChildMap[row] = new int[Columns]; - for (int col = 0; col < Columns; col++) + RowPercents.Add(((GridMultiplier * (row + 1)) / rows) - ((GridMultiplier * row) / rows)); + } + + for (int col = 0; col < cols; col++) + { + ColumnPercents.Add(((GridMultiplier * (col + 1)) / cols) - ((GridMultiplier * col) / cols)); + } + + int index = 0; + for (int row = 0; row < rows; row++) + { + for (int col = 0; col < cols; col++) { - layoutInfo.CellChildMap[row][col] = CellChildMap[row, col]; + CellChildMap[row, col] = index++; + if (index == TemplateZoneCount) + { + index--; + } } } - GridLayoutJson jsonObj = new GridLayoutJson + _rows = rows; + _cols = cols; + } + + private void InitPriorityGrid() + { + if (TemplateZoneCount <= _priorityData.Length) { - Uuid = Uuid, - Name = Name, - Type = ModelTypeID, - Info = layoutInfo, - }; - JsonSerializerOptions options = new JsonSerializerOptions + Reload(_priorityData[TemplateZoneCount - 1]); + } + else { - PropertyNamingPolicy = new DashCaseNamingPolicy(), - }; - - string jsonString = JsonSerializer.Serialize(jsonObj, options); - AddCustomLayoutJson(JsonSerializer.Deserialize(jsonString)); - SerializeCreatedCustomZonesets(); + // same as grid; + InitGrid(); + } } } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs index 61d3ac76bdae..ea66005b22ac 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs @@ -3,11 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json; namespace FancyZonesEditor.Models { @@ -42,6 +39,17 @@ protected LayoutModel(string name, LayoutType type) Type = type; } + protected LayoutModel(LayoutModel other) + { + _guid = other._guid; + _name = other._name; + Type = other.Type; + _isSelected = other._isSelected; + _isApplied = other._isApplied; + _sensitivityRadius = other._sensitivityRadius; + _zoneCount = other._zoneCount; + } + // Name - the display name for this layout model - is also used as the key in the registry public string Name { @@ -55,7 +63,7 @@ public string Name if (_name != value) { _name = value; - FirePropertyChanged(); + FirePropertyChanged(nameof(Name)); } } } @@ -82,6 +90,14 @@ public string Uuid } } + public bool IsCustom + { + get + { + return Type == LayoutType.Custom; + } + } + // IsSelected (not-persisted) - tracks whether or not this LayoutModel is selected in the picker // TODO: once we switch to a picker per monitor, we need to move this state to the view public bool IsSelected @@ -96,13 +112,14 @@ public bool IsSelected if (_isSelected != value) { _isSelected = value; - FirePropertyChanged(); + FirePropertyChanged(nameof(IsSelected)); } } } private bool _isSelected; + // IsApplied (not-persisted) - tracks whether or not this LayoutModel is applied in the picker public bool IsApplied { get @@ -115,13 +132,53 @@ public bool IsApplied if (_isApplied != value) { _isApplied = value; - FirePropertyChanged(); + FirePropertyChanged(nameof(IsApplied)); } } } private bool _isApplied; + public int SensitivityRadius + { + get + { + return _sensitivityRadius; + } + + set + { + if (value != _sensitivityRadius) + { + _sensitivityRadius = value; + FirePropertyChanged(nameof(SensitivityRadius)); + } + } + } + + private int _sensitivityRadius = LayoutSettings.DefaultSensitivityRadius; + + // TemplateZoneCount - number of zones selected in the picker window for template layouts + public int TemplateZoneCount + { + get + { + return _zoneCount; + } + + set + { + if (value != _zoneCount) + { + _zoneCount = value; + InitTemplateZones(); + FirePropertyChanged(nameof(TemplateZoneCount)); + } + } + } + + private int _zoneCount = LayoutSettings.DefaultZoneCount; + // implementation of INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; @@ -134,11 +191,11 @@ protected virtual void FirePropertyChanged([CallerMemberName] string propertyNam // Removes this Layout from the registry and the loaded CustomModels list public void Delete() { - int i = _customModels.IndexOf(this); + var customModels = MainWindowSettingsModel.CustomModels; + int i = customModels.IndexOf(this); if (i != -1) { - _customModels.RemoveAt(i); - _deletedCustomModels.Add(Guid.ToString().ToUpper()); + customModels.RemoveAt(i); } } @@ -146,48 +203,25 @@ public void Delete() public void AddCustomLayout(LayoutModel model) { bool updated = false; - for (int i = 0; i < _customModels.Count && !updated; i++) + var customModels = MainWindowSettingsModel.CustomModels; + for (int i = 0; i < customModels.Count && !updated; i++) { - if (_customModels[i].Uuid == model.Uuid) + if (customModels[i].Uuid == model.Uuid) { - _customModels[i] = model; + customModels[i] = model; updated = true; } } if (!updated) { - _customModels.Add(model); + customModels.Add(model); } } - // Add custom layouts json data that would be serialized to a temp file - public void AddCustomLayoutJson(JsonElement json) - { - _createdCustomLayouts.Add(json); - } - - public static void SerializeDeletedCustomZoneSets() - { - App.FancyZonesEditorIO.SerializeDeletedCustomZoneSets(_deletedCustomModels); - } - - public static void SerializeCreatedCustomZonesets() - { - App.FancyZonesEditorIO.SerializeCreatedCustomZonesets(_createdCustomLayouts); - } - - // Loads all the custom Layouts from tmp file passed by FancyZonesLib - public static ObservableCollection LoadCustomModels() - { - _customModels = new ObservableCollection(); - App.FancyZonesEditorIO.ParseLayouts(ref _customModels, ref _deletedCustomModels); - return _customModels; - } - - private static ObservableCollection _customModels; - private static List _deletedCustomModels = new List(); - private static List _createdCustomLayouts = new List(); + // InitTemplateZones + // Creates zones based on template zones count + public abstract void InitTemplateZones(); // Callbacks that the base LayoutModel makes to derived types protected abstract void PersistData(); @@ -197,21 +231,6 @@ public static ObservableCollection LoadCustomModels() public void Persist() { PersistData(); - Apply(); - } - - public void Apply() - { - MainWindowSettingsModel settings = ((App)App.Current).MainWindowSettings; - settings.ResetAppliedModel(); - IsApplied = true; - - // update settings - App.Overlay.CurrentLayoutSettings.ZonesetUuid = Uuid; - App.Overlay.CurrentLayoutSettings.Type = Type; - - // update temp file - App.FancyZonesEditorIO.SerializeAppliedLayouts(); } } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutSettings.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutSettings.cs index 32c5b63a7e44..3b8487a6353a 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutSettings.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutSettings.cs @@ -9,13 +9,16 @@ namespace FancyZonesEditor { public class LayoutSettings { - public static bool DefaultShowSpacing => true; + // TODO: share the constants b/w C# Editor and FancyZoneLib + public const bool DefaultShowSpacing = true; - public static int DefaultSpacing => 16; + public const int DefaultSpacing = 16; - public static int DefaultZoneCount => 3; + public const int DefaultZoneCount = 3; - public static int DefaultSensitivityRadius => 20; + public const int DefaultSensitivityRadius = 20; + + public const int MaxZones = 40; public string ZonesetUuid { get; set; } = string.Empty; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs index c48b371d56dd..a23e1d8fd8c3 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs @@ -2,12 +2,10 @@ // 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.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Windows; using FancyZonesEditor.Models; namespace FancyZonesEditor @@ -25,43 +23,17 @@ private enum DeviceIdParts VirtualDesktopId, } - private static CanvasLayoutModel _blankCustomModel; + private readonly CanvasLayoutModel _blankModel; private readonly CanvasLayoutModel _focusModel; private readonly GridLayoutModel _rowsModel; private readonly GridLayoutModel _columnsModel; private readonly GridLayoutModel _gridModel; private readonly GridLayoutModel _priorityGridModel; - public const ushort _focusModelId = 0xFFFF; - public const ushort _rowsModelId = 0xFFFE; - public const ushort _columnsModelId = 0xFFFD; - public const ushort _gridModelId = 0xFFFC; - public const ushort _priorityGridModelId = 0xFFFB; - public const ushort _blankCustomModelId = 0xFFFA; - public const ushort _lastDefinedId = _blankCustomModelId; - // Non-localizable strings public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones"; public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath; - // hard coded data for all the "Priority Grid" configurations that are unique to "Grid" - private static readonly byte[][] _priorityData = new byte[][] - { - new byte[] { 0, 0, 0, 0, 0, 1, 1, 39, 16, 39, 16, 0 }, - new byte[] { 0, 0, 0, 0, 0, 1, 2, 39, 16, 26, 11, 13, 5, 0, 1 }, - new byte[] { 0, 0, 0, 0, 0, 1, 3, 39, 16, 9, 196, 19, 136, 9, 196, 0, 1, 2 }, - new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3 }, - new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4 }, - new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3, 4, 1, 5 }, - new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4, 5, 1, 6 }, - new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 2, 7 }, - new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8 }, - new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 1, 8, 9 }, - new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9, 10 }, - }; - - private const int _multiplier = 10000; - public bool IsCustomLayoutActive { get @@ -80,112 +52,41 @@ public bool IsCustomLayoutActive public MainWindowSettingsModel() { - // Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid - DefaultModels = new List(5); + // Initialize default layout models: Blank, Focus, Columns, Rows, Grid, and PriorityGrid + _blankModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Blank, LayoutType.Blank) + { + TemplateZoneCount = 0, + SensitivityRadius = 0, + }; + DefaultModels.Add(_blankModel); + _focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus); + _focusModel.InitTemplateZones(); DefaultModels.Add(_focusModel); _columnsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Columns, LayoutType.Columns) { Rows = 1, - RowPercents = new List(1) { _multiplier }, + RowPercents = new List(1) { GridLayoutModel.GridMultiplier }, }; + _columnsModel.InitTemplateZones(); DefaultModels.Add(_columnsModel); _rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows) { Columns = 1, - ColumnPercents = new List(1) { _multiplier }, + ColumnPercents = new List(1) { GridLayoutModel.GridMultiplier }, }; + _rowsModel.InitTemplateZones(); DefaultModels.Add(_rowsModel); _gridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Grid, LayoutType.Grid); + _gridModel.InitTemplateZones(); DefaultModels.Add(_gridModel); _priorityGridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Priority_Grid, LayoutType.PriorityGrid); + _priorityGridModel.InitTemplateZones(); DefaultModels.Add(_priorityGridModel); - - _blankCustomModel = new CanvasLayoutModel(Properties.Resources.Custom_Layout_Create_New, LayoutType.Blank); - - UpdateTemplateLayoutModels(); - } - - // ZoneCount - number of zones selected in the picker window - public int ZoneCount - { - get - { - return App.Overlay.CurrentLayoutSettings.ZoneCount; - } - - set - { - if (App.Overlay.CurrentLayoutSettings.ZoneCount != value) - { - App.Overlay.CurrentLayoutSettings.ZoneCount = value; - UpdateTemplateLayoutModels(); - FirePropertyChanged(nameof(ZoneCount)); - } - } - } - - // Spacing - how much space in between zones of the grid do you want - public int Spacing - { - get - { - return App.Overlay.CurrentLayoutSettings.Spacing; - } - - set - { - value = Math.Max(0, value); - if (App.Overlay.CurrentLayoutSettings.Spacing != value) - { - App.Overlay.CurrentLayoutSettings.Spacing = value; - UpdateTemplateLayoutModels(); - FirePropertyChanged(nameof(Spacing)); - } - } - } - - // ShowSpacing - is the Spacing value used or ignored? - public bool ShowSpacing - { - get - { - return App.Overlay.CurrentLayoutSettings.ShowSpacing; - } - - set - { - if (App.Overlay.CurrentLayoutSettings.ShowSpacing != value) - { - App.Overlay.CurrentLayoutSettings.ShowSpacing = value; - UpdateTemplateLayoutModels(); - FirePropertyChanged(nameof(ShowSpacing)); - } - } - } - - // SensitivityRadius - how much space inside the zone to highlight the adjacent zone too - public int SensitivityRadius - { - get - { - return App.Overlay.CurrentLayoutSettings.SensitivityRadius; - } - - set - { - value = Math.Max(0, value); - if (App.Overlay.CurrentLayoutSettings.SensitivityRadius != value) - { - App.Overlay.CurrentLayoutSettings.SensitivityRadius = value; - UpdateTemplateLayoutModels(); - FirePropertyChanged(nameof(SensitivityRadius)); - } - } } // IsShiftKeyPressed - is the shift key currently being held down @@ -228,146 +129,63 @@ public bool IsCtrlKeyPressed private bool _isCtrlKeyPressed; - // UpdateLayoutModels - // Update the five default layouts based on the new ZoneCount - private void UpdateTemplateLayoutModels() + public LayoutModel BlankModel { - // Update the "Focus" Default Layout - _focusModel.Zones.Clear(); - - // Sanity check for imported settings that may have invalid data - if (ZoneCount < 1) - { - ZoneCount = 3; - } - - // If changing focus layout zones size and/or increment, - // same change should be applied in ZoneSet.cpp (ZoneSet::CalculateFocusLayout) - var workingArea = App.Overlay.WorkArea; - int topLeftCoordinate = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(100); // TODO: replace magic numbers - int width = (int)(workingArea.Width * 0.4); - int height = (int)(workingArea.Height * 0.4); - Int32Rect focusZoneRect = new Int32Rect(topLeftCoordinate, topLeftCoordinate, width, height); - int focusRectXIncrement = (ZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50); - int focusRectYIncrement = (ZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50); - - for (int i = 0; i < ZoneCount; i++) - { - _focusModel.Zones.Add(focusZoneRect); - focusZoneRect.X += focusRectXIncrement; - focusZoneRect.Y += focusRectYIncrement; - } - - // Update the "Rows" and "Columns" Default Layouts - // They can share their model, just transposed - _rowsModel.CellChildMap = new int[ZoneCount, 1]; - _columnsModel.CellChildMap = new int[1, ZoneCount]; - _rowsModel.Rows = _columnsModel.Columns = ZoneCount; - _rowsModel.RowPercents = _columnsModel.ColumnPercents = new List(ZoneCount); - - for (int i = 0; i < ZoneCount; i++) + get { - _rowsModel.CellChildMap[i, 0] = i; - _columnsModel.CellChildMap[0, i] = i; - - // Note: This is NOT equal to _multiplier / ZoneCount and is done like this to make - // the sum of all RowPercents exactly (_multiplier). - // _columnsModel is sharing the same array - _rowsModel.RowPercents.Add(((_multiplier * (i + 1)) / ZoneCount) - ((_multiplier * i) / ZoneCount)); + return _blankModel; } + } - // Update the "Grid" Default Layout - int rows = 1; - while (ZoneCount / rows >= rows) - { - rows++; - } + public static IList DefaultModels { get; } = new List(6); - rows--; - int cols = ZoneCount / rows; - if (ZoneCount % rows == 0) - { - // even grid - } - else + public static ObservableCollection CustomModels + { + get { - cols++; + return _customModels; } + } - _gridModel.Rows = rows; - _gridModel.Columns = cols; - _gridModel.RowPercents = new List(rows); - _gridModel.ColumnPercents = new List(cols); - _gridModel.CellChildMap = new int[rows, cols]; - - // Note: The following are NOT equal to _multiplier divided by rows or columns and is - // done like this to make the sum of all RowPercents exactly (_multiplier). - for (int row = 0; row < rows; row++) - { - _gridModel.RowPercents.Add(((_multiplier * (row + 1)) / rows) - ((_multiplier * row) / rows)); - } + private static ObservableCollection _customModels = new ObservableCollection(); - for (int col = 0; col < cols; col++) + public LayoutModel SelectedModel + { + get { - _gridModel.ColumnPercents.Add(((_multiplier * (col + 1)) / cols) - ((_multiplier * col) / cols)); + return _selectedModel; } - int index = 0; - for (int row = 0; row < rows; row++) + private set { - for (int col = 0; col < cols; col++) + if (_selectedModel != value) { - _gridModel.CellChildMap[row, col] = index++; - if (index == ZoneCount) - { - index--; - } + _selectedModel = value; + FirePropertyChanged(nameof(SelectedModel)); } } - - // Update the "Priority Grid" Default Layout - if (ZoneCount <= _priorityData.Length) - { - _priorityGridModel.Reload(_priorityData[ZoneCount - 1]); - } - else - { - // same as grid; - _priorityGridModel.Rows = _gridModel.Rows; - _priorityGridModel.Columns = _gridModel.Columns; - _priorityGridModel.RowPercents = _gridModel.RowPercents; - _priorityGridModel.ColumnPercents = _gridModel.ColumnPercents; - _priorityGridModel.CellChildMap = _gridModel.CellChildMap; - } } - public IList DefaultModels { get; } + private LayoutModel _selectedModel = null; - public static ObservableCollection CustomModels + public LayoutModel AppliedModel { get { - if (_customModels == null) - { - _customModels = LayoutModel.LoadCustomModels(); - _customModels.Insert(0, _blankCustomModel); - } - - return _customModels; + return _appliedModel; } - } - - private static ObservableCollection _customModels; - public CanvasLayoutModel BlankModel - { - get + private set { - return _blankModel; + if (_appliedModel != value) + { + _appliedModel = value; + FirePropertyChanged(nameof(AppliedModel)); + } } } - private CanvasLayoutModel _blankModel = new CanvasLayoutModel(string.Empty, LayoutType.Blank); + private LayoutModel _appliedModel = null; public static bool IsPredefinedLayout(LayoutModel model) { @@ -376,21 +194,13 @@ public static bool IsPredefinedLayout(LayoutModel model) public LayoutModel UpdateSelectedLayoutModel() { - UpdateTemplateLayoutModels(); - ResetAppliedModel(); - ResetSelectedModel(); - LayoutModel foundModel = null; LayoutSettings currentApplied = App.Overlay.CurrentLayoutSettings; // set new layout - if (currentApplied.Type == LayoutType.Blank) + if (currentApplied.Type == LayoutType.Custom) { - foundModel = BlankModel; - } - else if (currentApplied.Type == LayoutType.Custom) - { - foreach (LayoutModel model in MainWindowSettingsModel.CustomModels) + foreach (LayoutModel model in CustomModels) { if ("{" + model.Guid.ToString().ToUpperInvariant() + "}" == currentApplied.ZonesetUuid.ToUpperInvariant()) { @@ -408,6 +218,15 @@ public LayoutModel UpdateSelectedLayoutModel() { // found match foundModel = model; + foundModel.TemplateZoneCount = currentApplied.ZoneCount; + foundModel.SensitivityRadius = currentApplied.SensitivityRadius; + if (foundModel is GridLayoutModel grid) + { + grid.ShowSpacing = currentApplied.ShowSpacing; + grid.Spacing = currentApplied.Spacing; + } + + foundModel.InitTemplateZones(); break; } } @@ -415,80 +234,74 @@ public LayoutModel UpdateSelectedLayoutModel() if (foundModel == null) { - foundModel = DefaultModels[4]; // PriorityGrid + foundModel = _priorityGridModel; } - foundModel.IsSelected = true; - foundModel.IsApplied = true; - + SetSelectedModel(foundModel); + SetAppliedModel(foundModel); FirePropertyChanged(nameof(IsCustomLayoutActive)); return foundModel; } - public void ResetSelectedModel() + public void RestoreSelectedModel(LayoutModel model) { - foreach (LayoutModel model in CustomModels) + if (SelectedModel == null || model == null) { - if (model.IsSelected) - { - model.IsSelected = false; - break; - } + return; } - foreach (LayoutModel model in DefaultModels) + SelectedModel.SensitivityRadius = model.SensitivityRadius; + SelectedModel.TemplateZoneCount = model.TemplateZoneCount; + SelectedModel.IsSelected = model.IsSelected; + SelectedModel.IsApplied = model.IsApplied; + SelectedModel.Name = model.Name; + + if (model is GridLayoutModel grid) { - if (model.IsSelected) - { - model.IsSelected = false; - break; - } + ((GridLayoutModel)SelectedModel).Spacing = grid.Spacing; + ((GridLayoutModel)SelectedModel).ShowSpacing = grid.ShowSpacing; } } - public void ResetAppliedModel() + public void SetSelectedModel(LayoutModel model) { - foreach (LayoutModel model in CustomModels) + if (_selectedModel != null) { - if (model.IsApplied) - { - model.IsApplied = false; - break; - } + _selectedModel.IsSelected = false; } - foreach (LayoutModel model in DefaultModels) + if (model != null) { - if (model.IsApplied) - { - model.IsApplied = false; - break; - } + model.IsSelected = true; } + + SelectedModel = model; } - public void UpdateDesktopDependantProperties(LayoutSettings prevSettings) + public void SetAppliedModel(LayoutModel model) { - UpdateTemplateLayoutModels(); - - if (prevSettings.ZoneCount != ZoneCount) + if (_appliedModel != null) { - FirePropertyChanged(nameof(ZoneCount)); + _appliedModel.IsApplied = false; } - if (prevSettings.Spacing != Spacing) + if (model != null) { - FirePropertyChanged(nameof(Spacing)); + model.IsApplied = true; } - if (prevSettings.ShowSpacing != ShowSpacing) - { - FirePropertyChanged(nameof(ShowSpacing)); - } + AppliedModel = model; + } - if (prevSettings.SensitivityRadius != SensitivityRadius) + public void UpdateDefaultModels() + { + foreach (LayoutModel model in DefaultModels) { - FirePropertyChanged(nameof(SensitivityRadius)); + if (App.Overlay.CurrentLayoutSettings.Type == model.Type && App.Overlay.CurrentLayoutSettings.ZoneCount != model.TemplateZoneCount) + { + model.TemplateZoneCount = App.Overlay.CurrentLayoutSettings.ZoneCount; + model.InitTemplateZones(); + } } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Monitor.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Monitor.cs index 4290d6b1d5ff..f81aab8d3cc3 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Monitor.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Monitor.cs @@ -5,7 +5,6 @@ using System; using System.Reflection; using System.Windows; -using System.Windows.Input; using System.Windows.Media; using FancyZonesEditor.Utils; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs index be527fa5e8fd..ec0f2d2ab079 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs @@ -13,7 +13,6 @@ namespace FancyZonesEditor public class Overlay { private MainWindow _mainWindow; - private LayoutPreview _layoutPreview; private UserControl _editor; @@ -28,7 +27,7 @@ public Rect WorkArea return Monitors[CurrentDesktop].Device.WorkAreaRect; } - return default(Rect); + return default; } } @@ -54,7 +53,7 @@ public Window CurrentLayoutWindow return Monitors[CurrentDesktop].Window; } - return default(Window); + return default; } } @@ -100,14 +99,13 @@ public int CurrentDesktop return; } - var prevSettings = CurrentLayoutSettings; _currentDesktop = value; MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings; if (settings != null) { - settings.ResetAppliedModel(); - settings.UpdateDesktopDependantProperties(prevSettings); + settings.SetAppliedModel(null); + settings.UpdateDefaultModels(); } Update(); @@ -130,8 +128,8 @@ public bool SpanZonesAcrossMonitors if (_spanZonesAcrossMonitors) { - Rect workArea = default(Rect); - Rect bounds = default(Rect); + Rect workArea = default; + Rect bounds = default; foreach (Monitor monitor in Monitors) { @@ -174,7 +172,7 @@ public void Show() _layoutPreview = new LayoutPreview { IsActualSize = true, - Opacity = 0.5, + Opacity = 1, }; ShowLayout(); @@ -201,6 +199,30 @@ public void ShowLayout() } } + 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) { _layoutPreview = null; @@ -216,11 +238,10 @@ public void OpenEditor(LayoutModel model) CurrentLayoutWindow.Content = _editor; EditorWindow window; - bool isGrid = false; + if (model is GridLayoutModel) { window = new GridEditorWindow(); - isGrid = true; } else { @@ -230,14 +251,6 @@ public void OpenEditor(LayoutModel model) window.Owner = Monitors[App.Overlay.CurrentDesktop].Window; window.DataContext = model; window.Show(); - - if (isGrid) - { - (window as GridEditorWindow).NameTextBox().Focus(); - } - - window.LeftWindowCommands = null; - window.RightWindowCommands = null; } public void CloseEditor() @@ -246,7 +259,7 @@ public void CloseEditor() _layoutPreview = new LayoutPreview { IsActualSize = true, - Opacity = 0.5, + Opacity = 1, }; CurrentLayoutWindow.Content = _layoutPreview; @@ -318,8 +331,6 @@ private void OpenMainWindow() _mainWindow.ShowActivated = true; _mainWindow.Topmost = true; _mainWindow.Show(); - _mainWindow.LeftWindowCommands = null; - _mainWindow.RightWindowCommands = null; // window is set to topmost to make sure it shows on top of PowerToys settings page // we can reset topmost flag now diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs index e9ee173e4d79..161d9db94e44 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs @@ -78,6 +78,33 @@ public static string Apply { } } + /// + /// Looks up a localized string similar to Apply layout. + /// + public static string Apply_Layout { + get { + return ResourceManager.GetString("Apply_Layout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure?. + /// + public static string Are_You_Sure { + get { + return ResourceManager.GetString("Are_You_Sure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete this layout?. + /// + public static string Are_You_Sure_Description { + get { + return ResourceManager.GetString("Are_You_Sure_Description", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cancel. /// @@ -97,7 +124,7 @@ public static string Canvas_Layout_Editor { } /// - /// Looks up a localized string similar to Choose your layout for this desktop. + /// Looks up a localized string similar to Choose the layout for this desktop. /// public static string Choose_Layout { get { @@ -105,6 +132,15 @@ public static string Choose_Layout { } } + /// + /// Looks up a localized string similar to Choose layout type. + /// + public static string Choose_layout_type { + get { + return ResourceManager.GetString("Choose_layout_type", resourceCulture); + } + } + /// /// Looks up a localized string similar to Close. /// @@ -132,6 +168,33 @@ public static string Crash_Report_Message_Box_Text_Part2 { } } + /// + /// Looks up a localized string similar to Create. + /// + public static string Create { + get { + return ResourceManager.GetString("Create", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create custom layout. + /// + public static string Create_Custom_From_Template { + get { + return ResourceManager.GetString("Create_Custom_From_Template", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new layout. + /// + public static string Create_new_layout { + get { + return ResourceManager.GetString("Create_new_layout", resourceCulture); + } + } + /// /// Looks up a localized string similar to Custom. /// @@ -142,7 +205,7 @@ public static string Custom { } /// - /// Looks up a localized string similar to Create new custom. + /// Looks up a localized string similar to Custom layout. /// public static string Custom_Layout_Create_New { get { @@ -160,20 +223,29 @@ public static string Custom_Layout_Creator { } /// - /// Looks up a localized string similar to Delete custom layout. + /// Looks up a localized string similar to Custom layout. + /// + public static string Default_Custom_Layout_Name { + get { + return ResourceManager.GetString("Default_Custom_Layout_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete. /// - public static string Custom_Layout_Delete_Button { + public static string Delete { get { - return ResourceManager.GetString("Custom_Layout_Delete_Button", resourceCulture); + return ResourceManager.GetString("Delete", resourceCulture); } } /// - /// Looks up a localized string similar to Custom table layout creator. + /// Looks up a localized string similar to Delete zone. /// - public static string Custom_Table_Layout { + public static string Delete_Zone { get { - return ResourceManager.GetString("Custom_Table_Layout", resourceCulture); + return ResourceManager.GetString("Delete_Zone", resourceCulture); } } @@ -187,11 +259,38 @@ public static string Distance_adjacent_zones { } /// - /// Looks up a localized string similar to Edit selected layout. + /// Looks up a localized string similar to Duplicate. + /// + public static string Duplicate { + get { + return ResourceManager.GetString("Duplicate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit. + /// + public static string Edit { + get { + return ResourceManager.GetString("Edit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit layout. + /// + public static string Edit_Layout { + get { + return ResourceManager.GetString("Edit_Layout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit zones. /// - public static string Edit_Selected_Layout { + public static string Edit_zones { get { - return ResourceManager.GetString("Edit_Selected_Layout", resourceCulture); + return ResourceManager.GetString("Edit_zones", resourceCulture); } } @@ -276,6 +375,33 @@ public static string Error_Parsing_Device_Info { } } + /// + /// Looks up a localized string similar to 'zones-settings.json' contains malformed data.. + /// + public static string Error_Parsing_Zones_Settings_Malformed_Data { + get { + return ResourceManager.GetString("Error_Parsing_Zones_Settings_Malformed_Data", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Editor settings parsing error.. + /// + public static string Error_Parsing_Zones_Settings_Title { + get { + return ResourceManager.GetString("Error_Parsing_Zones_Settings_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Would you like to continue? Malformed data will be lost.. + /// + public static string Error_Parsing_Zones_Settings_User_Choice { + get { + return ResourceManager.GetString("Error_Parsing_Zones_Settings_User_Choice", resourceCulture); + } + } + /// /// Looks up a localized string similar to Error persisting custom layout. /// @@ -330,6 +456,60 @@ public static string Grid_Layout_Editor { } } + /// + /// Looks up a localized string similar to Create layouts that have overlapping zones. + /// + public static string Layout_Canvas_Description { + get { + return ResourceManager.GetString("Layout_Canvas_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Canvas. + /// + public static string Layout_Canvas_Title { + get { + return ResourceManager.GetString("Layout_Canvas_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create layouts that are horizontally or vertically stacked. + /// + public static string Layout_Grid_Description { + get { + return ResourceManager.GetString("Layout_Grid_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Grid. + /// + public static string Layout_Grid_Title { + get { + return ResourceManager.GetString("Layout_Grid_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Merge zones. + /// + public static string Merge_zones { + get { + return ResourceManager.GetString("Merge_zones", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Monitor. + /// + public static string Monitor { + get { + return ResourceManager.GetString("Monitor", resourceCulture); + } + } + /// /// Looks up a localized string similar to Name. /// @@ -340,7 +520,17 @@ public static string Name { } /// - /// Looks up a localized string similar to Note: Hold down Shift Key to change orientation of splitter. To merge zones, select the zones and click "merge".. + /// Looks up a localized string similar to Create or duplicate a layout to get started. + /// + public static string No_Custom_Layouts_Message { + get { + return ResourceManager.GetString("No_Custom_Layouts_Message", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hold down Shift key to change orientation of splitter. + ///To merge zones, select the zones and click "merge".. /// public static string Note_Custom_Table { get { @@ -348,6 +538,15 @@ public static string Note_Custom_Table { } } + /// + /// Looks up a localized string similar to Number of zones. + /// + public static string NumberOfZones { + get { + return ResourceManager.GetString("NumberOfZones", resourceCulture); + } + } + /// /// Looks up a localized string similar to Reset layout. /// @@ -358,7 +557,16 @@ public static string Reset_Layout { } /// - /// Looks up a localized string similar to Save and apply. + /// Looks up a localized string similar to Save. + /// + public static string Save { + get { + return ResourceManager.GetString("Save", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save & apply. /// public static string Save_Apply { get { @@ -367,38 +575,38 @@ public static string Save_Apply { } /// - /// Looks up a localized string similar to Show space around zones. + /// Looks up a localized string similar to Template settings. /// - public static string Show_Space_Zones { + public static string Settings { get { - return ResourceManager.GetString("Show_Space_Zones", resourceCulture); + return ResourceManager.GetString("Settings", resourceCulture); } } /// - /// Looks up a localized string similar to Space around zones. + /// Looks up a localized string similar to Show space around zones. /// - public static string Space_Around_Zones { + public static string Show_Space_Zones { get { - return ResourceManager.GetString("Space_Around_Zones", resourceCulture); + return ResourceManager.GetString("Show_Space_Zones", resourceCulture); } } /// - /// Looks up a localized string similar to Custom tab selected, press ctrl + tab to switch to Templates. + /// Looks up a localized string similar to Space around zones. /// - public static string Tab_Item_Custom { + public static string Space_Around_Zones { get { - return ResourceManager.GetString("Tab_Item_Custom", resourceCulture); + return ResourceManager.GetString("Space_Around_Zones", resourceCulture); } } /// - /// Looks up a localized string similar to Templates tab selected, press ctrl + tab to switch to Custom. + /// Looks up a localized string similar to No layout. /// - public static string Tab_Item_Templates { + public static string Template_Layout_Blank { get { - return ResourceManager.GetString("Tab_Item_Templates", resourceCulture); + return ResourceManager.GetString("Template_Layout_Blank", resourceCulture); } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx index 20203c17cf41..bd45e1ca4b5d 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx @@ -132,9 +132,6 @@ Grid layout editor - - Choose your layout for this desktop - Error logged to @@ -147,14 +144,11 @@ Custom layout creator - - Custom table layout creator - Distance to highlight adjacent zones - - Edit selected layout + + Edit layout FancyZones Editor @@ -166,10 +160,11 @@ Name - Note: Hold down Shift Key to change orientation of splitter. To merge zones, select the zones and click "merge". + Hold down Shift key to change orientation of splitter. +To merge zones, select the zones and click "merge". - Save and apply + Save & apply Show space around zones @@ -186,13 +181,6 @@ Increment number of zones in template layout - - Delete custom layout - - - Create new custom - As in Create new custom layout - FancyZones Editor arguments are invalid. @@ -220,12 +208,6 @@ Rows - - Custom tab selected, press ctrl + tab to switch to Templates - - - Templates tab selected, press ctrl + tab to switch to Custom - Close @@ -257,4 +239,99 @@ Reset layout as in Reset to a blank value + + Choose the layout for this desktop + + + Choose layout type + Dialog header that allows the user to select a specific layout type + + + Create + Button label that creates a new layout + + + Create new layout + Button label that allows the user to create a new layout + + + Custom layout + + + Delete + Context menu label that allows the user duplicate a layout + + + Duplicate + Context menu label that allows the user duplicate a layout + + + Edit + Context menu label that allows the user edit a layout + + + Create layouts that have overlapping zones + + + Canvas + + + Create layouts that are horizontally or vertically stacked + + + Grid + + + Merge zones + Button label that allows the user to merge 2 or more zones together + + + Monitor + + + Template settings + + + Are you sure? + + + Are you sure you want to delete this layout? + + + Edit zones + + + Create or duplicate a layout to get started + + + Delete zone + A tooltip on a button that allows the user to delete a zone + + + 'zones-settings.json' contains malformed data. + + + Editor settings parsing error. + + + Would you like to continue? Malformed data will be lost. + + + Apply layout + + + Save + + + Number of zones + + + No layout + + + Create custom layout + + + Custom layout + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Styles/ButtonStyles.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Styles/ButtonStyles.xaml new file mode 100644 index 000000000000..a0d8b7e1d484 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Styles/ButtonStyles.xaml @@ -0,0 +1,93 @@ + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Styles/LayoutPreviewStyles.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Styles/LayoutPreviewStyles.xaml new file mode 100644 index 000000000000..583398fcdbe1 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Styles/LayoutPreviewStyles.xaml @@ -0,0 +1,41 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Dark.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Dark.xaml new file mode 100644 index 000000000000..4ace93266dfa --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Dark.xaml @@ -0,0 +1,27 @@ + + + + Dark.Accent1 + PowerToysRun + Accent1 (Dark) + Dark + Accent1 + Black + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast1.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast1.xaml new file mode 100644 index 000000000000..4ad21a8ea6f4 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast1.xaml @@ -0,0 +1,27 @@ + + + + HighContrast.Accent2 + PowerToysRun + Accent2 (HighContrast) + HighContrast + Accent2 + White + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast2.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast2.xaml new file mode 100644 index 000000000000..6bdb2991a611 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast2.xaml @@ -0,0 +1,27 @@ + + + + HighContrast.Accent3 + PowerToysRun + Accent3 (HighContrast) + HighContrast + Accent3 + White + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastBlack.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastBlack.xaml new file mode 100644 index 000000000000..957683e13686 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastBlack.xaml @@ -0,0 +1,27 @@ + + + + HighContrast.Accent4 + PowerToysRun + Accent4 (HighContrast) + HighContrast + Accent4 + White + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastWhite.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastWhite.xaml new file mode 100644 index 000000000000..eefee53fe033 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastWhite.xaml @@ -0,0 +1,27 @@ + + + + HighContrast.Accent5 + PowerToysRun + Accent5 (HighContrast) + HighContrast + Accent5 + White + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Light.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Light.xaml new file mode 100644 index 000000000000..8b5d37109414 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Light.xaml @@ -0,0 +1,27 @@ + + + + Light.Accent1 + PowerToysRun + Accent1 (Light) + Light + Accent1 + White + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/CustomLibraryThemeProvider.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/CustomLibraryThemeProvider.cs new file mode 100644 index 000000000000..7b564894dcd0 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/CustomLibraryThemeProvider.cs @@ -0,0 +1,24 @@ +// 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.Collections.Generic; +using ControlzEx.Theming; + +namespace FancyZonesEditor.Utils +{ + public class CustomLibraryThemeProvider : LibraryThemeProvider + { + public static readonly CustomLibraryThemeProvider DefaultInstance = new CustomLibraryThemeProvider(); + + public CustomLibraryThemeProvider() + : base(true) + { + } + + /// + public override void FillColorSchemeValues(Dictionary values, RuntimeThemeColorValues colorValues) + { + } + } +} diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs index 8f092cd55f12..816308b365a9 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs @@ -4,10 +4,8 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.IO.Abstractions; -using System.Linq; using System.Text; using System.Text.Json; using System.Windows; @@ -18,16 +16,6 @@ namespace FancyZonesEditor.Utils public class FancyZonesEditorIO { // Non-localizable strings: JSON tags - private const string AppliedZonesetsJsonTag = "applied-zonesets"; - private const string DeviceIdJsonTag = "device-id"; - private const string ActiveZoneSetJsonTag = "active-zoneset"; - private const string UuidJsonTag = "uuid"; - private const string TypeJsonTag = "type"; - private const string EditorShowSpacingJsonTag = "editor-show-spacing"; - private const string EditorSpacingJsonTag = "editor-spacing"; - private const string EditorZoneCountJsonTag = "editor-zone-count"; - private const string EditorSensitivityRadiusJsonTag = "editor-sensitivity-radius"; - private const string BlankJsonTag = "blank"; private const string FocusJsonTag = "focus"; private const string ColumnsJsonTag = "columns"; @@ -36,45 +24,27 @@ public class FancyZonesEditorIO private const string PriorityGridJsonTag = "priority-grid"; private const string CustomJsonTag = "custom"; - private const string NameJsonTag = "name"; - private const string CustomZoneSetsJsonTag = "custom-zone-sets"; - private const string InfoJsonTag = "info"; - private const string RowsPercentageJsonTag = "rows-percentage"; - private const string ColumnsPercentageJsonTag = "columns-percentage"; - private const string CellChildMapJsonTag = "cell-child-map"; - private const string ZonesJsonTag = "zones"; - private const string CanvasJsonTag = "canvas"; - private const string RefWidthJsonTag = "ref-width"; - private const string RefHeightJsonTag = "ref-height"; - private const string XJsonTag = "X"; - private const string YJsonTag = "Y"; - private const string WidthJsonTag = "width"; - private const string HeightJsonTag = "height"; - // Non-localizable strings: Files private const string ZonesSettingsFile = "\\Microsoft\\PowerToys\\FancyZones\\zones-settings.json"; - private const string ActiveZoneSetsTmpFileName = "FancyZonesActiveZoneSets.json"; - private const string AppliedZoneSetsTmpFileName = "FancyZonesAppliedZoneSets.json"; - private const string DeletedCustomZoneSetsTmpFileName = "FancyZonesDeletedCustomZoneSets.json"; + private const string ParamsFile = "\\Microsoft\\PowerToys\\FancyZones\\editor-parameters.json"; // Non-localizable string: Multi-monitor id private const string MultiMonitorId = "FancyZones#MultiMonitorDevice"; private readonly IFileSystem _fileSystem = new FileSystem(); - private JsonSerializerOptions _options = new JsonSerializerOptions + private readonly JsonSerializerOptions _options = new JsonSerializerOptions { PropertyNamingPolicy = new DashCaseNamingPolicy(), + WriteIndented = true, }; - public string ActiveZoneSetTmpFile { get; private set; } - - public string AppliedZoneSetTmpFile { get; private set; } - - public string DeletedCustomZoneSetsTmpFile { get; private set; } + private List _unusedDevices = new List(); public string FancyZonesSettingsFile { get; private set; } + public string FancyZonesEditorParamsFile { get; private set; } + private enum CmdArgs { PowerToysPID = 0, @@ -89,41 +59,43 @@ private enum CmdArgs private struct NativeMonitorData { - public string Id { get; set; } + public string MonitorId { get; set; } public int Dpi { get; set; } - public int X { get; set; } + public int LeftCoordinate { get; set; } - public int Y { get; set; } + public int TopCoordinate { get; set; } + + public bool IsSelected { get; set; } public override string ToString() { var sb = new StringBuilder(); sb.Append("ID: "); - sb.AppendLine(Id); + sb.AppendLine(MonitorId); sb.Append("DPI: "); sb.AppendLine(Dpi.ToString()); sb.Append("X: "); - sb.AppendLine(X.ToString()); + sb.AppendLine(LeftCoordinate.ToString()); sb.Append("Y: "); - sb.AppendLine(Y.ToString()); + sb.AppendLine(TopCoordinate.ToString()); return sb.ToString(); } } - private struct ActiveZoneSetWrapper + private struct DeviceWrapper { - public string Uuid { get; set; } + public struct ActiveZoneSetWrapper + { + public string Uuid { get; set; } - public string Type { get; set; } - } + public string Type { get; set; } + } - private struct AppliedZoneSet - { public string DeviceId { get; set; } public ActiveZoneSetWrapper ActiveZoneset { get; set; } @@ -137,31 +109,110 @@ private struct AppliedZoneSet public int EditorSensitivityRadius { get; set; } } - private struct AppliedZonesetsToDesktops + private class CanvasInfoWrapper { - public List AppliedZonesets { get; set; } + public struct CanvasZoneWrapper + { + public int X { get; set; } + + public int Y { get; set; } + + public int Width { get; set; } + + public int Height { get; set; } + } + + public int RefWidth { get; set; } + + public int RefHeight { get; set; } + + public List Zones { get; set; } + + public int SensitivityRadius { get; set; } = LayoutSettings.DefaultSensitivityRadius; } - private struct DeletedCustomZoneSetsWrapper + private class GridInfoWrapper { - public List DeletedCustomZoneSets { get; set; } + public int Rows { get; set; } + + public int Columns { get; set; } + + public List RowsPercentage { get; set; } + + public List ColumnsPercentage { get; set; } + + public int[][] CellChildMap { get; set; } + + public bool ShowSpacing { get; set; } = LayoutSettings.DefaultShowSpacing; + + public int Spacing { get; set; } = LayoutSettings.DefaultSpacing; + + public int SensitivityRadius { get; set; } = LayoutSettings.DefaultSensitivityRadius; } - private struct CreatedCustomZoneSetsWrapper + private struct CustomLayoutWrapper { - public List CreatedCustomZoneSets { get; set; } + public string Uuid { get; set; } + + public string Name { get; set; } + + public string Type { get; set; } + + public JsonElement Info { get; set; } // CanvasInfoWrapper or GridInfoWrapper } - public FancyZonesEditorIO() + private struct TemplateLayoutWrapper { - string tmpDirPath = _fileSystem.Path.GetTempPath(); + public string Type { get; set; } + + public bool ShowSpacing { get; set; } + + public int Spacing { get; set; } - ActiveZoneSetTmpFile = tmpDirPath + ActiveZoneSetsTmpFileName; - AppliedZoneSetTmpFile = tmpDirPath + AppliedZoneSetsTmpFileName; - DeletedCustomZoneSetsTmpFile = tmpDirPath + DeletedCustomZoneSetsTmpFileName; + public int ZoneCount { get; set; } + public int SensitivityRadius { get; set; } + } + + private struct ZoneSettingsWrapper + { + public List Devices { get; set; } + + public List CustomZoneSets { get; set; } + + public List Templates { get; set; } + } + + private struct EditorParams + { + public int ProcessId { get; set; } + + public bool SpanZonesAcrossMonitors { get; set; } + + public List Monitors { get; set; } + } + + public struct ParsingResult + { + public bool Result { get; } + + public string Message { get; } + + public string MalformedData { get; } + + public ParsingResult(bool result, string message = "", string data = "") + { + Result = result; + Message = message; + MalformedData = data; + } + } + + public FancyZonesEditorIO() + { var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); FancyZonesSettingsFile = localAppDataDir + ZonesSettingsFile; + FancyZonesEditorParamsFile = localAppDataDir + ParamsFile; } // All strings in this function shouldn't be localized. @@ -200,14 +251,21 @@ public static void ParseCommandLineArguments() // Span zones across monitors App.Overlay.SpanZonesAcrossMonitors = int.Parse(argsParts[(int)CmdArgs.SpanZones]) == 1; + // Target monitor id + string targetMonitorName = argsParts[(int)CmdArgs.TargetMonitorId]; + if (!App.Overlay.SpanZonesAcrossMonitors) { - // Target monitor id - string targetMonitorName = argsParts[(int)CmdArgs.TargetMonitorId]; + // Test launch with custom monitors configuration + bool isCustomMonitorConfigurationMode = targetMonitorName.StartsWith("Monitor#"); + if (isCustomMonitorConfigurationMode) + { + App.Overlay.Monitors.Clear(); + } // Monitors count int count = int.Parse(argsParts[(int)CmdArgs.MonitorsCount]); - if (count != App.Overlay.DesktopsCount) + if (count != App.Overlay.DesktopsCount && !isCustomMonitorConfigurationMode) { MessageBox.Show(Properties.Resources.Error_Invalid_Arguments, Properties.Resources.Error_Message_Box_Title); ((App)Application.Current).Shutdown(); @@ -222,13 +280,14 @@ public static void ParseCommandLineArguments() for (int i = 0; i < count; i++) { var nativeData = default(NativeMonitorData); - nativeData.Id = argsParts[(int)CmdArgs.MonitorId + (i * monitorArgsCount)]; + nativeData.MonitorId = argsParts[(int)CmdArgs.MonitorId + (i * monitorArgsCount)]; nativeData.Dpi = int.Parse(argsParts[(int)CmdArgs.DPI + (i * monitorArgsCount)]); - nativeData.X = int.Parse(argsParts[(int)CmdArgs.MonitorLeft + (i * monitorArgsCount)]); - nativeData.Y = int.Parse(argsParts[(int)CmdArgs.MonitorTop + (i * monitorArgsCount)]); + nativeData.LeftCoordinate = int.Parse(argsParts[(int)CmdArgs.MonitorLeft + (i * monitorArgsCount)]); + nativeData.TopCoordinate = int.Parse(argsParts[(int)CmdArgs.MonitorTop + (i * monitorArgsCount)]); + nativeMonitorData.Add(nativeData); - if (nativeData.X == 0 && nativeData.Y == 0) + if (nativeData.LeftCoordinate == 0 && nativeData.TopCoordinate == 0) { primaryMonitorDPI = nativeData.Dpi; } @@ -244,30 +303,51 @@ public static void ParseCommandLineArguments() double scaleFactor = 96f / primaryMonitorDPI; // Update monitors data - foreach (Monitor monitor in monitors) + if (isCustomMonitorConfigurationMode) { - bool matchFound = false; - monitor.Scale(scaleFactor); + foreach (NativeMonitorData nativeData in nativeMonitorData) + { + var splittedId = nativeData.MonitorId.Split('_'); + int width = int.Parse(splittedId[1]); + int height = int.Parse(splittedId[2]); - double scaledBoundX = (int)(monitor.Device.UnscaledBounds.X * identifyScaleFactor); - double scaledBoundY = (int)(monitor.Device.UnscaledBounds.Y * identifyScaleFactor); + Rect bounds = new Rect(nativeData.LeftCoordinate, nativeData.TopCoordinate, width, height); + bool isPrimary = nativeData.LeftCoordinate == 0 && nativeData.TopCoordinate == 0; - foreach (NativeMonitorData nativeData in nativeMonitorData) + Monitor monitor = new Monitor(bounds, bounds, isPrimary); + monitor.Device.Id = nativeData.MonitorId; + monitor.Device.Dpi = nativeData.Dpi; + + monitors.Add(monitor); + } + } + else + { + foreach (Monitor monitor in monitors) { - // Can't do an exact match since the rounding algorithm used by the framework is different from ours - if (scaledBoundX >= (nativeData.X - 1) && scaledBoundX <= (nativeData.X + 1) && - scaledBoundY >= (nativeData.Y - 1) && scaledBoundY <= (nativeData.Y + 1)) + bool matchFound = false; + monitor.Scale(scaleFactor); + + double scaledBoundX = (int)(monitor.Device.UnscaledBounds.X * identifyScaleFactor); + double scaledBoundY = (int)(monitor.Device.UnscaledBounds.Y * identifyScaleFactor); + + foreach (NativeMonitorData nativeData in nativeMonitorData) { - monitor.Device.Id = nativeData.Id; - monitor.Device.Dpi = nativeData.Dpi; - matchFound = true; - break; + // Can't do an exact match since the rounding algorithm used by the framework is different from ours + if (scaledBoundX >= (nativeData.LeftCoordinate - 1) && scaledBoundX <= (nativeData.LeftCoordinate + 1) && + scaledBoundY >= (nativeData.TopCoordinate - 1) && scaledBoundY <= (nativeData.TopCoordinate + 1)) + { + monitor.Device.Id = nativeData.MonitorId; + monitor.Device.Dpi = nativeData.Dpi; + matchFound = true; + break; + } } - } - if (matchFound == false) - { - MessageBox.Show(string.Format(Properties.Resources.Error_Monitor_Match_Not_Found, monitor.Device.UnscaledBounds.ToString())); + if (matchFound == false) + { + MessageBox.Show(string.Format(Properties.Resources.Error_Monitor_Match_Not_Found, monitor.Device.UnscaledBounds.ToString())); + } } } @@ -282,6 +362,10 @@ public static void ParseCommandLineArguments() } } } + else + { + App.Overlay.Monitors[App.Overlay.CurrentDesktop].Device.Id = targetMonitorName; + } } catch (Exception) { @@ -290,230 +374,183 @@ public static void ParseCommandLineArguments() } } - public void ParseDeviceInfoData() + public ParsingResult ParseParams() { - try + if (_fileSystem.File.Exists(FancyZonesEditorParamsFile)) { - JsonElement jsonObject = default(JsonElement); + string data = string.Empty; - if (_fileSystem.File.Exists(ActiveZoneSetTmpFile)) + try { - Stream inputStream = _fileSystem.File.Open(ActiveZoneSetTmpFile, FileMode.Open); - jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement; - inputStream.Close(); + data = ReadFile(FancyZonesEditorParamsFile); + EditorParams editorParams = JsonSerializer.Deserialize(data, _options); - JsonElement json = jsonObject.GetProperty(AppliedZonesetsJsonTag); + // Process ID + App.PowerToysPID = editorParams.ProcessId; - int layoutId = 0; - for (int i = 0; i < json.GetArrayLength() && layoutId < App.Overlay.DesktopsCount; i++) - { - var zonesetData = json[i]; + // Span zones across monitors + App.Overlay.SpanZonesAcrossMonitors = editorParams.SpanZonesAcrossMonitors; - string deviceId = zonesetData.GetProperty(DeviceIdJsonTag).GetString(); + if (!App.Overlay.SpanZonesAcrossMonitors) + { + // Monitors count + if (editorParams.Monitors.Count != App.Overlay.DesktopsCount) + { + MessageBox.Show(Properties.Resources.Error_Invalid_Arguments, Properties.Resources.Error_Message_Box_Title); + ((App)Application.Current).Shutdown(); + } - string currentLayoutType = zonesetData.GetProperty(ActiveZoneSetJsonTag).GetProperty(TypeJsonTag).GetString(); - LayoutType type = JsonTagToLayoutType(currentLayoutType); + string targetMonitorName = string.Empty; - if (!App.Overlay.SpanZonesAcrossMonitors) + double primaryMonitorDPI = 96f; + double minimalUsedMonitorDPI = double.MaxValue; + foreach (NativeMonitorData nativeData in editorParams.Monitors) { - var monitors = App.Overlay.Monitors; - for (int monitorIndex = 0; monitorIndex < monitors.Count; monitorIndex++) + if (nativeData.LeftCoordinate == 0 && nativeData.TopCoordinate == 0) { - if (monitors[monitorIndex].Device.Id == deviceId) - { - monitors[monitorIndex].Settings = new LayoutSettings - { - ZonesetUuid = zonesetData.GetProperty(ActiveZoneSetJsonTag).GetProperty(UuidJsonTag).GetString(), - ShowSpacing = zonesetData.GetProperty(EditorShowSpacingJsonTag).GetBoolean(), - Spacing = zonesetData.GetProperty(EditorSpacingJsonTag).GetInt32(), - Type = type, - ZoneCount = zonesetData.GetProperty(EditorZoneCountJsonTag).GetInt32(), - SensitivityRadius = zonesetData.GetProperty(EditorSensitivityRadiusJsonTag).GetInt32(), - }; - - break; - } + primaryMonitorDPI = nativeData.Dpi; } - } - else - { - bool isLayoutMultiMonitor = deviceId.StartsWith(MultiMonitorId); - if (isLayoutMultiMonitor) - { - // one zoneset for all desktops - App.Overlay.Monitors[App.Overlay.CurrentDesktop].Settings = new LayoutSettings - { - ZonesetUuid = zonesetData.GetProperty(ActiveZoneSetJsonTag).GetProperty(UuidJsonTag).GetString(), - ShowSpacing = zonesetData.GetProperty(EditorShowSpacingJsonTag).GetBoolean(), - Spacing = zonesetData.GetProperty(EditorSpacingJsonTag).GetInt32(), - Type = type, - ZoneCount = zonesetData.GetProperty(EditorZoneCountJsonTag).GetInt32(), - SensitivityRadius = zonesetData.GetProperty(EditorSensitivityRadiusJsonTag).GetInt32(), - }; - App.Overlay.Monitors[App.Overlay.CurrentDesktop].Device.Id = deviceId; + if (minimalUsedMonitorDPI > nativeData.Dpi) + { + minimalUsedMonitorDPI = nativeData.Dpi; + } - break; + if (nativeData.IsSelected) + { + targetMonitorName = nativeData.MonitorId; } } - } - } - } - catch (Exception ex) - { - App.ShowExceptionMessageBox(Properties.Resources.Error_Parsing_Device_Info, ex); - } - } - public void ParseLayouts(ref ObservableCollection custom, ref List deleted) - { - try - { - Stream inputStream = _fileSystem.File.Open(FancyZonesSettingsFile, FileMode.Open); - JsonDocument jsonObject = JsonDocument.Parse(inputStream, options: default); - JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty(CustomZoneSetsJsonTag).EnumerateArray(); + var monitors = App.Overlay.Monitors; + double identifyScaleFactor = minimalUsedMonitorDPI / primaryMonitorDPI; + double scaleFactor = 96f / primaryMonitorDPI; - while (customZoneSetsEnumerator.MoveNext()) - { - var current = customZoneSetsEnumerator.Current; - string name = current.GetProperty(NameJsonTag).GetString(); - string type = current.GetProperty(TypeJsonTag).GetString(); - string uuid = current.GetProperty(UuidJsonTag).GetString(); - var info = current.GetProperty(InfoJsonTag); - - if (type.Equals(GridJsonTag, StringComparison.OrdinalIgnoreCase)) - { - bool error = false; - - int rows = info.GetProperty(RowsJsonTag).GetInt32(); - int columns = info.GetProperty(ColumnsJsonTag).GetInt32(); - - List rowsPercentage = new List(rows); - JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty(RowsPercentageJsonTag).EnumerateArray(); - - List columnsPercentage = new List(columns); - JsonElement.ArrayEnumerator columnsPercentageEnumerator = info.GetProperty(ColumnsPercentageJsonTag).EnumerateArray(); - - if (rows <= 0 || columns <= 0 || rowsPercentageEnumerator.Count() != rows || columnsPercentageEnumerator.Count() != columns) + // Update monitors data + foreach (Monitor monitor in monitors) { - error = true; - } + bool matchFound = false; + monitor.Scale(scaleFactor); - while (!error && rowsPercentageEnumerator.MoveNext()) - { - int percentage = rowsPercentageEnumerator.Current.GetInt32(); - if (percentage <= 0) + double scaledBoundX = (int)(monitor.Device.UnscaledBounds.X * identifyScaleFactor); + double scaledBoundY = (int)(monitor.Device.UnscaledBounds.Y * identifyScaleFactor); + + foreach (NativeMonitorData nativeData in editorParams.Monitors) { - error = true; - break; + // Can't do an exact match since the rounding algorithm used by the framework is different from ours + if (scaledBoundX >= (nativeData.LeftCoordinate - 1) && scaledBoundX <= (nativeData.LeftCoordinate + 1) && + scaledBoundY >= (nativeData.TopCoordinate - 1) && scaledBoundY <= (nativeData.TopCoordinate + 1)) + { + monitor.Device.Id = nativeData.MonitorId; + monitor.Device.Dpi = nativeData.Dpi; + matchFound = true; + break; + } } - rowsPercentage.Add(percentage); + if (matchFound == false) + { + MessageBox.Show(string.Format(Properties.Resources.Error_Monitor_Match_Not_Found, monitor.Device.UnscaledBounds.ToString())); + } } - while (!error && columnsPercentageEnumerator.MoveNext()) + // Set active desktop + for (int i = 0; i < monitors.Count; i++) { - int percentage = columnsPercentageEnumerator.Current.GetInt32(); - if (percentage <= 0) + var monitor = monitors[i]; + if (monitor.Device.Id == targetMonitorName) { - error = true; + App.Overlay.CurrentDesktop = i; break; } - - columnsPercentage.Add(percentage); - } - - int i = 0; - JsonElement.ArrayEnumerator cellChildMapRows = info.GetProperty(CellChildMapJsonTag).EnumerateArray(); - int[,] cellChildMap = new int[rows, columns]; - - if (cellChildMapRows.Count() != rows) - { - error = true; } - - while (!error && cellChildMapRows.MoveNext()) + } + else + { + // Update monitors data + foreach (Monitor monitor in App.Overlay.Monitors) { - int j = 0; - JsonElement.ArrayEnumerator cellChildMapRowElems = cellChildMapRows.Current.EnumerateArray(); - if (cellChildMapRowElems.Count() != columns) + bool matchFound = false; + foreach (NativeMonitorData nativeData in editorParams.Monitors) { - error = true; - break; + // Can't do an exact match since the rounding algorithm used by the framework is different from ours + if (monitor.Device.UnscaledBounds.X >= (nativeData.LeftCoordinate - 1) && monitor.Device.UnscaledBounds.X <= (nativeData.LeftCoordinate + 1) && + monitor.Device.UnscaledBounds.Y >= (nativeData.TopCoordinate - 1) && monitor.Device.UnscaledBounds.Y <= (nativeData.TopCoordinate + 1)) + { + monitor.Device.Id = nativeData.MonitorId; + monitor.Device.Dpi = nativeData.Dpi; + matchFound = true; + break; + } } - while (cellChildMapRowElems.MoveNext()) + if (matchFound == false) { - cellChildMap[i, j++] = cellChildMapRowElems.Current.GetInt32(); + MessageBox.Show(string.Format(Properties.Resources.Error_Monitor_Match_Not_Found, monitor.Device.UnscaledBounds.ToString())); } - - i++; } - - if (error) - { - App.ShowExceptionMessageBox(string.Format(Properties.Resources.Error_Layout_Malformed_Data, name)); - deleted.Add(Guid.Parse(uuid).ToString().ToUpperInvariant()); - continue; - } - - custom.Add(new GridLayoutModel(uuid, name, LayoutType.Custom, rows, columns, rowsPercentage, columnsPercentage, cellChildMap)); } - else if (type.Equals(CanvasJsonTag, StringComparison.OrdinalIgnoreCase)) - { - int workAreaWidth = info.GetProperty(RefWidthJsonTag).GetInt32(); - int workAreaHeight = info.GetProperty(RefHeightJsonTag).GetInt32(); - - JsonElement.ArrayEnumerator zonesEnumerator = info.GetProperty(ZonesJsonTag).EnumerateArray(); - IList zones = new List(); - - bool error = false; + } + catch (Exception ex) + { + return new ParsingResult(false, ex.Message, data); + } - if (workAreaWidth <= 0 || workAreaHeight <= 0) - { - error = true; - } + return new ParsingResult(true); + } + else + { + return new ParsingResult(false); + } + } - while (!error && zonesEnumerator.MoveNext()) - { - int x = zonesEnumerator.Current.GetProperty(XJsonTag).GetInt32(); - int y = zonesEnumerator.Current.GetProperty(YJsonTag).GetInt32(); - int width = zonesEnumerator.Current.GetProperty(WidthJsonTag).GetInt32(); - int height = zonesEnumerator.Current.GetProperty(HeightJsonTag).GetInt32(); + public ParsingResult ParseZoneSettings() + { + _unusedDevices.Clear(); - if (width <= 0 || height <= 0) - { - error = true; - break; - } + if (_fileSystem.File.Exists(FancyZonesSettingsFile)) + { + ZoneSettingsWrapper zoneSettings; + string settingsString = string.Empty; - zones.Add(new Int32Rect(x, y, width, height)); - } + try + { + settingsString = ReadFile(FancyZonesSettingsFile); + zoneSettings = JsonSerializer.Deserialize(settingsString, _options); + } + catch (Exception ex) + { + return new ParsingResult(false, ex.Message, settingsString); + } - if (error) - { - App.ShowExceptionMessageBox(string.Format(Properties.Resources.Error_Layout_Malformed_Data, name)); - deleted.Add(Guid.Parse(uuid).ToString().ToUpperInvariant()); - continue; - } + try + { + bool devicesParsingResult = SetDevices(zoneSettings.Devices); + bool customZonesParsingResult = SetCustomLayouts(zoneSettings.CustomZoneSets); + bool templatesParsingResult = SetTemplateLayouts(zoneSettings.Templates); - custom.Add(new CanvasLayoutModel(uuid, name, LayoutType.Custom, zones, workAreaWidth, workAreaHeight)); + if (!devicesParsingResult || !customZonesParsingResult) + { + return new ParsingResult(false, Properties.Resources.Error_Parsing_Zones_Settings_Malformed_Data, settingsString); } } - - inputStream.Close(); - } - catch (Exception ex) - { - App.ShowExceptionMessageBox(Properties.Resources.Error_Loading_Custom_Layouts, ex); + catch (Exception ex) + { + return new ParsingResult(false, ex.Message, settingsString); + } } + + return new ParsingResult(true); } - public void SerializeAppliedLayouts() + public void SerializeZoneSettings() { - AppliedZonesetsToDesktops applied = new AppliedZonesetsToDesktops { }; - applied.AppliedZonesets = new List(); + ZoneSettingsWrapper zoneSettings = new ZoneSettingsWrapper { }; + zoneSettings.Devices = new List(); + zoneSettings.CustomZoneSets = new List(); + zoneSettings.Templates = new List(); + // Serialize used devices foreach (var monitor in App.Overlay.Monitors) { LayoutSettings zoneset = monitor.Settings; @@ -522,17 +559,14 @@ public void SerializeAppliedLayouts() continue; } - ActiveZoneSetWrapper activeZoneSet = new ActiveZoneSetWrapper - { - Uuid = zoneset.ZonesetUuid, - }; - - activeZoneSet.Type = LayoutTypeToJsonTag(zoneset.Type); - - applied.AppliedZonesets.Add(new AppliedZoneSet + zoneSettings.Devices.Add(new DeviceWrapper { DeviceId = monitor.Device.Id, - ActiveZoneset = activeZoneSet, + ActiveZoneset = new DeviceWrapper.ActiveZoneSetWrapper + { + Uuid = zoneset.ZonesetUuid, + Type = LayoutTypeToJsonTag(zoneset.Type), + }, EditorShowSpacing = zoneset.ShowSpacing, EditorSpacing = zoneset.Spacing, EditorZoneCount = zoneset.ZoneCount, @@ -540,10 +574,121 @@ public void SerializeAppliedLayouts() }); } + // Serialize unused devices + foreach (var device in _unusedDevices) + { + zoneSettings.Devices.Add(device); + } + + // Serialize custom zonesets + foreach (LayoutModel layout in MainWindowSettingsModel.CustomModels) + { + JsonElement info; + string type; + + if (layout is CanvasLayoutModel) + { + type = CanvasLayoutModel.ModelTypeID; + var canvasLayout = layout as CanvasLayoutModel; + + var canvasRect = canvasLayout.CanvasRect; + if (canvasRect.Width == 0 || canvasRect.Height == 0) + { + canvasRect = App.Overlay.WorkArea; + } + + var wrapper = new CanvasInfoWrapper + { + RefWidth = (int)canvasRect.Width, + RefHeight = (int)canvasRect.Height, + Zones = new List(), + SensitivityRadius = canvasLayout.SensitivityRadius, + }; + + foreach (var zone in canvasLayout.Zones) + { + wrapper.Zones.Add(new CanvasInfoWrapper.CanvasZoneWrapper + { + X = zone.X, + Y = zone.Y, + Width = zone.Width, + Height = zone.Height, + }); + } + + string json = JsonSerializer.Serialize(wrapper, _options); + info = JsonSerializer.Deserialize(json); + } + else if (layout is GridLayoutModel) + { + type = GridLayoutModel.ModelTypeID; + var gridLayout = layout as GridLayoutModel; + + var cells = new int[gridLayout.Rows][]; + for (int row = 0; row < gridLayout.Rows; row++) + { + cells[row] = new int[gridLayout.Columns]; + for (int column = 0; column < gridLayout.Columns; column++) + { + cells[row][column] = gridLayout.CellChildMap[row, column]; + } + } + + var wrapper = new GridInfoWrapper + { + Rows = gridLayout.Rows, + Columns = gridLayout.Columns, + RowsPercentage = gridLayout.RowPercents, + ColumnsPercentage = gridLayout.ColumnPercents, + CellChildMap = cells, + ShowSpacing = gridLayout.ShowSpacing, + Spacing = gridLayout.Spacing, + SensitivityRadius = gridLayout.SensitivityRadius, + }; + + string json = JsonSerializer.Serialize(wrapper, _options); + info = JsonSerializer.Deserialize(json); + } + else + { + // Error + continue; + } + + CustomLayoutWrapper customLayout = new CustomLayoutWrapper + { + Uuid = layout.Uuid, + Name = layout.Name, + Type = type, + Info = info, + }; + + zoneSettings.CustomZoneSets.Add(customLayout); + } + + // Serialize template layouts + foreach (LayoutModel layout in MainWindowSettingsModel.DefaultModels) + { + TemplateLayoutWrapper wrapper = new TemplateLayoutWrapper + { + Type = LayoutTypeToJsonTag(layout.Type), + SensitivityRadius = layout.SensitivityRadius, + ZoneCount = layout.TemplateZoneCount, + }; + + if (layout is GridLayoutModel grid) + { + wrapper.ShowSpacing = grid.ShowSpacing; + wrapper.Spacing = grid.Spacing; + } + + zoneSettings.Templates.Add(wrapper); + } + try { - string jsonString = JsonSerializer.Serialize(applied, _options); - _fileSystem.File.WriteAllText(ActiveZoneSetTmpFile, jsonString); + string jsonString = JsonSerializer.Serialize(zoneSettings, _options); + _fileSystem.File.WriteAllText(FancyZonesSettingsFile, jsonString); } catch (Exception ex) { @@ -551,68 +696,177 @@ public void SerializeAppliedLayouts() } } - public void SerializeDeletedCustomZoneSets(List models) + private string ReadFile(string fileName) { - DeletedCustomZoneSetsWrapper deletedLayouts = new DeletedCustomZoneSetsWrapper - { - DeletedCustomZoneSets = models, - }; + Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open); + StreamReader reader = new StreamReader(inputStream); + string data = reader.ReadToEnd(); + inputStream.Close(); + return data; + } - try + private bool SetDevices(List devices) + { + if (devices == null) { - string jsonString = JsonSerializer.Serialize(deletedLayouts, _options); - _fileSystem.File.WriteAllText(DeletedCustomZoneSetsTmpFile, jsonString); + return false; } - catch (Exception ex) + + bool result = true; + var monitors = App.Overlay.Monitors; + foreach (var device in devices) { - App.ShowExceptionMessageBox(Properties.Resources.Error_Serializing_Deleted_Layouts, ex); + if (device.DeviceId == null || device.DeviceId.Length == 0 || device.ActiveZoneset.Uuid == null || device.ActiveZoneset.Uuid.Length == 0) + { + result = false; + continue; + } + + bool unused = true; + foreach (Monitor monitor in monitors) + { + if (monitor.Device.Id == device.DeviceId) + { + var settings = new LayoutSettings + { + ZonesetUuid = device.ActiveZoneset.Uuid, + ShowSpacing = device.EditorShowSpacing, + Spacing = device.EditorSpacing, + Type = JsonTagToLayoutType(device.ActiveZoneset.Type), + ZoneCount = device.EditorZoneCount, + SensitivityRadius = device.EditorSensitivityRadius, + }; + + monitor.Settings = settings; + unused = false; + break; + } + } + + if (unused) + { + _unusedDevices.Add(device); + } } + + return result; } - public void SerializeCreatedCustomZonesets(List models) + private bool SetCustomLayouts(List customLayouts) { - CreatedCustomZoneSetsWrapper layouts = new CreatedCustomZoneSetsWrapper + if (customLayouts == null) { - CreatedCustomZoneSets = models, - }; + return false; + } - try + MainWindowSettingsModel.CustomModels.Clear(); + bool result = true; + + foreach (var zoneSet in customLayouts) { - string jsonString = JsonSerializer.Serialize(layouts, _options); - _fileSystem.File.WriteAllText(AppliedZoneSetTmpFile, jsonString); + if (zoneSet.Uuid == null || zoneSet.Uuid.Length == 0) + { + result = false; + continue; + } + + LayoutModel layout; + if (zoneSet.Type == CanvasLayoutModel.ModelTypeID) + { + var info = JsonSerializer.Deserialize(zoneSet.Info.GetRawText(), _options); + + var zones = new List(); + foreach (var zone in info.Zones) + { + zones.Add(new Int32Rect { X = (int)zone.X, Y = (int)zone.Y, Width = (int)zone.Width, Height = (int)zone.Height }); + } + + layout = new CanvasLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, zones, info.RefWidth, info.RefHeight); + layout.SensitivityRadius = info.SensitivityRadius; + } + else if (zoneSet.Type == GridLayoutModel.ModelTypeID) + { + var info = JsonSerializer.Deserialize(zoneSet.Info.GetRawText(), _options); + + var cells = new int[info.Rows, info.Columns]; + for (int row = 0; row < info.Rows; row++) + { + for (int column = 0; column < info.Columns; column++) + { + cells[row, column] = info.CellChildMap[row][column]; + } + } + + layout = new GridLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, info.Rows, info.Columns, info.RowsPercentage, info.ColumnsPercentage, cells); + layout.SensitivityRadius = info.SensitivityRadius; + (layout as GridLayoutModel).ShowSpacing = info.ShowSpacing; + (layout as GridLayoutModel).Spacing = info.Spacing; + } + else + { + result = false; + continue; + } + + MainWindowSettingsModel.CustomModels.Add(layout); } - catch (Exception ex) + + return result; + } + + private bool SetTemplateLayouts(List templateLayouts) + { + if (templateLayouts == null) { - App.ShowExceptionMessageBox(Properties.Resources.Error_Persisting_Custom_Layout, ex); + return false; } + + foreach (var wrapper in templateLayouts) + { + var type = JsonTagToLayoutType(wrapper.Type); + + foreach (var layout in MainWindowSettingsModel.DefaultModels) + { + if (layout.Type == type) + { + layout.SensitivityRadius = wrapper.SensitivityRadius; + layout.TemplateZoneCount = wrapper.ZoneCount; + + if (layout is GridLayoutModel grid) + { + grid.ShowSpacing = wrapper.ShowSpacing; + grid.Spacing = wrapper.Spacing; + } + + layout.InitTemplateZones(); + } + } + } + + return true; } private LayoutType JsonTagToLayoutType(string tag) { - LayoutType type = LayoutType.Blank; switch (tag) { + case BlankJsonTag: + return LayoutType.Blank; case FocusJsonTag: - type = LayoutType.Focus; - break; + return LayoutType.Focus; case ColumnsJsonTag: - type = LayoutType.Columns; - break; + return LayoutType.Columns; case RowsJsonTag: - type = LayoutType.Rows; - break; + return LayoutType.Rows; case GridJsonTag: - type = LayoutType.Grid; - break; + return LayoutType.Grid; case PriorityGridJsonTag: - type = LayoutType.PriorityGrid; - break; + return LayoutType.PriorityGrid; case CustomJsonTag: - type = LayoutType.Custom; - break; + return LayoutType.Custom; } - return type; + return LayoutType.Blank; } private string LayoutTypeToJsonTag(LayoutType type) @@ -623,19 +877,19 @@ private string LayoutTypeToJsonTag(LayoutType type) return BlankJsonTag; case LayoutType.Focus: return FocusJsonTag; - case LayoutType.Rows: - return RowsJsonTag; case LayoutType.Columns: return ColumnsJsonTag; + case LayoutType.Rows: + return RowsJsonTag; case LayoutType.Grid: return GridJsonTag; case LayoutType.PriorityGrid: return PriorityGridJsonTag; case LayoutType.Custom: return CustomJsonTag; + default: + return string.Empty; } - - return string.Empty; } private static string ParsingCmdArgsErrorReport(string args, int count, string targetMonitorName, List monitorData, List monitors) diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/ThemeManager.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/ThemeManager.cs new file mode 100644 index 000000000000..2316d51712dd --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/ThemeManager.cs @@ -0,0 +1,200 @@ +// 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.Linq; +using System.Windows; +using ControlzEx.Theming; +using Microsoft.Win32; + +namespace FancyZonesEditor.Utils +{ + public class ThemeManager : IDisposable + { + private readonly Application _app; + private const string LightTheme = "Light.Accent1"; + private const string DarkTheme = "Dark.Accent1"; + private const string HighContrastOneTheme = "HighContrast.Accent2"; + private const string HighContrastTwoTheme = "HighContrast.Accent3"; + private const string HighContrastBlackTheme = "HighContrast.Accent4"; + private const string HighContrastWhiteTheme = "HighContrast.Accent5"; + + private Theme currentTheme; + private bool _disposed; + + public event ThemeChangedHandler ThemeChanged; + + public ThemeManager(Application app) + { + _app = app; + + Uri highContrastOneThemeUri = new Uri("pack://application:,,,/Themes/HighContrast1.xaml"); + Uri highContrastTwoThemeUri = new Uri("pack://application:,,,/Themes/HighContrast2.xaml"); + Uri highContrastBlackThemeUri = new Uri("pack://application:,,,/Themes/HighContrastWhite.xaml"); + Uri highContrastWhiteThemeUri = new Uri("pack://application:,,,/Themes/HighContrastBlack.xaml"); + Uri lightThemeUri = new Uri("pack://application:,,,/Themes/Light.xaml"); + Uri darkThemeUri = new Uri("pack://application:,,,/Themes/Dark.xaml"); + + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastOneThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastTwoThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastBlackThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastWhiteThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + lightThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + darkThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + + ResetTheme(); + ControlzEx.Theming.ThemeManager.Current.ThemeSyncMode = ThemeSyncMode.SyncWithAppMode; + ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged; + SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged; + } + + private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(SystemParameters.HighContrast)) + { + ResetTheme(); + } + } + + public Theme GetCurrentTheme() + { + return currentTheme; + } + + private static Theme GetHighContrastBaseType() + { + string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes"; + string theme = (string)Registry.GetValue(registryKey, "CurrentTheme", string.Empty); + theme = theme.Split('\\').Last().Split('.').First().ToString(); + + switch (theme) + { + case "hc1": + return Theme.HighContrastOne; + case "hc2": + return Theme.HighContrastTwo; + case "hcwhite": + return Theme.HighContrastWhite; + case "hcblack": + return Theme.HighContrastBlack; + default: + return Theme.None; + } + } + + private void ResetTheme() + { + if (SystemParameters.HighContrast) + { + Theme highContrastBaseType = GetHighContrastBaseType(); + ChangeTheme(highContrastBaseType); + } + else + { + string baseColor = WindowsThemeHelper.GetWindowsBaseColor(); + ChangeTheme((Theme)Enum.Parse(typeof(Theme), baseColor)); + } + } + + private void ChangeTheme(Theme theme) + { + Theme oldTheme = currentTheme; + if (theme == currentTheme) + { + return; + } + + if (theme == Theme.HighContrastOne) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastOneTheme); + currentTheme = Theme.HighContrastOne; + } + else if (theme == Theme.HighContrastTwo) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastTwoTheme); + currentTheme = Theme.HighContrastTwo; + } + else if (theme == Theme.HighContrastWhite) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastWhiteTheme); + currentTheme = Theme.HighContrastWhite; + } + else if (theme == Theme.HighContrastBlack) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastBlackTheme); + currentTheme = Theme.HighContrastBlack; + } + else if (theme == Theme.Light) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, LightTheme); + currentTheme = Theme.Light; + } + else if (theme == Theme.Dark) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, DarkTheme); + currentTheme = Theme.Dark; + } + else + { + currentTheme = Theme.None; + } + + ThemeChanged?.Invoke(oldTheme, currentTheme); + } + + private void Current_ThemeChanged(object sender, ThemeChangedEventArgs e) + { + ResetTheme(); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged; + SystemParameters.StaticPropertyChanged -= SystemParameters_StaticPropertyChanged; + _disposed = true; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + + public delegate void ThemeChangedHandler(Theme oldTheme, Theme newTheme); + + public enum Theme + { + None, + Light, + Dark, + HighContrastOne, + HighContrastTwo, + HighContrastBlack, + HighContrastWhite, + } +} diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/ViewModels/MonitorViewModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/ViewModels/MonitorViewModel.cs index 080b6b509ff9..eff99c9aa1f9 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/ViewModels/MonitorViewModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/ViewModels/MonitorViewModel.cs @@ -4,7 +4,6 @@ using System.Collections.ObjectModel; using System.ComponentModel; -using System.Windows; using FancyZonesEditor.Utils; namespace FancyZonesEditor.ViewModels @@ -22,14 +21,6 @@ public class MonitorViewModel : INotifyPropertyChanged public static double DesktopPreviewMultiplier { get; private set; } - public Visibility DesktopsPanelVisibility - { - get - { - return App.Overlay.MultiMonitorMode ? Visibility.Visible : Visibility.Collapsed; - } - } - public RelayCommand AddCommand { get; set; } public RelayCommand DeleteCommand { get; set; } @@ -57,7 +48,7 @@ public MonitorViewModel() double maxMultiplier = MaxPreviewDisplaySize / maxDimension; double minMultiplier = MinPreviewDisplaySize / minDimension; - DesktopPreviewMultiplier = (minMultiplier + maxMultiplier) / 2; + DesktopPreviewMultiplier = (minMultiplier + maxMultiplier) / 3.5; } private void RaisePropertyChanged(string propertyName) diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/WindowLayout.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/WindowLayout.xaml index 8601aa0a5c1c..1fd306a0afe5 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/WindowLayout.xaml +++ b/src/modules/fancyzones/editor/FancyZonesEditor/WindowLayout.xaml @@ -4,7 +4,8 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" - Title="Window1" Height="450" Width="800" + Title="Window1" Height="450" + Width="800" WindowState="Maximized" ShowInTaskbar="False" ResizeMode="NoResize" @@ -16,7 +17,7 @@ - + @@ -25,7 +26,7 @@