mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
CmdPal: Add a dock (#45824)
Add support for a "dock" window in CmdPal. The dock is a toolbar powered by the `APPBAR` APIs. This gives you a persistent region to display commands for quick shortcuts or glanceable widgets. The dock can be pinned to any side of the screen. The dock can be independently styled with any of the theming controls cmdpal already has The dock has three "regions" to pin to - the "start", the "center", and the "end". Elements on the dock are grouped as "bands", which contains a set of "items". Each "band" is one atomic unit. For example, the Media Player extension produces 4 items, but one _band_. The dock has only one size (for now) The dock will only appear on your primary display (for now) This PR includes support for pinning arbitrary top-level commands to the dock - however, we're planning on replacing that with a more universal ability to pin any command to the dock or top level. (see #45191). This is at least usable for now. This is definitely still _even more preview_ than usual PowerToys features, but it's more than usable. I'd love to get it out there and start collecting feedback on where to improve next. I'll probably add a follow-up issue for tracking the remaining bugs & nits. closes #45201 --------- Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
@@ -11,6 +11,7 @@ using Microsoft.CmdPal.Common.Helpers;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.Ext.ClipboardHistory.Messages;
|
||||
using Microsoft.CmdPal.UI.Controls;
|
||||
using Microsoft.CmdPal.UI.Dock;
|
||||
using Microsoft.CmdPal.UI.Events;
|
||||
using Microsoft.CmdPal.UI.Helpers;
|
||||
using Microsoft.CmdPal.UI.Messages;
|
||||
@@ -18,6 +19,7 @@ using Microsoft.CmdPal.UI.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Services;
|
||||
using Microsoft.CmdPal.ViewModels.Messages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Composition;
|
||||
@@ -45,6 +47,7 @@ namespace Microsoft.CmdPal.UI;
|
||||
public sealed partial class MainWindow : WindowEx,
|
||||
IRecipient<DismissMessage>,
|
||||
IRecipient<ShowWindowMessage>,
|
||||
IRecipient<ShowPaletteAtMessage>,
|
||||
IRecipient<HideWindowMessage>,
|
||||
IRecipient<QuitMessage>,
|
||||
IRecipient<NavigateToPageMessage>,
|
||||
@@ -145,6 +148,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
WeakReferenceMessenger.Default.Register<DismissMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<ShowWindowMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<ShowPaletteAtMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<HideWindowMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<NavigateToPageMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<NavigationDepthMessage>(this);
|
||||
@@ -206,7 +210,10 @@ public sealed partial class MainWindow : WindowEx,
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsChangedHandler(SettingsModel sender, object? args) => HotReloadSettings();
|
||||
private void SettingsChangedHandler(SettingsModel sender, object? args)
|
||||
{
|
||||
DispatcherQueue.TryEnqueue(HotReloadSettings);
|
||||
}
|
||||
|
||||
private void RootElementLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
@@ -496,6 +503,78 @@ public sealed partial class MainWindow : WindowEx,
|
||||
}
|
||||
|
||||
private void ShowHwnd(IntPtr hwndValue, MonitorBehavior target)
|
||||
{
|
||||
var positionWindowForTargetMonitor = (HWND hwnd) =>
|
||||
{
|
||||
if (target == MonitorBehavior.ToLast)
|
||||
{
|
||||
var originalScreen = new SizeInt32(_currentWindowPosition.ScreenWidth, _currentWindowPosition.ScreenHeight);
|
||||
var newRect = WindowPositionHelper.AdjustRectForVisibility(_currentWindowPosition.ToPhysicalWindowRectangle(), originalScreen, _currentWindowPosition.Dpi);
|
||||
MoveAndResizeDpiAware(newRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
var display = GetScreen(hwnd, target);
|
||||
PositionCentered(display);
|
||||
}
|
||||
};
|
||||
ShowHwnd(hwndValue, positionWindowForTargetMonitor);
|
||||
}
|
||||
|
||||
private void ShowHwnd(IntPtr hwndValue, Point anchorInPixels, AnchorPoint anchorCorner)
|
||||
{
|
||||
var positionWindowForAnchor = (HWND hwnd) =>
|
||||
{
|
||||
PInvoke.GetWindowRect(hwnd, out var bounds);
|
||||
var swpFlags = SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER;
|
||||
switch (anchorCorner)
|
||||
{
|
||||
case AnchorPoint.TopLeft:
|
||||
PInvoke.SetWindowPos(
|
||||
hwnd,
|
||||
HWND.HWND_TOP,
|
||||
(int)anchorInPixels.X,
|
||||
(int)anchorInPixels.Y,
|
||||
0,
|
||||
0,
|
||||
swpFlags);
|
||||
break;
|
||||
case AnchorPoint.TopRight:
|
||||
PInvoke.SetWindowPos(
|
||||
hwnd,
|
||||
HWND.HWND_TOP,
|
||||
(int)(anchorInPixels.X - bounds.Width),
|
||||
(int)anchorInPixels.Y,
|
||||
0,
|
||||
0,
|
||||
swpFlags);
|
||||
break;
|
||||
case AnchorPoint.BottomLeft:
|
||||
PInvoke.SetWindowPos(
|
||||
hwnd,
|
||||
HWND.HWND_TOP,
|
||||
(int)anchorInPixels.X,
|
||||
(int)(anchorInPixels.Y - bounds.Height),
|
||||
0,
|
||||
0,
|
||||
swpFlags);
|
||||
break;
|
||||
case AnchorPoint.BottomRight:
|
||||
PInvoke.SetWindowPos(
|
||||
hwnd,
|
||||
HWND.HWND_TOP,
|
||||
(int)(anchorInPixels.X - bounds.Width),
|
||||
(int)(anchorInPixels.Y - bounds.Height),
|
||||
0,
|
||||
0,
|
||||
swpFlags);
|
||||
break;
|
||||
}
|
||||
};
|
||||
ShowHwnd(hwndValue, positionWindowForAnchor);
|
||||
}
|
||||
|
||||
private void ShowHwnd(IntPtr hwndValue, Action<HWND>? positionWindow)
|
||||
{
|
||||
StopAutoGoHome();
|
||||
|
||||
@@ -514,16 +593,9 @@ public sealed partial class MainWindow : WindowEx,
|
||||
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
|
||||
}
|
||||
|
||||
if (target == MonitorBehavior.ToLast)
|
||||
if (positionWindow is not null)
|
||||
{
|
||||
var originalScreen = new SizeInt32(_currentWindowPosition.ScreenWidth, _currentWindowPosition.ScreenHeight);
|
||||
var newRect = WindowPositionHelper.AdjustRectForVisibility(_currentWindowPosition.ToPhysicalWindowRectangle(), originalScreen, _currentWindowPosition.Dpi);
|
||||
MoveAndResizeDpiAware(newRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
var display = GetScreen(hwnd, target);
|
||||
PositionCentered(display);
|
||||
positionWindow(hwnd);
|
||||
}
|
||||
|
||||
// Check if the debugger is attached. If it is, we don't want to apply the tool window style,
|
||||
@@ -605,6 +677,11 @@ public sealed partial class MainWindow : WindowEx,
|
||||
ShowHwnd(message.Hwnd, settings.SummonOn);
|
||||
}
|
||||
|
||||
internal void Receive(ShowPaletteAtMessage message)
|
||||
{
|
||||
ShowHwnd(HWND.Null, message.PosPixels, message.Anchor);
|
||||
}
|
||||
|
||||
public void Receive(HideWindowMessage message)
|
||||
{
|
||||
// This might come in off the UI thread. Make sure to hop back.
|
||||
@@ -715,6 +792,8 @@ public sealed partial class MainWindow : WindowEx,
|
||||
// Sure, it's not ideal, but at least it's not visible.
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new WindowHiddenMessage());
|
||||
|
||||
// Start auto-go-home timer
|
||||
RestartAutoGoHome();
|
||||
}
|
||||
@@ -1121,6 +1200,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
// but that's the price to pay for having the HWND not light-dismiss while we're debugging.
|
||||
Cloak();
|
||||
this.Hide();
|
||||
WeakReferenceMessenger.Default.Send(new WindowHiddenMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1178,6 +1258,8 @@ public sealed partial class MainWindow : WindowEx,
|
||||
DisposeAcrylic();
|
||||
}
|
||||
|
||||
void IRecipient<ShowPaletteAtMessage>.Receive(ShowPaletteAtMessage message) => Receive(message);
|
||||
|
||||
public void Receive(ToggleDevRibbonMessage message)
|
||||
{
|
||||
_devRibbon?.Visibility = _devRibbon.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
Reference in New Issue
Block a user