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
This commit is contained in:
Mike Griese
2026-03-01 06:03:40 -06:00
committed by GitHub
parent 90e81cbfd5
commit 543399b62b
3 changed files with 138 additions and 54 deletions

View File

@@ -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);