The menu does show up, it's just totally in the wrong spot

This commit is contained in:
Mike Griese
2026-02-09 10:33:18 -06:00
parent fd2df1c7e1
commit 8044d758fb
6 changed files with 174 additions and 63 deletions

View File

@@ -476,6 +476,7 @@ public partial class ShellViewModel : ObservableObject,
navigationToken,
TaskContinuationOptions.None,
_scheduler);
return;
}
// -------------------------------------------------------------

View File

@@ -178,6 +178,12 @@ public sealed partial class DockControl : UserControl, IRecipient<CloseContextMe
borderPos.X + (dockItem.ActualWidth / 2),
borderPos.Y + (dockItem.ActualHeight / 2));
// // borderCenter is in DIPs, relative to the dock window.
// // we need screen DIPs
// var windowPos = dockItem.XamlRoot.Content.XamlRoot.TransformToVisual(null).TransformPoint(new Point(0, 0));
// var screenPos = new Point(
// borderCenter.X + windowPos.X,
// borderCenter.Y + windowPos.Y);
InvokeItem(item, borderCenter);
e.Handled = true;
}

View File

@@ -20,6 +20,30 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.Resources>
<Style
x:Name="ContextMenuFlyoutStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Style.Setters>
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="{ThemeResource DesktopAcrylicTransparentBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource DividerStrokeColorDefaultBrush}" />
</Style.Setters>
</Style>
<!-- Backdrop requires ShouldConstrainToRootBounds="False" -->
<Flyout
x:Name="ContextMenuFlyout"
FlyoutPresenterStyle="{StaticResource ContextMenuFlyoutStyle}"
Opened="ContextMenuFlyout_Opened"
ShouldConstrainToRootBounds="False"
SystemBackdrop="{ThemeResource AcrylicBackgroundFillColorDefaultBackdrop}">
<cpcontrols:ContextMenu x:Name="ContextMenuControl" MaxHeight="512" />
</Flyout>
</Grid.Resources>
<!-- Colorization overlay for transparent backdrop -->
<Border
x:Name="ColorizationOverlay"

View File

@@ -5,6 +5,7 @@
using System.Runtime.InteropServices;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Dock;
using Microsoft.CmdPal.UI.ViewModels.Messages;
@@ -16,6 +17,7 @@ using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls.Primitives;
using Windows.Foundation;
using Windows.Win32;
using Windows.Win32.Foundation;
@@ -35,6 +37,8 @@ public sealed partial class DockWindow : WindowEx,
IRecipient<BringToTopMessage>,
IRecipient<RequestShowPaletteAtMessage>,
IRecipient<QuitMessage>,
IRecipient<ShowCommandInContextMenuMessage>,
IRecipient<CloseContextMenuMessage>,
IDisposable
{
#pragma warning disable SA1306 // Field names should begin with lower-case letter
@@ -93,6 +97,9 @@ public sealed partial class DockWindow : WindowEx,
WeakReferenceMessenger.Default.Register<RequestShowPaletteAtMessage>(this);
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
WeakReferenceMessenger.Default.Register<ShowCommandInContextMenuMessage>(this);
WeakReferenceMessenger.Default.Register<CloseContextMenuMessage>(this);
_hwnd = GetWindowHandle(this);
// Subclass the window to intercept messages
@@ -529,6 +536,11 @@ public sealed partial class DockWindow : WindowEx,
return false;
}
private void ContextMenuFlyout_Opened(object sender, object e)
{
ContextMenuControl.FocusSearchBox();
}
public void Receive(QuitMessage message)
{
DispatcherQueue.TryEnqueue(() =>
@@ -539,6 +551,25 @@ public sealed partial class DockWindow : WindowEx,
});
}
public void Receive(ShowCommandInContextMenuMessage message)
{
DispatcherQueue.TryEnqueue(() =>
{
ShowFlyoutOnUiThread(message.Context, message.Position);
});
}
public void Receive(CloseContextMenuMessage message)
{
DispatcherQueue.TryEnqueue(() =>
{
if (ContextMenuFlyout.IsOpen)
{
ContextMenuFlyout.Hide();
}
});
}
void IRecipient<RequestShowPaletteAtMessage>.Receive(RequestShowPaletteAtMessage message)
{
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => RequestShowPaletteOnUiThread(message.PosDips));
@@ -546,6 +577,10 @@ public sealed partial class DockWindow : WindowEx,
private void RequestShowPaletteOnUiThread(Point posDips)
{
// TODO!!!! posDips was originally relative to the dock window.
// It's now relative to the screen.
// this method wasn't yet updated for that.
// pos is relative to our root. We need to convert to screen coords.
var rootPosDips = Root.TransformToVisual(null).TransformPoint(new Point(0, 0));
var screenPosDips = new Point(rootPosDips.X + posDips.X, rootPosDips.Y + posDips.Y);
@@ -613,6 +648,93 @@ public sealed partial class DockWindow : WindowEx,
WeakReferenceMessenger.Default.Send<ShowPaletteAtMessage>(new(screenPosPixels, anchorPoint));
}
private void ShowFlyoutOnUiThread(IContextMenuContext context, Point position)
{
ContextMenuControl.ViewModel.SelectedItem = context;
// Depending on the side we're on, change the direction of the results
switch (_settings.Side)
{
case DockSide.Top:
case DockSide.Left:
case DockSide.Right:
ContextMenuControl.ViewModel.FilterOnTop = true;
break;
case DockSide.Bottom:
ContextMenuControl.ViewModel.FilterOnTop = false;
break;
}
// position is relative to our root. We need to convert to screen coords.
//
// Root.TransformToVisual(null) will always return 0,0, because Root is at the root of our window.
//
// we instead need:
// var rootPosDips = Root.TransformToVisual(null).TransformPoint(new Point(0, 0));
// var screenPosDips = new Point(rootPosDips.X + position.X, rootPosDips.Y + position.Y);
// TODO! deal with DPI - I'm sure this is wrong
var dpi = PInvoke.GetDpiForWindow(_hwnd);
PInvoke.GetWindowRect(_hwnd, out var ourRect);
var scaleFactor = dpi / 96.0;
var positionPixels = new Point(position.X * scaleFactor, position.Y * scaleFactor);
var screenPosPixels = // new Point(screenPosDips.X * scaleFactor, screenPosDips.Y * scaleFactor);
new Point(ourRect.left + positionPixels.X, ourRect.top + positionPixels.Y);
var screenWidth = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSCREEN);
var screenHeight = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSCREEN);
var onTopHalf = screenPosPixels.Y < screenHeight / 2;
var onLeftHalf = screenPosPixels.X < screenWidth / 2;
var onRightHalf = !onLeftHalf;
var onBottomHalf = !onTopHalf;
var anchorPoint = _settings.Side switch
{
DockSide.Top => onLeftHalf ? AnchorPoint.TopLeft : AnchorPoint.TopRight,
DockSide.Bottom => onLeftHalf ? AnchorPoint.BottomLeft : AnchorPoint.BottomRight,
DockSide.Left => onTopHalf ? AnchorPoint.TopLeft : AnchorPoint.BottomLeft,
DockSide.Right => onTopHalf ? AnchorPoint.TopRight : AnchorPoint.BottomRight,
_ => AnchorPoint.TopLeft,
};
// we also need to slide the anchor point a bit away from the dock
var paddingDips = 8;
var paddingPixels = paddingDips * scaleFactor;
// Depending on the side we're on, we need to offset differently
switch (_settings.Side)
{
case DockSide.Top:
screenPosPixels.Y += paddingPixels;
break;
case DockSide.Bottom:
screenPosPixels.Y -= paddingPixels;
break;
case DockSide.Left:
screenPosPixels.X += paddingPixels;
break;
case DockSide.Right:
screenPosPixels.X -= paddingPixels;
break;
}
// var finalPosDips = new Point((screenPosPixels.X / scaleFactor) - rootPosDips.X, (screenPosPixels.Y / scaleFactor) - rootPosDips.Y);
var finalPosDips = new Point(
(screenPosPixels.X - ourRect.left) / scaleFactor,
(screenPosPixels.Y - ourRect.top) / scaleFactor);
ContextMenuFlyout.ShowAt(
Root,
new FlyoutShowOptions()
{
ShowMode = FlyoutShowMode.Standard,
Position = finalPosDips,
});
}
public DockWindowViewModel WindowViewModel => _windowViewModel;
public void Dispose()

View File

@@ -16,29 +16,6 @@
Closed="MainWindow_Closed"
mc:Ignorable="d">
<Grid x:Name="RootElement">
<Grid.Resources>
<Style
x:Name="ContextMenuFlyoutStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Style.Setters>
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="{ThemeResource DesktopAcrylicTransparentBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource DividerStrokeColorDefaultBrush}" />
</Style.Setters>
</Style>
<!-- Backdrop requires ShouldConstrainToRootBounds="False" -->
<Flyout
x:Name="ContextMenuFlyout"
FlyoutPresenterStyle="{StaticResource ContextMenuFlyoutStyle}"
Opened="ContextMenuFlyout_Opened"
ShouldConstrainToRootBounds="False"
SystemBackdrop="{ThemeResource AcrylicBackgroundFillColorDefaultBackdrop}">
<controls:ContextMenu x:Name="ContextMenuControl" />
</Flyout>
</Grid.Resources>
<controls:BlurImageControl
HorizontalAlignment="Stretch"

View File

@@ -28,7 +28,6 @@ using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Input;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.Windows.AppLifecycle;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
@@ -58,8 +57,6 @@ public sealed partial class MainWindow : WindowEx,
IRecipient<NavigationDepthMessage>,
IRecipient<SearchQueryMessage>,
IRecipient<ErrorOccurredMessage>,
IRecipient<ShowCommandInContextMenuMessage>,
IRecipient<CloseContextMenuMessage>,
IRecipient<DragStartedMessage>,
IRecipient<DragCompletedMessage>,
IDisposable
@@ -144,8 +141,6 @@ public sealed partial class MainWindow : WindowEx,
WeakReferenceMessenger.Default.Register<NavigationDepthMessage>(this);
WeakReferenceMessenger.Default.Register<SearchQueryMessage>(this);
WeakReferenceMessenger.Default.Register<ErrorOccurredMessage>(this);
WeakReferenceMessenger.Default.Register<ShowCommandInContextMenuMessage>(this);
WeakReferenceMessenger.Default.Register<CloseContextMenuMessage>(this);
WeakReferenceMessenger.Default.Register<DragStartedMessage>(this);
WeakReferenceMessenger.Default.Register<DragCompletedMessage>(this);
@@ -680,41 +675,6 @@ public sealed partial class MainWindow : WindowEx,
_sessionErrorCount++;
}
public void Receive(ShowCommandInContextMenuMessage message)
{
DispatcherQueue.TryEnqueue(() =>
{
ContextMenuControl.ViewModel.SelectedItem = message.Context;
ContextMenuFlyout.ShouldConstrainToRootBounds = false;
ContextMenuFlyout.ShowMode = FlyoutShowMode.Standard;
ContextMenuFlyout.ShowAt(RootElement);
// ContextMenuFlyout.ShowAt(
// RootElement,
// new FlyoutShowOptions()
// {
// ShowMode = FlyoutShowMode.Standard,
// Position = message.Position,
// });
});
}
public void Receive(CloseContextMenuMessage message)
{
DispatcherQueue.TryEnqueue(() =>
{
if (ContextMenuFlyout.IsOpen)
{
ContextMenuFlyout.Hide();
}
});
}
private void ContextMenuFlyout_Opened(object sender, object e)
{
ContextMenuControl.FocusSearchBox();
}
/// <summary>
/// Ends the current telemetry session and emits the CmdPal_SessionDuration event.
/// Aggregates all session metrics collected since ShowWindow and sends them to telemetry.
@@ -1251,6 +1211,27 @@ public sealed partial class MainWindow : WindowEx,
});
}
// public void Receive(ShowCommandInContextMenuMessage message)
// {
// DispatcherQueue.TryEnqueue(() =>
// {
// ContextMenuControl.ViewModel.SelectedItem = message.Context;
// ContextMenuFlyout.ShouldConstrainToRootBounds = false;
// ContextMenuFlyout.ShowMode = FlyoutShowMode.Standard;
// // message.Position is in dips,
// ContextMenuFlyout.ShowAt(RootElement);
// // ContextMenuFlyout.ShowAt(
// // RootElement,
// // new FlyoutShowOptions()
// // {
// // ShowMode = FlyoutShowMode.Standard,
// // Position = message.Position,
// // });
// });
// }
private unsafe void StealForeground()
{
var foregroundWindow = PInvoke.GetForegroundWindow();