diff --git a/NOTICE.md b/NOTICE.md index 4dcc82579d..d75fe99522 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1519,23 +1519,23 @@ SOFTWARE. - Mages 3.0.0 - Markdig.Signed 0.34.0 - MessagePack 3.1.3 -- Microsoft.Bcl.AsyncInterfaces 9.0.7 +- Microsoft.Bcl.AsyncInterfaces 9.0.8 - Microsoft.Bot.AdaptiveExpressions.Core 4.23.0 - Microsoft.CodeAnalysis.NetAnalyzers 9.0.0 -- Microsoft.Data.Sqlite 9.0.7 +- Microsoft.Data.Sqlite 9.0.8 - Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16 - Microsoft.DotNet.ILCompiler (A) -- Microsoft.Extensions.DependencyInjection 9.0.7 -- Microsoft.Extensions.Hosting 9.0.7 -- Microsoft.Extensions.Hosting.WindowsServices 9.0.7 -- Microsoft.Extensions.Logging 9.0.7 -- Microsoft.Extensions.Logging.Abstractions 9.0.7 +- Microsoft.Extensions.DependencyInjection 9.0.8 +- Microsoft.Extensions.Hosting 9.0.8 +- Microsoft.Extensions.Hosting.WindowsServices 9.0.8 +- Microsoft.Extensions.Logging 9.0.8 +- Microsoft.Extensions.Logging.Abstractions 9.0.8 - Microsoft.NET.ILLink.Tasks (A) - Microsoft.SemanticKernel 1.15.0 - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.2903.40 -- Microsoft.Win32.SystemEvents 9.0.7 -- Microsoft.Windows.Compatibility 9.0.7 +- Microsoft.Win32.SystemEvents 9.0.8 +- Microsoft.Windows.Compatibility 9.0.8 - Microsoft.Windows.CsWin32 0.3.183 - Microsoft.Windows.CsWinRT 2.2.0 - Microsoft.Windows.SDK.BuildTools 10.0.26100.4188 @@ -1555,25 +1555,25 @@ SOFTWARE. - SkiaSharp.Views.WinUI 2.88.9 - StreamJsonRpc 2.21.69 - StyleCop.Analyzers 1.2.0-beta.556 -- System.CodeDom 9.0.7 +- System.CodeDom 9.0.8 - System.CommandLine 2.0.0-beta4.22272.1 -- System.ComponentModel.Composition 9.0.7 -- System.Configuration.ConfigurationManager 9.0.7 -- System.Data.OleDb 9.0.7 +- System.ComponentModel.Composition 9.0.8 +- System.Configuration.ConfigurationManager 9.0.8 +- System.Data.OleDb 9.0.8 - System.Data.SqlClient 4.9.0 -- System.Diagnostics.EventLog 9.0.7 -- System.Diagnostics.PerformanceCounter 9.0.7 -- System.Drawing.Common 9.0.7 +- System.Diagnostics.EventLog 9.0.8 +- System.Diagnostics.PerformanceCounter 9.0.8 +- System.Drawing.Common 9.0.8 - System.IO.Abstractions 22.0.13 - System.IO.Abstractions.TestingHelpers 22.0.13 -- System.Management 9.0.7 +- System.Management 9.0.8 - System.Net.Http 4.3.4 - System.Private.Uri 4.3.2 - System.Reactive 6.0.1 -- System.Runtime.Caching 9.0.7 -- System.ServiceProcess.ServiceController 9.0.7 -- System.Text.Encoding.CodePages 9.0.7 -- System.Text.Json 9.0.7 +- System.Runtime.Caching 9.0.8 +- System.ServiceProcess.ServiceController 9.0.8 +- System.Text.Encoding.CodePages 9.0.8 +- System.Text.Json 9.0.8 - System.Text.RegularExpressions 4.3.1 - UnicodeInformation 2.6.0 - UnitsNet 5.56.0 diff --git a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp index 25a95f4d39..05670742ec 100644 --- a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp +++ b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp @@ -5,6 +5,7 @@ #include "MouseHighlighter.h" #include "trace.h" #include +#include #ifdef COMPOSITION namespace winrt @@ -49,6 +50,9 @@ private: void BringToFront(); HHOOK m_mouseHook = NULL; static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept; + // Helpers for spotlight overlay + float GetDpiScale() const; + void UpdateSpotlightMask(float cx, float cy, float radius, bool show); static constexpr auto m_className = L"MouseHighlighter"; static constexpr auto m_windowTitle = L"PowerToys Mouse Highlighter"; @@ -67,7 +71,14 @@ private: winrt::CompositionSpriteShape m_leftPointer{ nullptr }; winrt::CompositionSpriteShape m_rightPointer{ nullptr }; winrt::CompositionSpriteShape m_alwaysPointer{ nullptr }; - winrt::CompositionSpriteShape m_spotlightPointer{ nullptr }; + // Spotlight overlay (mask with soft feathered edge) + winrt::SpriteVisual m_overlay{ nullptr }; + winrt::CompositionMaskBrush m_spotlightMask{ nullptr }; + winrt::CompositionRadialGradientBrush m_spotlightMaskGradient{ nullptr }; + winrt::CompositionColorBrush m_spotlightSource{ nullptr }; + winrt::CompositionColorGradientStop m_maskStopCenter{ nullptr }; + winrt::CompositionColorGradientStop m_maskStopInner{ nullptr }; + winrt::CompositionColorGradientStop m_maskStopOuter{ nullptr }; bool m_leftPointerEnabled = true; bool m_rightPointerEnabled = true; @@ -123,6 +134,35 @@ bool Highlighter::CreateHighlighter() m_shape.RelativeSizeAdjustment({ 1.0f, 1.0f }); m_root.Children().InsertAtTop(m_shape); + // Create spotlight overlay (soft feather, DPI-aware) + m_overlay = m_compositor.CreateSpriteVisual(); + m_overlay.RelativeSizeAdjustment({ 1.0f, 1.0f }); + m_spotlightSource = m_compositor.CreateColorBrush(m_alwaysColor); + m_spotlightMaskGradient = m_compositor.CreateRadialGradientBrush(); + m_spotlightMaskGradient.MappingMode(winrt::CompositionMappingMode::Absolute); + // Center region fully transparent + m_maskStopCenter = m_compositor.CreateColorGradientStop(); + m_maskStopCenter.Offset(0.0f); + m_maskStopCenter.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0)); + // Inner edge of feather (still transparent) + m_maskStopInner = m_compositor.CreateColorGradientStop(); + m_maskStopInner.Offset(0.995f); // will be updated per-radius + m_maskStopInner.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0)); + // Outer edge (opaque mask -> overlay visible) + m_maskStopOuter = m_compositor.CreateColorGradientStop(); + m_maskStopOuter.Offset(1.0f); + m_maskStopOuter.Color(winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255)); + m_spotlightMaskGradient.ColorStops().Append(m_maskStopCenter); + m_spotlightMaskGradient.ColorStops().Append(m_maskStopInner); + m_spotlightMaskGradient.ColorStops().Append(m_maskStopOuter); + + m_spotlightMask = m_compositor.CreateMaskBrush(); + m_spotlightMask.Source(m_spotlightSource); + m_spotlightMask.Mask(m_spotlightMaskGradient); + m_overlay.Brush(m_spotlightMask); + m_overlay.IsVisible(false); + m_root.Children().InsertAtTop(m_overlay); + return true; } catch (...) @@ -165,12 +205,8 @@ void Highlighter::AddDrawingPoint(MouseButton button) // always if (m_spotlightMode) { - float borderThickness = static_cast(std::hypot(GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN))); - circleGeometry.Radius({ static_cast(borderThickness / 2.0 + m_radius), static_cast(borderThickness / 2.0 + m_radius) }); - circleShape.FillBrush(nullptr); - circleShape.StrokeBrush(m_compositor.CreateColorBrush(m_alwaysColor)); - circleShape.StrokeThickness(borderThickness); - m_spotlightPointer = circleShape; + UpdateSpotlightMask(static_cast(pt.x), static_cast(pt.y), m_radius, true); + return; } else { @@ -209,20 +245,14 @@ void Highlighter::UpdateDrawingPointPosition(MouseButton button) } else { - // always + // always / spotlight idle if (m_spotlightMode) { - if (m_spotlightPointer) - { - m_spotlightPointer.Offset({ static_cast(pt.x), static_cast(pt.y) }); - } + UpdateSpotlightMask(static_cast(pt.x), static_cast(pt.y), m_radius, true); } - else + else if (m_alwaysPointer) { - if (m_alwaysPointer) - { - m_alwaysPointer.Offset({ static_cast(pt.x), static_cast(pt.y) }); - } + m_alwaysPointer.Offset({ static_cast(pt.x), static_cast(pt.y) }); } } } @@ -266,9 +296,9 @@ void Highlighter::ClearDrawingPoint() { if (m_spotlightMode) { - if (m_spotlightPointer) + if (m_overlay) { - m_spotlightPointer.StrokeBrush().as().Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0)); + m_overlay.IsVisible(false); } } else @@ -421,7 +451,10 @@ void Highlighter::StopDrawing() m_leftPointer = nullptr; m_rightPointer = nullptr; m_alwaysPointer = nullptr; - m_spotlightPointer = nullptr; + if (m_overlay) + { + m_overlay.IsVisible(false); + } ShowWindow(m_hwnd, SW_HIDE); UnhookWindowsHookEx(m_mouseHook); ClearDrawing(); @@ -452,6 +485,16 @@ void Highlighter::ApplySettings(MouseHighlighterSettings settings) m_rightPointerEnabled = false; } + // Keep spotlight overlay color updated + if (m_spotlightSource) + { + m_spotlightSource.Color(m_alwaysColor); + } + if (!m_spotlightMode && m_overlay) + { + m_overlay.IsVisible(false); + } + if (instance->m_visible) { instance->StopDrawing(); @@ -563,6 +606,43 @@ void Highlighter::Terminate() } } +float Highlighter::GetDpiScale() const +{ + return static_cast(GetDpiForWindow(m_hwnd)) / 96.0f; +} + +// Update spotlight radial mask center/radius with DPI-aware feather +void Highlighter::UpdateSpotlightMask(float cx, float cy, float radius, bool show) +{ + if (!m_spotlightMaskGradient) + { + return; + } + + m_spotlightMaskGradient.EllipseCenter({ cx, cy }); + m_spotlightMaskGradient.EllipseRadius({ radius, radius }); + + const float dpiScale = GetDpiScale(); + // Target a very fine edge: ~1 physical pixel, convert to DIPs: 1 / dpiScale + const float featherDip = 1.0f / (dpiScale > 0.0f ? dpiScale : 1.0f); + const float safeRadius = (std::max)(radius, 1.0f); + const float featherRel = (std::min)(0.25f, featherDip / safeRadius); + + if (m_maskStopInner) + { + m_maskStopInner.Offset((std::max)(0.0f, 1.0f - featherRel)); + } + + if (m_spotlightSource) + { + m_spotlightSource.Color(m_alwaysColor); + } + if (m_overlay) + { + m_overlay.IsVisible(show); + } +} + #pragma region MouseHighlighter_API void MouseHighlighterApplySettings(MouseHighlighterSettings settings) diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props index 664b2d678a..d364f7da8b 100644 --- a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props +++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props @@ -12,6 +12,6 @@ - + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandContextItemViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandContextItemViewModel.cs index 60fc815a52..f2060efe88 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandContextItemViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandContextItemViewModel.cs @@ -2,11 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using Microsoft.CmdPal.Core.ViewModels.Models; using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; namespace Microsoft.CmdPal.Core.ViewModels; +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public partial class CommandContextItemViewModel(ICommandContextItem contextItem, WeakReference context) : CommandItemViewModel(new(contextItem), context), IContextItemViewModel { private readonly KeyChord nullKeyChord = new(0, 0, 0); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandItemViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandItemViewModel.cs index 1861b53ef7..1b9dcf211a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandItemViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandItemViewModel.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using Microsoft.CmdPal.Core.ViewModels.Messages; using Microsoft.CmdPal.Core.ViewModels.Models; using Microsoft.CommandPalette.Extensions; @@ -9,6 +10,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit; namespace Microsoft.CmdPal.Core.ViewModels; +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBarContext { public ExtensionObject Model => _commandItemModel; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IContextItemViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IContextItemViewModel.cs index 704947c3a8..cad1af9d4d 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IContextItemViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/IContextItemViewModel.cs @@ -4,12 +4,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Microsoft.CmdPal.Core.ViewModels; +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public interface IContextItemViewModel { } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/SeparatorContextItemViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/SeparatorContextItemViewModel.cs index c6858f490d..8d896bd341 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/SeparatorContextItemViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/SeparatorContextItemViewModel.cs @@ -2,11 +2,13 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using Microsoft.CmdPal.Core.ViewModels; using Microsoft.CommandPalette.Extensions; namespace Microsoft.CmdPal.Core.ViewModels; +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public partial class SeparatorContextItemViewModel() : IContextItemViewModel, ISeparatorContextItem { } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs index 71c0a4e810..f877afa9c5 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs @@ -60,6 +60,7 @@ public partial class MainListPage : DynamicListPage, var settings = _serviceProvider.GetService()!; settings.SettingsChanged += SettingsChangedHandler; HotReloadSettings(settings); + _includeApps = _tlcManager.IsProviderActive(AllAppsCommandProvider.WellKnownId); IsLoading = true; } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs index d561a0e00f..9728e8339e 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs @@ -98,35 +98,107 @@ public partial class ContentFormViewModel(IFormContent _form, WeakReference(new(openUrlAction.Url)); - return; - } + // BODGY circa GH #40979 + // Usually, you're supposed to try to cast the action to a specific + // type, and use those objects to get the data you need. + // However, there's something weird with AdaptiveCards and the way it + // works when we consume it when built in Release, with AOT (and + // trimming) enabled. Any sort of `action.As()` + // or similar will throw a System.InvalidCastException. + // + // Instead we have this horror show. + // + // The `action.ToJson()` blob ACTUALLY CONTAINS THE `type` field, which + // we can use to determine what kind of action it is. Then we can parse + // the JSON manually based on the type. + var actionJson = action.ToJson(); - if (action is AdaptiveSubmitAction or AdaptiveExecuteAction) + if (actionJson.TryGetValue("type", out var actionTypeValue)) { - // Get the data and inputs - var dataString = (action as AdaptiveSubmitAction)?.DataJson.Stringify() ?? string.Empty; - var inputString = inputs.Stringify(); + var actionTypeString = actionTypeValue.GetString(); + Logger.LogTrace($"atString={actionTypeString}"); - _ = Task.Run(() => + var actionType = actionTypeString switch { - try - { - var model = _formModel.Unsafe!; - if (model != null) + "Action.Submit" => ActionType.Submit, + "Action.Execute" => ActionType.Execute, + "Action.OpenUrl" => ActionType.OpenUrl, + _ => ActionType.Unsupported, + }; + + Logger.LogDebug($"{actionTypeString}->{actionType}"); + + switch (actionType) + { + case ActionType.OpenUrl: { - var result = model.SubmitForm(inputString, dataString); - WeakReferenceMessenger.Default.Send(new(new(result))); + HandleOpenUrlAction(action, actionJson); } - } - catch (Exception ex) - { - ShowException(ex); - } - }); + + break; + case ActionType.Submit: + case ActionType.Execute: + { + HandleSubmitAction(action, actionJson, inputs); + } + + break; + default: + Logger.LogError($"{actionType} was an unexpected action `type`"); + break; + } } + else + { + Logger.LogError($"actionJson.TryGetValue(type) failed"); + } + } + + private void HandleOpenUrlAction(IAdaptiveActionElement action, JsonObject actionJson) + { + if (actionJson.TryGetValue("url", out var actionUrlValue)) + { + var actionUrl = actionUrlValue.GetString() ?? string.Empty; + if (Uri.TryCreate(actionUrl, default(UriCreationOptions), out var uri)) + { + WeakReferenceMessenger.Default.Send(new(uri)); + } + else + { + Logger.LogError($"Failed to produce URI for {actionUrlValue}"); + } + } + } + + private void HandleSubmitAction( + IAdaptiveActionElement action, + JsonObject actionJson, + JsonObject inputs) + { + var dataString = string.Empty; + if (actionJson.TryGetValue("data", out var actionDataValue)) + { + dataString = actionDataValue.Stringify() ?? string.Empty; + } + + var inputString = inputs.Stringify(); + _ = Task.Run(() => + { + try + { + var model = _formModel.Unsafe!; + if (model != null) + { + var result = model.SubmitForm(inputString, dataString); + Logger.LogDebug($"SubmitForm() returned {result}"); + WeakReferenceMessenger.Default.Send(new(new(result))); + } + } + catch (Exception ex) + { + ShowException(ex); + } + }); } private static readonly string ErrorCardJson = """ diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs index 03b40bf8d8..ec3604621d 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs @@ -64,6 +64,9 @@ public partial class App : Application this.InitializeComponent(); + // Ensure types used in XAML are preserved for AOT compilation + TypePreservation.PreserveTypes(); + NativeEventWaiter.WaitForEventLoop( "Local\\PowerToysCmdPal-ExitEvent-eb73f6be-3f22-4b36-aee3-62924ba40bfd", () => { diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml index 9fb047641f..107db49939 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml @@ -59,8 +59,24 @@ + + + + + @@ -155,12 +171,7 @@ Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind ViewModel.PrimaryCommand.Name, Mode=OneWay}" /> - + @@ -179,19 +190,10 @@ Text="{x:Bind ViewModel.SecondaryCommand.Name, Mode=OneWay}" /> - + - + @@ -199,7 +201,7 @@