diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index bd976f35e845..33218dc41042 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -360,7 +360,6 @@ - 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..9f9830c47426 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs @@ -40,27 +40,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 +330,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 +374,7 @@ private void ArrangeGridRects(Size arrangeSize) Preview.Height = workArea.Height; GridLayoutModel model = Model; - if (model == null) + if (model == null || _data == null) { return; } @@ -383,9 +385,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 +411,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..6a6fa585da18 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs @@ -3,40 +3,31 @@ // 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.Controls.Primitives; using System.Windows.Input; +using System.Windows.Media; 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; - // Localizable string - private static readonly string _defaultNamePrefix = "Custom Layout "; - public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize; - public double SettingsTextMaxWidth - { - get - { - return (Width / 2) - 60; - } - } - public MainWindow(bool spanZonesAcrossMonitors, Rect workArea) { InitializeComponent(); @@ -59,7 +50,6 @@ public MainWindow(bool spanZonesAcrossMonitors, Rect workArea) public void Update() { DataContext = _settings; - SetSelectedItem(); } private void MainWindow_KeyUp(object sender, KeyEventArgs e) @@ -72,30 +62,36 @@ private void MainWindow_KeyUp(object sender, KeyEventArgs e) 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)) { - _settings.ZoneCount++; + return; } - } - private void NewCustomLayoutButton_Click(object sender, RoutedEventArgs e) - { - WindowLayout window = new WindowLayout(); - window.Show(); - Hide(); + if (model.TemplateZoneCount < LayoutSettings.MaxZones) + { + model.TemplateZoneCount++; + } } private void LayoutItem_Click(object sender, MouseButtonEventArgs e) { - Select(((Border)sender).DataContext as LayoutModel); + Select(((Grid)sender).DataContext as LayoutModel); + Apply(); } private void LayoutItem_Focused(object sender, RoutedEventArgs e) @@ -103,7 +99,7 @@ private void LayoutItem_Focused(object sender, RoutedEventArgs e) 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) { @@ -124,8 +120,18 @@ private void Select(LayoutModel newSelection) App.Overlay.CurrentDataContext = newSelection; } - private void EditLayout_Click(object sender, RoutedEventArgs e) + private async void NewLayoutButton_Click(object sender, RoutedEventArgs e) { + LayoutNameText.Text = string.Empty; + GridLayoutRadioButton.IsChecked = true; + GridLayoutRadioButton.Focus(); + await NewLayoutDialog.ShowAsync(); + } + + private void DuplicateLayout_Click(object sender, RoutedEventArgs e) + { + CloseFlyout((DependencyObject)sender); + var mainEditor = App.Overlay; if (!(mainEditor.CurrentDataContext is LayoutModel model)) { @@ -133,26 +139,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,11 +173,12 @@ private void EditLayout_Click(object sender, RoutedEventArgs e) } } } - - model.Name = _defaultNamePrefix + (++maxCustomIndex); } - mainEditor.OpenEditor(model); + model.Name = name + " (" + (++maxCustomIndex) + ')'; + + model.Persist(); + App.FancyZonesEditorIO.SerializeZoneSettings(); } private void Apply_Click(object sender, RoutedEventArgs e) @@ -182,46 +195,48 @@ private void Apply() { model.Apply(); } - - if (!mainEditor.MultiMonitorMode) - { - Close(); - } } 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 async void DeleteLayout_Click(object sender, RoutedEventArgs e) { - SetSelectedItem(); - } + CloseFlyout((DependencyObject)sender); - private void SetSelectedItem() - { - foreach (LayoutModel model in MainWindowSettingsModel.CustomModels) + var dialog = new ModernWpf.Controls.ContentDialog() { - if (model.IsSelected) - { - TemplateTab.SelectedItem = model; - return; - } + 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) + { + LayoutModel model = ((FrameworkElement)sender).DataContext as LayoutModel; + model.Delete(); } } - private void OnDelete(object sender, RoutedEventArgs e) + private void EditLayout_Click(object sender, RoutedEventArgs e) { - LayoutModel model = ((FrameworkElement)sender).DataContext as LayoutModel; - if (model.IsSelected) + CloseFlyout((DependencyObject)sender); + + var mainEditor = App.Overlay; + if (!(mainEditor.CurrentDataContext is LayoutModel model)) { - SetSelectedItem(); + return; } - model.Delete(); + model.IsSelected = false; + Hide(); + + mainEditor.OpenEditor(model); } private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) @@ -239,15 +254,9 @@ private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e e.Handled = true; } - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - this.Close(); - } - private void Reset_Click(object sender, RoutedEventArgs e) { var overlay = App.Overlay; - MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings; if (overlay.CurrentDataContext is LayoutModel model) { @@ -255,15 +264,92 @@ private void Reset_Click(object sender, RoutedEventArgs e) model.IsApplied = false; } - overlay.CurrentLayoutSettings.ZonesetUuid = settings.BlankModel.Uuid; + overlay.CurrentLayoutSettings.ZonesetUuid = MainWindowSettingsModel.BlankModel.Uuid; overlay.CurrentLayoutSettings.Type = LayoutType.Blank; - overlay.CurrentDataContext = settings.BlankModel; + overlay.CurrentDataContext = MainWindowSettingsModel.BlankModel; - App.FancyZonesEditorIO.SerializeAppliedLayouts(); + App.FancyZonesEditorIO.SerializeZoneSettings(); + } + + private void NewLayoutDialog_PrimaryButtonClick(ModernWpf.Controls.ContentDialog sender, ModernWpf.Controls.ContentDialogButtonClickEventArgs args) + { + LayoutModel selectedLayoutModel; - if (!overlay.MultiMonitorMode) + if (GridLayoutRadioButton.IsChecked == true) + { + // 1:1 Copy from MainWindowSettingsModel, so probably needs to be refactored / combined. + int multiplier = 10000; + int zoneCount = 3; + + GridLayoutModel columnsModel = new GridLayoutModel(LayoutNameText.Text, LayoutType.Columns) + { + Rows = 1, + RowPercents = new List(1) { multiplier }, + }; + + columnsModel.CellChildMap = new int[1, zoneCount]; + columnsModel.Columns = zoneCount; + columnsModel.ColumnPercents = new List(zoneCount); + + for (int i = 0; i < 3; i++) + { + columnsModel.CellChildMap[0, i] = i; + columnsModel.ColumnPercents.Add(((multiplier * (i + 1)) / zoneCount) - ((multiplier * i) / zoneCount)); + } + + selectedLayoutModel = columnsModel; + } + else + { + selectedLayoutModel = new CanvasLayoutModel(LayoutNameText.Text, LayoutType.Blank); + } + + 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) + { + monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext); + } + + private void CloseFlyout(DependencyObject closingButton) + { + try + { + var flyoutPresenter = VisualTreeHelper.GetParent(closingButton); + while (flyoutPresenter != null && !(flyoutPresenter is FlyoutPresenter)) + { + flyoutPresenter = VisualTreeHelper.GetParent(flyoutPresenter); + } + + if (flyoutPresenter != null) + { + var popup = ((FrameworkElement)flyoutPresenter).Parent as Popup; + if (popup != null) + { + popup.IsOpen = false; + } + } + } + catch (Exception) { - Close(); } } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs index 03ac60aaaaa3..ac3fab388bcc 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Text.Json; using System.Windows; namespace FancyZonesEditor.Models @@ -14,7 +13,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; } @@ -54,6 +53,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 +92,7 @@ public override LayoutModel Clone() layout.Zones.Add(zone); } + layout.SensitivityRadius = SensitivityRadius; return layout; } @@ -81,37 +103,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 +112,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..ea6ef33a5f2b 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; -using System.Text.Json; -using System.Windows; namespace FancyZonesEditor.Models { @@ -14,7 +12,25 @@ namespace FancyZonesEditor.Models public class GridLayoutModel : LayoutModel { // Non-localizable strings - private const string ModelTypeID = "grid"; + public const string ModelTypeID = "grid"; + + private const int _multiplier = 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 @@ -67,6 +83,52 @@ public int Columns // ColumnPercents - represents the %age width of each column in the grid public List ColumnPercents { get; set; } + // ShowSpacing - flag if free space between cells should be presented + public bool ShowSpacing + { + get + { + return _showSpacing; + } + + set + { + if (value != _showSpacing) + { + _showSpacing = value; + App.Overlay.Monitors[App.Overlay.CurrentDesktop].Settings.ShowSpacing = value; + + FirePropertyChanged(nameof(ShowSpacing)); + App.FancyZonesEditorIO.SerializeZoneSettings(); + } + } + } + + private bool _showSpacing = LayoutSettings.DefaultShowSpacing; + + // Spacing - free space between cells + public int Spacing + { + get + { + return _spacing; + } + + set + { + if (value != _spacing) + { + _spacing = value; + App.Overlay.Monitors[App.Overlay.CurrentDesktop].Settings.Spacing = value; + + FirePropertyChanged(nameof(Spacing)); + App.FancyZonesEditorIO.SerializeZoneSettings(); + } + } + } + + 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 // TODO: do I need FreeZones on the data model? - I think I do @@ -102,8 +164,8 @@ 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 +187,8 @@ public void Reload(byte[] data) CellChildMap[row, col] = data[i++]; } } + + FirePropertyChanged(); } // Clone @@ -171,71 +235,139 @@ public void RestoreTo(GridLayoutModel layout) } layout.ColumnPercents = colPercents; + + layout.ShowSpacing = ShowSpacing; + layout.Spacing = Spacing; + layout.SensitivityRadius = SensitivityRadius; } - private struct GridLayoutInfo + // InitTemplateZones + // Creates zones based on template zones count + public override void InitTemplateZones() { - public int Rows { get; set; } + switch (Type) + { + case LayoutType.Rows: + InitRows(); + break; + case LayoutType.Columns: + InitColumns(); + break; + case LayoutType.Grid: + InitGrid(); + break; + case LayoutType.PriorityGrid: + InitPriorityGrid(); + break; + } - public int Columns { get; set; } + FirePropertyChanged(); + } - public List RowsPercentage { get; set; } + // PersistData + // Implements the LayoutModel.PersistData abstract method + protected override void PersistData() + { + AddCustomLayout(this); + } + + private void InitRows() + { + CellChildMap = new int[TemplateZoneCount, 1]; + RowPercents = new List(TemplateZoneCount); + + 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(((_multiplier * (i + 1)) / TemplateZoneCount) - ((_multiplier * 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(((_multiplier * (i + 1)) / TemplateZoneCount) - ((_multiplier * 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(((_multiplier * (row + 1)) / rows) - ((_multiplier * row) / rows)); + } + + for (int col = 0; col < cols; col++) + { + ColumnPercents.Add(((_multiplier * (col + 1)) / cols) - ((_multiplier * 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..3a9a57021f88 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 { @@ -103,6 +100,7 @@ public bool IsSelected private bool _isSelected; + // IsApplied (not-persisted) - tracks whether or not this LayoutModel is applied in the picker public bool IsApplied { get @@ -122,6 +120,52 @@ public bool IsApplied private bool _isApplied; + public int SensitivityRadius + { + get + { + return _sensitivityRadius; + } + + set + { + if (value != _sensitivityRadius) + { + _sensitivityRadius = value; + App.Overlay.Monitors[App.Overlay.CurrentDesktop].Settings.SensitivityRadius = value; + App.FancyZonesEditorIO.SerializeZoneSettings(); + } + } + } + + 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; + + App.Overlay.Monitors[App.Overlay.CurrentDesktop].Settings.ZoneCount = value; + App.FancyZonesEditorIO.SerializeZoneSettings(); + + InitTemplateZones(); + + FirePropertyChanged(nameof(TemplateZoneCount)); + } + } + } + + private int _zoneCount = LayoutSettings.DefaultZoneCount; + // implementation of INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; @@ -134,11 +178,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 +190,27 @@ 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); + App.FancyZonesEditorIO.SerializeZoneSettings(); } - 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(); @@ -209,9 +232,17 @@ public void Apply() // update settings App.Overlay.CurrentLayoutSettings.ZonesetUuid = Uuid; App.Overlay.CurrentLayoutSettings.Type = Type; + App.Overlay.CurrentLayoutSettings.SensitivityRadius = SensitivityRadius; + App.Overlay.CurrentLayoutSettings.ZoneCount = TemplateZoneCount; + + if (this is GridLayoutModel) + { + App.Overlay.CurrentLayoutSettings.ShowSpacing = ((GridLayoutModel)this).ShowSpacing; + App.Overlay.CurrentLayoutSettings.Spacing = ((GridLayoutModel)this).Spacing; + } // update temp file - App.FancyZonesEditorIO.SerializeAppliedLayouts(); + App.FancyZonesEditorIO.SerializeZoneSettings(); } } } 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..ab2c1c93c61e 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs @@ -7,7 +7,6 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Windows; using FancyZonesEditor.Models; namespace FancyZonesEditor @@ -25,7 +24,6 @@ private enum DeviceIdParts VirtualDesktopId, } - private static CanvasLayoutModel _blankCustomModel; private readonly CanvasLayoutModel _focusModel; private readonly GridLayoutModel _rowsModel; private readonly GridLayoutModel _columnsModel; @@ -44,22 +42,6 @@ private enum DeviceIdParts 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 @@ -83,6 +65,7 @@ public MainWindowSettingsModel() // Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid DefaultModels = new List(5); _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) @@ -90,6 +73,7 @@ public MainWindowSettingsModel() Rows = 1, RowPercents = new List(1) { _multiplier }, }; + _columnsModel.InitTemplateZones(); DefaultModels.Add(_columnsModel); _rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows) @@ -97,95 +81,16 @@ public MainWindowSettingsModel() Columns = 1, ColumnPercents = new List(1) { _multiplier }, }; + _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,138 +133,19 @@ public bool IsCtrlKeyPressed private bool _isCtrlKeyPressed; - // UpdateLayoutModels - // Update the five default layouts based on the new ZoneCount - private void UpdateTemplateLayoutModels() - { - // 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++) - { - _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)); - } - - // Update the "Grid" Default Layout - int rows = 1; - while (ZoneCount / rows >= rows) - { - rows++; - } - - rows--; - int cols = ZoneCount / rows; - if (ZoneCount % rows == 0) - { - // even grid - } - else - { - cols++; - } - - _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)); - } - - for (int col = 0; col < cols; col++) - { - _gridModel.ColumnPercents.Add(((_multiplier * (col + 1)) / cols) - ((_multiplier * col) / cols)); - } - - int index = 0; - for (int row = 0; row < rows; row++) - { - for (int col = 0; col < cols; col++) - { - _gridModel.CellChildMap[row, col] = index++; - if (index == ZoneCount) - { - index--; - } - } - } - - // 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; } public static ObservableCollection CustomModels { get { - if (_customModels == null) - { - _customModels = LayoutModel.LoadCustomModels(); - _customModels.Insert(0, _blankCustomModel); - } - return _customModels; } } - private static ObservableCollection _customModels; + private static ObservableCollection _customModels = new ObservableCollection(); - public CanvasLayoutModel BlankModel + public static CanvasLayoutModel BlankModel { get { @@ -367,7 +153,7 @@ public CanvasLayoutModel BlankModel } } - private CanvasLayoutModel _blankModel = new CanvasLayoutModel(string.Empty, LayoutType.Blank); + private static CanvasLayoutModel _blankModel = new CanvasLayoutModel(string.Empty, LayoutType.Blank); public static bool IsPredefinedLayout(LayoutModel model) { @@ -376,7 +162,6 @@ public static bool IsPredefinedLayout(LayoutModel model) public LayoutModel UpdateSelectedLayoutModel() { - UpdateTemplateLayoutModels(); ResetAppliedModel(); ResetSelectedModel(); @@ -390,7 +175,7 @@ public LayoutModel UpdateSelectedLayoutModel() } 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 +193,15 @@ public LayoutModel UpdateSelectedLayoutModel() { // found match foundModel = model; + foundModel.TemplateZoneCount = currentApplied.ZoneCount; + foundModel.SensitivityRadius = currentApplied.SensitivityRadius; + if (foundModel is GridLayoutModel) + { + ((GridLayoutModel)foundModel).ShowSpacing = currentApplied.ShowSpacing; + ((GridLayoutModel)foundModel).Spacing = currentApplied.Spacing; + } + + foundModel.InitTemplateZones(); break; } } @@ -467,28 +261,15 @@ public void ResetAppliedModel() } } - public void UpdateDesktopDependantProperties(LayoutSettings prevSettings) + public void UpdateDefaultModels() { - UpdateTemplateLayoutModels(); - - if (prevSettings.ZoneCount != ZoneCount) - { - FirePropertyChanged(nameof(ZoneCount)); - } - - if (prevSettings.Spacing != Spacing) - { - FirePropertyChanged(nameof(Spacing)); - } - - if (prevSettings.ShowSpacing != ShowSpacing) - { - FirePropertyChanged(nameof(ShowSpacing)); - } - - if (prevSettings.SensitivityRadius != SensitivityRadius) + 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/Overlay.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs index be527fa5e8fd..6530a38e215b 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs @@ -6,14 +6,15 @@ using System.Collections.Generic; using System.Windows; using System.Windows.Controls; +using System.Windows.Media; using FancyZonesEditor.Models; +using Windows.UI.Xaml.Media; namespace FancyZonesEditor { public class Overlay { private MainWindow _mainWindow; - private LayoutPreview _layoutPreview; private UserControl _editor; @@ -28,7 +29,7 @@ public Rect WorkArea return Monitors[CurrentDesktop].Device.WorkAreaRect; } - return default(Rect); + return default; } } @@ -54,7 +55,7 @@ public Window CurrentLayoutWindow return Monitors[CurrentDesktop].Window; } - return default(Window); + return default; } } @@ -100,14 +101,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.UpdateDefaultModels(); } Update(); @@ -130,8 +130,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 +174,7 @@ public void Show() _layoutPreview = new LayoutPreview { IsActualSize = true, - Opacity = 0.5, + Opacity = 1, }; ShowLayout(); @@ -216,11 +216,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 +229,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 +237,7 @@ public void CloseEditor() _layoutPreview = new LayoutPreview { IsActualSize = true, - Opacity = 0.5, + Opacity = 1, }; CurrentLayoutWindow.Content = _layoutPreview; @@ -318,8 +309,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..0aab0c75a1d7 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs @@ -78,6 +78,24 @@ public static string Apply { } } + /// + /// 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 +115,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 +123,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 +159,24 @@ 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 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 +187,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 +205,20 @@ public static string Custom_Layout_Creator { } /// - /// Looks up a localized string similar to Delete custom layout. + /// 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 +232,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 +348,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 +429,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 +493,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 { @@ -358,7 +521,7 @@ public static string Reset_Layout { } /// - /// Looks up a localized string similar to Save and apply. + /// Looks up a localized string similar to Save & apply. /// public static string Save_Apply { get { @@ -367,38 +530,29 @@ 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. - /// - public static string Space_Around_Zones { - get { - return ResourceManager.GetString("Space_Around_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 Show space around zones. /// - public static string Tab_Item_Custom { + public static string Show_Space_Zones { get { - return ResourceManager.GetString("Tab_Item_Custom", resourceCulture); + return ResourceManager.GetString("Show_Space_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 Space around zones. /// - public static string Tab_Item_Templates { + public static string Space_Around_Zones { get { - return ResourceManager.GetString("Tab_Item_Templates", resourceCulture); + return ResourceManager.GetString("Space_Around_Zones", resourceCulture); } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx index 20203c17cf41..1dc1ca816696 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,81 @@ 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. + \ 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..c80b6819ddb6 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Styles/LayoutPreviewStyles.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + \ 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..1ec1456324a2 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Dark.xaml @@ -0,0 +1,29 @@ + + + + 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..825e5c693b69 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast1.xaml @@ -0,0 +1,29 @@ + + + + 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..9f3f474fbe70 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrast2.xaml @@ -0,0 +1,28 @@ + + + + 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..c08ccd6a1465 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastBlack.xaml @@ -0,0 +1,28 @@ + + + + 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..2fa22281f574 --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/HighContrastWhite.xaml @@ -0,0 +1,28 @@ + + + + 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..9ca6d6828eca --- /dev/null +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Themes/Light.xaml @@ -0,0 +1,28 @@ + + + + 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..8366c2fc7aa8 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,26 @@ 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(), }; - 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 +58,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 +108,95 @@ private struct AppliedZoneSet public int EditorSensitivityRadius { get; set; } } - private struct AppliedZonesetsToDesktops + private class CanvasInfoWrapper + { + 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 class GridInfoWrapper + { + 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 CustomLayoutWrapper { - public List AppliedZonesets { 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 } - private struct DeletedCustomZoneSetsWrapper + private struct ZoneSettingsWrapper { - public List DeletedCustomZoneSets { get; set; } + public List Devices { get; set; } + + public List CustomZoneSets { get; set; } } - private struct CreatedCustomZoneSetsWrapper + private struct EditorParams { - public List CreatedCustomZoneSets { get; set; } + public int ProcessId { get; set; } + + public bool SpanZonesAcrossMonitors { get; set; } + + public List Monitors { get; set; } } - public FancyZonesEditorIO() + public struct ParsingResult { - string tmpDirPath = _fileSystem.Path.GetTempPath(); + public bool Result { get; } + + public string Message { get; } - ActiveZoneSetTmpFile = tmpDirPath + ActiveZoneSetsTmpFileName; - AppliedZoneSetTmpFile = tmpDirPath + AppliedZoneSetsTmpFileName; - DeletedCustomZoneSetsTmpFile = tmpDirPath + DeletedCustomZoneSetsTmpFileName; + 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 +235,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 +264,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 +287,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 +346,10 @@ public static void ParseCommandLineArguments() } } } + else + { + App.Overlay.Monitors[App.Overlay.CurrentDesktop].Device.Id = targetMonitorName; + } } catch (Exception) { @@ -290,230 +358,181 @@ 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(); - 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; + var monitors = App.Overlay.Monitors; + double identifyScaleFactor = minimalUsedMonitorDPI / primaryMonitorDPI; + double scaleFactor = 96f / primaryMonitorDPI; - 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); - 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(); + // Serialize used devices foreach (var monitor in App.Overlay.Monitors) { LayoutSettings zoneset = monitor.Settings; @@ -522,17 +541,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,51 +556,220 @@ public void SerializeAppliedLayouts() }); } - try + // Serialize unused devices + foreach (var device in _unusedDevices) { - string jsonString = JsonSerializer.Serialize(applied, _options); - _fileSystem.File.WriteAllText(ActiveZoneSetTmpFile, jsonString); + zoneSettings.Devices.Add(device); } - catch (Exception ex) - { - App.ShowExceptionMessageBox(Properties.Resources.Error_Applying_Layout, ex); - } - } - public void SerializeDeletedCustomZoneSets(List models) - { - DeletedCustomZoneSetsWrapper deletedLayouts = new DeletedCustomZoneSetsWrapper + // Serialize custom zonesets + foreach (LayoutModel layout in MainWindowSettingsModel.CustomModels) { - DeletedCustomZoneSets = models, - }; + if (layout.Type == LayoutType.Blank) + { + continue; + } + + 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); + } try { - string jsonString = JsonSerializer.Serialize(deletedLayouts, _options); - _fileSystem.File.WriteAllText(DeletedCustomZoneSetsTmpFile, jsonString); + string jsonString = JsonSerializer.Serialize(zoneSettings, _options); + _fileSystem.File.WriteAllText(FancyZonesSettingsFile, jsonString); } catch (Exception ex) { - App.ShowExceptionMessageBox(Properties.Resources.Error_Serializing_Deleted_Layouts, ex); + App.ShowExceptionMessageBox(Properties.Resources.Error_Applying_Layout, ex); } } - public void SerializeCreatedCustomZonesets(List models) + private string ReadFile(string fileName) { - CreatedCustomZoneSetsWrapper layouts = new CreatedCustomZoneSetsWrapper - { - CreatedCustomZoneSets = 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) + { + bool result = true; + var monitors = App.Overlay.Monitors; + foreach (var device in devices) { - string jsonString = JsonSerializer.Serialize(layouts, _options); - _fileSystem.File.WriteAllText(AppliedZoneSetTmpFile, jsonString); + 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); + } } - catch (Exception ex) + + return result; + } + + private bool SetCustomLayouts(List customLayouts) + { + MainWindowSettingsModel.CustomModels.Clear(); + bool result = true; + + foreach (var zoneSet in customLayouts) { - App.ShowExceptionMessageBox(Properties.Resources.Error_Persisting_Custom_Layout, ex); + 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); } + + return result; } private LayoutType JsonTagToLayoutType(string tag) @@ -619,23 +804,21 @@ private string LayoutTypeToJsonTag(LayoutType type) { switch (type) { - case LayoutType.Blank: - 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..8f1c51dd76b9 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/ViewModels/MonitorViewModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/ViewModels/MonitorViewModel.cs @@ -22,14 +22,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 +49,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 @@