From 543399b62b38472600ee6cbdbe48f3bb93eaae9c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Sun, 1 Mar 2026 06:03:40 -0600 Subject: [PATCH] dock: shift around the padding for fitts law (#45834) This makes the buttons hitbox extend all the way to the edges of the dock, but the visual presentation of these buttons is unchanged. This lets us adhere to fitts law appropriately. Closes #45596 Closes #45590 --- .../Microsoft.CmdPal.UI/Dock/DockControl.xaml | 12 +- .../Dock/DockItemControl.xaml | 107 +++++++++--------- .../Dock/DockItemControl.xaml.cs | 73 ++++++++++++ 3 files changed, 138 insertions(+), 54 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml index 1987bb0675..b8acc4a182 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml @@ -182,7 +182,7 @@ @@ -334,12 +334,16 @@ + + + + @@ -373,7 +377,8 @@ - + + @@ -411,7 +416,8 @@ - + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml index 2247fae6ef..4dcd630779 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml @@ -58,62 +58,67 @@ - - - - + x:Name="PART_RootGrid" + Margin="{TemplateBinding InnerMargin}" + Padding="{TemplateBinding Padding}" + VerticalAlignment="Stretch" + Background="{TemplateBinding Background}" + BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="{TemplateBinding CornerRadius}"> + + + + + - - + + - - - + - - + Visibility="{TemplateBinding TextVisibility}"> + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml.cs index 0c95230f74..527b2bd834 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockItemControl.xaml.cs @@ -5,10 +5,12 @@ using Microsoft.CmdPal.UI.Controls; using Microsoft.CmdPal.UI.ViewModels; using Microsoft.CmdPal.UI.ViewModels.Dock; +using Microsoft.CmdPal.UI.ViewModels.Settings; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Markup; +using Microsoft.UI.Xaml.Media; namespace Microsoft.CmdPal.UI.Dock; @@ -56,6 +58,15 @@ public sealed partial class DockItemControl : Control set => SetValue(IconProperty, value); } + public static readonly DependencyProperty InnerMarginProperty = + DependencyProperty.Register(nameof(InnerMargin), typeof(Thickness), typeof(DockItemControl), new PropertyMetadata(new Thickness(0))); + + public Thickness InnerMargin + { + get => (Thickness)GetValue(InnerMarginProperty); + set => SetValue(InnerMarginProperty, value); + } + public static readonly DependencyProperty TextVisibilityProperty = DependencyProperty.Register(nameof(TextVisibility), typeof(Visibility), typeof(DockItemControl), new PropertyMetadata(null, OnTextPropertyChanged)); @@ -68,6 +79,8 @@ public sealed partial class DockItemControl : Control private const string IconPresenterName = "IconPresenter"; private FrameworkElement? _iconPresenter; + private DockControl? _parentDock; + private long _dockSideCallbackToken = -1; private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { @@ -174,9 +187,13 @@ public sealed partial class DockItemControl : Control PointerEntered -= Control_PointerEntered; PointerExited -= Control_PointerExited; + Loaded -= DockItemControl_Loaded; + Unloaded -= DockItemControl_Unloaded; PointerEntered += Control_PointerEntered; PointerExited += Control_PointerExited; + Loaded += DockItemControl_Loaded; + Unloaded += DockItemControl_Unloaded; IsEnabledChanged += OnIsEnabledChanged; @@ -187,6 +204,62 @@ public sealed partial class DockItemControl : Control UpdateAllVisibility(); } + private void DockItemControl_Loaded(object sender, RoutedEventArgs e) + { + // Walk the visual tree to find our parent DockControl and watch its DockSide. + // This lets us extend the hit-test area toward the screen edge. + DependencyObject? parent = VisualTreeHelper.GetParent(this); + while (parent is not null and not DockControl) + { + parent = VisualTreeHelper.GetParent(parent); + } + + if (parent is DockControl dock) + { + _parentDock = dock; + UpdateInnerMarginForDockSide(dock.DockSide); + _dockSideCallbackToken = dock.RegisterPropertyChangedCallback( + DockControl.DockSideProperty, + OnParentDockSideChanged); + } + } + + private void DockItemControl_Unloaded(object sender, RoutedEventArgs e) + { + if (_parentDock is not null && _dockSideCallbackToken >= 0) + { + _parentDock.UnregisterPropertyChangedCallback( + DockControl.DockSideProperty, + _dockSideCallbackToken); + _dockSideCallbackToken = -1; + _parentDock = null; + } + } + + private void OnParentDockSideChanged(DependencyObject sender, DependencyProperty dp) + { + if (sender is DockControl dock) + { + UpdateInnerMarginForDockSide(dock.DockSide); + } + } + + private void UpdateInnerMarginForDockSide(DockSide side) + { + // Push the visual (PART_RootGrid) inward on the screen-edge side so + // the transparent hit-test area extends all the way to the edge. + // The values here compensate for the margin/padding removed from the + // DockControl's ContentGrid on the screen-edge side. + InnerMargin = side switch + { + DockSide.Top => new Thickness(0, 4, 0, 0), + DockSide.Bottom => new Thickness(0, 0, 0, 4), + DockSide.Left => new Thickness(8, 0, 0, 0), + DockSide.Right => new Thickness(0, 0, 8, 0), + _ => new Thickness(0), + }; + } + private void Control_PointerEntered(object sender, PointerRoutedEventArgs e) { VisualStateManager.GoToState(this, "PointerOver", true);