mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-08 04:07:40 +02:00
[FancyZones Editor] New UX for the FZ editor. (#9325)
* Removed MetroWindow, added theming support and modernWPF * Rmoved MahApps refs * Removed MahApps * Updated canvas zones * Updated GridEditor * Fixes * UI updates * New layout type selection dialog * New editor UI * Updates * Fix * UI enhancements * Updated UI * Added styles to layoutpreview * Accesibility improvements * Accesibility and styling improvements * Fix * Cleaned up brushes * Updated UX * Updated UI * Added no layouts description * Fix * UI fixes * [FZ Editor] Serialize/deserialize settings (#8615) * conflicts fix * [FZ Editor] Parse json file instead of command line args (#8649) * [FZ Editor] Serialize/deserialize settings fix (#8707) * [FZ Editor] Hide unsupported settings in custom layouts flyouts (#8716) * [FZ Editor] Duplicate custom layouts (#8718) * [FZ Editor] Duplicate layout behavior (#8720) * New UX proposal * Updated spacing * Switching to toggleswitches * Revert toggleswitch * Updated colorbrush * Updated string for saving label * Updated UI * Dark theme color fixes * Removed space * [FZ Editor] Bind dialog properties (#9199) * Resize editor window to fit the content in single-monitor mode (#9203) * Editor opening fix (#9207) * Disable "Create" button if the Name textbox is empty (#9212) * [FZ Editor] Changed edit dialog for template layouts. (#9233) * [FZ Editor] Small fixes and refactoring. (#9236) * new layout creation refactoring * "Save and apply" applies the layout * number of zones header hide * [FZ Editor] Empty layout template. (#9237) * [FZ Editor] Move "Duplicate" and "Delete" buttons to the Edit dialog. (#9272) * [FZ Editor] Preview the applied layout after editing another layout. (#9278) * Fixed "Save and apply" button behavior (#9286) * [FZ Editor] Save template layouts in the settings. (#9283) * Added default custom layout name (#9291) * close dialog before opening zones editor (#9302) * Pressing Esc closes dialogs (#9301) * [FZ Editor] Reset applied layout to "No layout" if it was deleted. (#9315) * [FZ Editor] Dark theme colors (#9317) * "Number of zones" buttons colors. (#9321) * rebase fix * added ModernWpf.dll * address PR comments: updated colors * added comments, replaced magic numbers * refactoring * merge zones crash fix * removed redundant using directive Co-authored-by: Niels Laute <niels9001@hotmail.com> Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
@@ -3,40 +3,32 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using FancyZonesEditor.Models;
|
||||
using MahApps.Metro.Controls;
|
||||
using FancyZonesEditor.Utils;
|
||||
using ModernWpf.Controls;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : MetroWindow
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
// TODO: share the constants b/w C# Editor and FancyZoneLib
|
||||
public const int MaxZones = 40;
|
||||
private const int DefaultWrapPanelItemSize = 262;
|
||||
private const int SmallWrapPanelItemSize = 180;
|
||||
private const int DefaultWrapPanelItemSize = 164;
|
||||
private const int SmallWrapPanelItemSize = 164;
|
||||
private const int MinimalForDefaultWrapPanelsHeight = 900;
|
||||
|
||||
private readonly MainWindowSettingsModel _settings = ((App)Application.Current).MainWindowSettings;
|
||||
private LayoutModel _backup = null;
|
||||
|
||||
// Localizable string
|
||||
private static readonly string _defaultNamePrefix = "Custom Layout ";
|
||||
private ContentDialog _openedDialog = null;
|
||||
|
||||
public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize;
|
||||
|
||||
public double SettingsTextMaxWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Width / 2) - 60;
|
||||
}
|
||||
}
|
||||
|
||||
public MainWindow(bool spanZonesAcrossMonitors, Rect workArea)
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -51,59 +43,84 @@ namespace FancyZonesEditor
|
||||
|
||||
if (workArea.Height < MinimalForDefaultWrapPanelsHeight || App.Overlay.MultiMonitorMode)
|
||||
{
|
||||
SizeToContent = SizeToContent.WidthAndHeight;
|
||||
WrapPanelItemSize = SmallWrapPanelItemSize;
|
||||
}
|
||||
|
||||
SizeToContent = SizeToContent.WidthAndHeight;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
DataContext = _settings;
|
||||
SetSelectedItem();
|
||||
}
|
||||
|
||||
private void MainWindow_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Escape)
|
||||
{
|
||||
OnClosing(sender, null);
|
||||
if (_openedDialog != null)
|
||||
{
|
||||
_openedDialog.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnClosing(sender, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DecrementZones_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_settings.ZoneCount > 1)
|
||||
var mainEditor = App.Overlay;
|
||||
if (!(mainEditor.CurrentDataContext is LayoutModel model))
|
||||
{
|
||||
_settings.ZoneCount--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.TemplateZoneCount > 1)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (model.TemplateZoneCount < LayoutSettings.MaxZones)
|
||||
{
|
||||
model.TemplateZoneCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private void NewCustomLayoutButton_Click(object sender, RoutedEventArgs e)
|
||||
private void LayoutItem_MouseEnter(object sender, MouseEventArgs e)
|
||||
{
|
||||
WindowLayout window = new WindowLayout();
|
||||
window.Show();
|
||||
Hide();
|
||||
// Select(((Grid)sender).DataContext as LayoutModel);
|
||||
}
|
||||
|
||||
private void LayoutItem_Click(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
Select(((Border)sender).DataContext as LayoutModel);
|
||||
LayoutModel selectedLayoutModel = ((Grid)sender).DataContext as LayoutModel;
|
||||
Select(selectedLayoutModel);
|
||||
Apply();
|
||||
}
|
||||
|
||||
private void LayoutItem_Focused(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Ignore focus on Edit button click
|
||||
if (e.Source is Button)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Select(((Border)sender).DataContext as LayoutModel);
|
||||
}
|
||||
|
||||
private void LayoutItem_Apply(object sender, KeyEventArgs e)
|
||||
private void LayoutItem_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Return || e.Key == Key.Space)
|
||||
{
|
||||
@@ -115,17 +132,39 @@ namespace FancyZonesEditor
|
||||
|
||||
private void Select(LayoutModel newSelection)
|
||||
{
|
||||
if (App.Overlay.CurrentDataContext is LayoutModel currentSelection)
|
||||
{
|
||||
currentSelection.IsSelected = false;
|
||||
}
|
||||
|
||||
newSelection.IsSelected = true;
|
||||
_settings.SetSelectedModel(newSelection);
|
||||
App.Overlay.CurrentDataContext = newSelection;
|
||||
}
|
||||
|
||||
private void EditLayout_Click(object sender, RoutedEventArgs e)
|
||||
private async void NewLayoutButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string defaultNamePrefix = FancyZonesEditor.Properties.Resources.Default_Custom_Layout_Name;
|
||||
int maxCustomIndex = 0;
|
||||
foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
|
||||
{
|
||||
string name = customModel.Name;
|
||||
if (name.StartsWith(defaultNamePrefix))
|
||||
{
|
||||
if (int.TryParse(name.Substring(defaultNamePrefix.Length), out int i))
|
||||
{
|
||||
if (maxCustomIndex < i)
|
||||
{
|
||||
maxCustomIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LayoutNameText.Text = defaultNamePrefix + " " + (++maxCustomIndex);
|
||||
GridLayoutRadioButton.IsChecked = true;
|
||||
GridLayoutRadioButton.Focus();
|
||||
await NewLayoutDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private void DuplicateLayout_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
EditLayoutDialog.Hide();
|
||||
|
||||
var mainEditor = App.Overlay;
|
||||
if (!(mainEditor.CurrentDataContext is LayoutModel model))
|
||||
{
|
||||
@@ -133,26 +172,32 @@ namespace FancyZonesEditor
|
||||
}
|
||||
|
||||
model.IsSelected = false;
|
||||
Hide();
|
||||
|
||||
bool isPredefinedLayout = MainWindowSettingsModel.IsPredefinedLayout(model);
|
||||
// make a copy
|
||||
model = model.Clone();
|
||||
mainEditor.CurrentDataContext = model;
|
||||
|
||||
if (!MainWindowSettingsModel.CustomModels.Contains(model) || isPredefinedLayout)
|
||||
string name = model.Name;
|
||||
var index = name.LastIndexOf('(');
|
||||
if (index != -1)
|
||||
{
|
||||
if (isPredefinedLayout)
|
||||
{
|
||||
// make a copy
|
||||
model = model.Clone();
|
||||
mainEditor.CurrentDataContext = model;
|
||||
}
|
||||
name = name.Remove(index);
|
||||
name = name.TrimEnd();
|
||||
}
|
||||
|
||||
int maxCustomIndex = 0;
|
||||
foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
|
||||
int maxCustomIndex = 0;
|
||||
foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
|
||||
{
|
||||
string customModelName = customModel.Name;
|
||||
if (customModelName.StartsWith(name))
|
||||
{
|
||||
string name = customModel.Name;
|
||||
if (name.StartsWith(_defaultNamePrefix))
|
||||
int openBraceIndex = customModelName.LastIndexOf('(');
|
||||
int closeBraceIndex = customModelName.LastIndexOf(')');
|
||||
if (openBraceIndex != -1 && closeBraceIndex != -1)
|
||||
{
|
||||
if (int.TryParse(name.Substring(_defaultNamePrefix.Length), out int i))
|
||||
string indexSubstring = customModelName.Substring(openBraceIndex + 1, closeBraceIndex - openBraceIndex - 1);
|
||||
|
||||
if (int.TryParse(indexSubstring, out int i))
|
||||
{
|
||||
if (maxCustomIndex < i)
|
||||
{
|
||||
@@ -161,67 +206,70 @@ namespace FancyZonesEditor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model.Name = _defaultNamePrefix + (++maxCustomIndex);
|
||||
}
|
||||
|
||||
mainEditor.OpenEditor(model);
|
||||
}
|
||||
model.Name = name + " (" + (++maxCustomIndex) + ')';
|
||||
|
||||
private void Apply_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Apply();
|
||||
model.Persist();
|
||||
|
||||
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
((App)Application.Current).MainWindowSettings.ResetAppliedModel();
|
||||
|
||||
var mainEditor = App.Overlay;
|
||||
if (mainEditor.CurrentDataContext is LayoutModel model)
|
||||
{
|
||||
model.Apply();
|
||||
}
|
||||
|
||||
if (!mainEditor.MultiMonitorMode)
|
||||
{
|
||||
Close();
|
||||
_settings.SetAppliedModel(model);
|
||||
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClosing(object sender, EventArgs e)
|
||||
{
|
||||
LayoutModel.SerializeDeletedCustomZoneSets();
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
App.Overlay.CloseLayoutWindow();
|
||||
App.Current.Shutdown();
|
||||
}
|
||||
|
||||
private void OnInitialized(object sender, EventArgs e)
|
||||
private void DeleteLayout_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SetSelectedItem();
|
||||
EditLayoutDialog.Hide();
|
||||
DeleteLayout((FrameworkElement)sender);
|
||||
}
|
||||
|
||||
private void SetSelectedItem()
|
||||
private async void EditLayout_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (LayoutModel model in MainWindowSettingsModel.CustomModels)
|
||||
var dataContext = ((FrameworkElement)sender).DataContext;
|
||||
Select((LayoutModel)dataContext);
|
||||
|
||||
if (_settings.SelectedModel is GridLayoutModel grid)
|
||||
{
|
||||
if (model.IsSelected)
|
||||
{
|
||||
TemplateTab.SelectedItem = model;
|
||||
return;
|
||||
}
|
||||
_backup = new GridLayoutModel(grid);
|
||||
}
|
||||
else if (_settings.SelectedModel is CanvasLayoutModel canvas)
|
||||
{
|
||||
_backup = new CanvasLayoutModel(canvas);
|
||||
}
|
||||
|
||||
await EditLayoutDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private void OnDelete(object sender, RoutedEventArgs e)
|
||||
private void EditZones_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LayoutModel model = ((FrameworkElement)sender).DataContext as LayoutModel;
|
||||
if (model.IsSelected)
|
||||
EditLayoutDialog.Hide();
|
||||
var mainEditor = App.Overlay;
|
||||
if (!(mainEditor.CurrentDataContext is LayoutModel model))
|
||||
{
|
||||
SetSelectedItem();
|
||||
return;
|
||||
}
|
||||
|
||||
model.Delete();
|
||||
_settings.SetSelectedModel(model);
|
||||
|
||||
Hide();
|
||||
mainEditor.OpenEditor(model);
|
||||
}
|
||||
|
||||
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
@@ -239,32 +287,129 @@ namespace FancyZonesEditor
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
private void NewLayoutDialog_PrimaryButtonClick(ModernWpf.Controls.ContentDialog sender, ModernWpf.Controls.ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
this.Close();
|
||||
LayoutModel selectedLayoutModel;
|
||||
|
||||
if (GridLayoutRadioButton.IsChecked == true)
|
||||
{
|
||||
GridLayoutModel gridModel = new GridLayoutModel(LayoutNameText.Text, LayoutType.Custom)
|
||||
{
|
||||
Rows = 1,
|
||||
RowPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
|
||||
};
|
||||
selectedLayoutModel = gridModel;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedLayoutModel = new CanvasLayoutModel(LayoutNameText.Text, LayoutType.Custom)
|
||||
{
|
||||
TemplateZoneCount = 0,
|
||||
};
|
||||
}
|
||||
|
||||
selectedLayoutModel.InitTemplateZones();
|
||||
|
||||
App.Overlay.CurrentDataContext = selectedLayoutModel;
|
||||
var mainEditor = App.Overlay;
|
||||
Hide();
|
||||
mainEditor.OpenEditor(selectedLayoutModel);
|
||||
}
|
||||
|
||||
private void Reset_Click(object sender, RoutedEventArgs e)
|
||||
// This is required to fix a WPF rendering bug when using custom chrome
|
||||
private void OnContentRendered(object sender, EventArgs e)
|
||||
{
|
||||
var overlay = App.Overlay;
|
||||
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
if (overlay.CurrentDataContext is LayoutModel model)
|
||||
private void MonitorItem_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Return || e.Key == Key.Space)
|
||||
{
|
||||
model.IsSelected = false;
|
||||
model.IsApplied = false;
|
||||
monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void MonitorItem_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext);
|
||||
}
|
||||
|
||||
// EditLayout: Cancel changes
|
||||
private void EditLayoutDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
// restore model properties from settings
|
||||
_settings.RestoreSelectedModel(_backup);
|
||||
_backup = null;
|
||||
|
||||
Select(_settings.AppliedModel);
|
||||
}
|
||||
|
||||
// EditLayout: Save changes
|
||||
private void EditLayoutDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
var mainEditor = App.Overlay;
|
||||
if (!(mainEditor.CurrentDataContext is LayoutModel model))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
overlay.CurrentLayoutSettings.ZonesetUuid = settings.BlankModel.Uuid;
|
||||
overlay.CurrentLayoutSettings.Type = LayoutType.Blank;
|
||||
overlay.CurrentDataContext = settings.BlankModel;
|
||||
_backup = null;
|
||||
|
||||
App.FancyZonesEditorIO.SerializeAppliedLayouts();
|
||||
|
||||
if (!overlay.MultiMonitorMode)
|
||||
// update current settings
|
||||
if (model == _settings.AppliedModel)
|
||||
{
|
||||
Close();
|
||||
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
|
||||
}
|
||||
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
|
||||
// reset selected model
|
||||
Select(_settings.AppliedModel);
|
||||
}
|
||||
|
||||
private async void DeleteLayout(FrameworkElement element)
|
||||
{
|
||||
var dialog = new ModernWpf.Controls.ContentDialog()
|
||||
{
|
||||
Title = FancyZonesEditor.Properties.Resources.Are_You_Sure,
|
||||
Content = FancyZonesEditor.Properties.Resources.Are_You_Sure_Description,
|
||||
PrimaryButtonText = FancyZonesEditor.Properties.Resources.Delete,
|
||||
SecondaryButtonText = FancyZonesEditor.Properties.Resources.Cancel,
|
||||
};
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
LayoutModel model = element.DataContext as LayoutModel;
|
||||
|
||||
if (model == _settings.AppliedModel)
|
||||
{
|
||||
_settings.SetAppliedModel(_settings.BlankModel);
|
||||
Select(_settings.BlankModel);
|
||||
}
|
||||
|
||||
foreach (var monitor in App.Overlay.Monitors)
|
||||
{
|
||||
if (monitor.Settings.ZonesetUuid == model.Uuid)
|
||||
{
|
||||
App.Overlay.SetLayoutSettings(monitor, _settings.BlankModel);
|
||||
}
|
||||
}
|
||||
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
model.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void Dialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args)
|
||||
{
|
||||
_openedDialog = sender;
|
||||
}
|
||||
|
||||
private void Dialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs args)
|
||||
{
|
||||
_openedDialog = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user