mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
Continuing part deux
This commit is contained in:
@@ -4,17 +4,16 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class BeginInvokeEvent : TelemetryEventBase, IEvent
|
public class BeginInvokeEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
|
||||||
public BeginInvokeEvent()
|
public BeginInvokeEvent()
|
||||||
{
|
{
|
||||||
@@ -4,17 +4,16 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class ColdLaunchEvent : TelemetryEventBase, IEvent
|
public class ColdLaunchEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
|
||||||
public ColdLaunchEvent()
|
public ColdLaunchEvent()
|
||||||
{
|
{
|
||||||
@@ -4,15 +4,14 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class DismissedOnEscEvent : TelemetryEventBase, IEvent
|
public class DismissedOnEscEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,14 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class DismissedOnLostFocusEvent : TelemetryEventBase, IEvent
|
public class DismissedOnLostFocusEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,14 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class HotkeySummonedEvent : TelemetryEventBase, IEvent
|
public class HotkeySummonedEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public bool Global { get; set; }
|
public bool Global { get; set; }
|
||||||
|
|
||||||
@@ -21,5 +20,5 @@ public class HotkeySummonedEvent : TelemetryEventBase, IEvent
|
|||||||
Global = global;
|
Global = global;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,14 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class OpenPageEvent : TelemetryEventBase, IEvent
|
public class OpenPageEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public int PageDepth { get; set; }
|
public int PageDepth { get; set; }
|
||||||
|
|
||||||
@@ -26,5 +25,5 @@ public class OpenPageEvent : TelemetryEventBase, IEvent
|
|||||||
EventName = "CmdPal_OpenPage";
|
EventName = "CmdPal_OpenPage";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
}
|
}
|
||||||
@@ -4,17 +4,16 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class OpenUriEvent : TelemetryEventBase, IEvent
|
public class OpenUriEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
|
|
||||||
@@ -4,15 +4,14 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class ProcessStartedEvent : TelemetryEventBase, IEvent
|
public class ProcessStartedEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
}
|
}
|
||||||
@@ -4,17 +4,16 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class ReactivateInstanceEvent : TelemetryEventBase, IEvent
|
public class ReactivateInstanceEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
|
||||||
public ReactivateInstanceEvent()
|
public ReactivateInstanceEvent()
|
||||||
{
|
{
|
||||||
@@ -4,17 +4,16 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class RunCommandEvent : TelemetryEventBase, IEvent
|
public class RunCommandEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
|
||||||
public string Command { get; set; }
|
public string Command { get; set; }
|
||||||
|
|
||||||
@@ -4,17 +4,16 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Events;
|
namespace Microsoft.CommandPalette.UI.Models.Events;
|
||||||
|
|
||||||
[EventData]
|
[EventData]
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||||
public class RunQueryEvent : TelemetryEventBase, IEvent
|
public class RunQueryEvent : EventBase, IEvent
|
||||||
{
|
{
|
||||||
public override PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
|
||||||
public string Query { get; set; }
|
public string Query { get; set; }
|
||||||
|
|
||||||
@@ -4,16 +4,16 @@
|
|||||||
|
|
||||||
using Microsoft.Windows.ApplicationModel.Resources;
|
using Microsoft.Windows.ApplicationModel.Resources;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.Helpers;
|
namespace Microsoft.CommandPalette.UI.Models.Helpers;
|
||||||
|
|
||||||
internal static class ResourceLoaderInstance
|
public static class ResourceLoaderInstance
|
||||||
{
|
{
|
||||||
internal static ResourceLoader ResourceLoader { get; private set; }
|
public static ResourceLoader ResourceLoader { get; private set; }
|
||||||
|
|
||||||
static ResourceLoaderInstance()
|
static ResourceLoaderInstance()
|
||||||
{
|
{
|
||||||
ResourceLoader = new ResourceLoader("resources.pri");
|
ResourceLoader = new ResourceLoader("resources.pri");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string GetString(string resourceId) => ResourceLoader.GetString(resourceId);
|
public static string GetString(string resourceId) => ResourceLoader.GetString(resourceId);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record ClearSearchMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record FocusSearchBoxMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record HandleCommandResultMessage() // ExtensionObject<ICommandResult> Result)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record HideDetailsMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record LaunchUriMessage(Uri Uri)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record NavigateBackMessage(bool FromBackspace = false)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record NavigateToPageMessage(); // PageViewModel Page, bool WithAnimation, CancellationToken CancellationToken);
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to do a command - navigate to a page or invoke it
|
||||||
|
/// </summary>
|
||||||
|
public record PerformCommandMessage
|
||||||
|
{
|
||||||
|
// public ExtensionObject<ICommand> Command { get; }
|
||||||
|
public object? Context { get; }
|
||||||
|
|
||||||
|
public bool WithAnimation { get; set; } = true;
|
||||||
|
|
||||||
|
public PerformCommandMessage() // ExtensionObject<ICommand> command)
|
||||||
|
{
|
||||||
|
// Command = command;
|
||||||
|
Context = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public PerformCommandMessage(ExtensionObject<ICommand> command, ExtensionObject<IListItem> context)
|
||||||
|
// {
|
||||||
|
// Command = command;
|
||||||
|
// Context = context.Unsafe;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public PerformCommandMessage(ExtensionObject<ICommand> command, ExtensionObject<ICommandItem> context)
|
||||||
|
// {
|
||||||
|
// Command = command;
|
||||||
|
// Context = context.Unsafe;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public PerformCommandMessage(ExtensionObject<ICommand> command, ExtensionObject<ICommandContextItem> context)
|
||||||
|
// {
|
||||||
|
// Command = command;
|
||||||
|
// Context = context.Unsafe;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public PerformCommandMessage(CommandContextItemViewModel contextCommand)
|
||||||
|
// {
|
||||||
|
// Command = contextCommand.Command.Model;
|
||||||
|
// Context = contextCommand.Model.Unsafe;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public PerformCommandMessage(ConfirmResultViewModel vm)
|
||||||
|
// {
|
||||||
|
// Command = vm.PrimaryCommand.Model;
|
||||||
|
// Context = null;
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record SettingsWindowClosedMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.CommandPalette.Extensions;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record ShowConfirmationMessage(IConfirmationArgs? Args)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record ShowDetailsMessage() // DetailsViewModel Details)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record ShowToastMessage(string Message)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Windows.System;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
|
||||||
|
public record TryCommandKeybindingMessage(bool Ctrl, bool Alt, bool Shift, bool Win, VirtualKey Key)
|
||||||
|
{
|
||||||
|
public bool Handled { get; set; }
|
||||||
|
}
|
||||||
@@ -39,8 +39,4 @@
|
|||||||
<ProjectReference Include="..\..\SDK\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
<ProjectReference Include="..\..\SDK\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Events\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||||
|
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
|
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CommandPalette</OutputPath>
|
||||||
|
<!-- For MVVM Toolkit Partial Properties/AOT support -->
|
||||||
|
<LangVersion>preview</LangVersion>
|
||||||
|
<!-- Disable SA1313 for Primary Constructor fields conflict https://learn.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors -->
|
||||||
|
<NoWarn>SA1313;</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommunityToolkit.Common" />
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||||
|
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||||
|
<ProjectReference Include="..\..\SDK\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
GetPhysicallyInstalledSystemMemory
|
||||||
|
GlobalMemoryStatusEx
|
||||||
|
GetSystemInfo
|
||||||
|
GetForegroundWindow
|
||||||
|
SetForegroundWindow
|
||||||
|
GetWindowRect
|
||||||
|
GetCursorPos
|
||||||
|
SetWindowPos
|
||||||
|
HWND_TOPMOST
|
||||||
|
HWND_BOTTOM
|
||||||
|
IsIconic
|
||||||
|
RegisterHotKey
|
||||||
|
UnregisterHotKey
|
||||||
|
SetWindowLongPtr
|
||||||
|
CallWindowProc
|
||||||
|
ShowWindow
|
||||||
|
SetForegroundWindow
|
||||||
|
EnableWindow
|
||||||
|
IsWindowEnabled
|
||||||
|
SetFocus
|
||||||
|
SetActiveWindow
|
||||||
|
MonitorFromWindow
|
||||||
|
GetMonitorInfo
|
||||||
|
GetDpiForMonitor
|
||||||
|
WM_HOTKEY
|
||||||
|
WM_NCLBUTTONDBLCLK
|
||||||
|
|
||||||
|
Shell_NotifyIcon
|
||||||
|
LoadIcon
|
||||||
|
WM_USER
|
||||||
|
WM_WINDOWPOSCHANGING
|
||||||
|
RegisterWindowMessageW
|
||||||
|
ExtractIconEx
|
||||||
|
TRACK_POPUP_MENU_FLAGS
|
||||||
|
WM_COMMAND
|
||||||
|
WM_RBUTTONUP
|
||||||
|
WM_LBUTTONUP
|
||||||
|
WM_LBUTTONDBLCLK
|
||||||
|
CreatePopupMenu
|
||||||
|
TrackPopupMenuEx
|
||||||
|
InsertMenu
|
||||||
|
|
||||||
|
MessageBox
|
||||||
|
DwmGetWindowAttribute
|
||||||
|
DwmSetWindowAttribute
|
||||||
|
DWM_CLOAKED_APP
|
||||||
|
DWM_WINDOW_CORNER_PREFERENCE
|
||||||
|
|
||||||
|
CoWaitForMultipleObjects
|
||||||
|
INFINITE
|
||||||
|
CWMO_FLAGS
|
||||||
|
|
||||||
|
GetCurrentThreadId
|
||||||
|
SetWindowsHookEx
|
||||||
|
UnhookWindowsHookEx
|
||||||
|
CallNextHookEx
|
||||||
|
GetModuleHandle
|
||||||
|
|
||||||
|
GetWindowLong
|
||||||
|
SetWindowLong
|
||||||
|
WINDOW_EX_STYLE
|
||||||
|
CreateWindowEx
|
||||||
|
WNDCLASSEXW
|
||||||
|
RegisterClassEx
|
||||||
|
GetStockObject
|
||||||
|
GetModuleHandle
|
||||||
@@ -8,14 +8,10 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Microsoft.CommandPalette.UI.Models;
|
using Microsoft.CommandPalette.UI.Models;
|
||||||
using Microsoft.CommandPalette.UI.Models.Messages;
|
using Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Windows.Win32;
|
|
||||||
using Windows.Win32.Foundation;
|
|
||||||
using Windows.Win32.UI.Shell;
|
|
||||||
using Windows.Win32.UI.WindowsAndMessaging;
|
|
||||||
using WinRT.Interop;
|
using WinRT.Interop;
|
||||||
using RS_ = Microsoft.CommandPalette.UI.Helpers.ResourceLoaderInstance;
|
using RS_ = Microsoft.CommandPalette.UI.Models.Helpers.ResourceLoaderInstance;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.Helpers;
|
namespace Microsoft.CommandPalette.UI.Services;
|
||||||
|
|
||||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_*")]
|
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_*")]
|
||||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_*")]
|
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_*")]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.ViewModels;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encapsulates a navigation request within Command Palette view models.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="TargetViewModel">A view model that should be navigated to.</param>
|
||||||
|
/// <param name="NavigationToken"> A <see cref="CancellationToken"/> that can be used to cancel the pending navigation.</param>
|
||||||
|
public record AsyncNavigationRequest(object? TargetViewModel, CancellationToken NavigationToken);
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class PageViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -2,7 +2,11 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.ViewModels;
|
namespace Microsoft.CommandPalette.ViewModels;
|
||||||
@@ -12,28 +16,381 @@ public partial class ShellViewModel : ObservableObject
|
|||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
private readonly TaskScheduler _scheduler;
|
private readonly TaskScheduler _scheduler;
|
||||||
|
|
||||||
|
// private IPage? _rootPage;
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool IsLoaded { get; set; } = false;
|
public partial bool IsLoaded { get; set; } = false;
|
||||||
|
|
||||||
private bool _isNested;
|
// private bool _isNested;
|
||||||
|
// public bool IsNested => _isNested;
|
||||||
|
// Cancellation token source for page loading/navigation operations
|
||||||
|
// private CancellationTokenSource? _navigationCts;
|
||||||
|
// [ObservableProperty]
|
||||||
|
// public partial DetailsViewModel? Details { get; set; }
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial bool IsDetailsVisible { get; set; }
|
||||||
|
|
||||||
public bool IsNested => _isNested;
|
[ObservableProperty]
|
||||||
|
public partial bool IsSearchBoxVisible { get; set; } = true;
|
||||||
|
|
||||||
|
// private PageViewModel _currentPage;
|
||||||
|
// public PageViewModel CurrentPage
|
||||||
|
// {
|
||||||
|
// get => _currentPage;
|
||||||
|
// set
|
||||||
|
// {
|
||||||
|
// var oldValue = _currentPage;
|
||||||
|
// if (SetProperty(ref _currentPage, value))
|
||||||
|
// {
|
||||||
|
// oldValue.PropertyChanged -= CurrentPage_PropertyChanged;
|
||||||
|
// value.PropertyChanged += CurrentPage_PropertyChanged;
|
||||||
|
// if (oldValue is IDisposable disposable)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// disposable.Dispose();
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogError(ex.ToString());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
private void CurrentPage_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// if (e.PropertyName == nameof(PageViewModel.HasSearchBox))
|
||||||
|
// {
|
||||||
|
// IsSearchBoxVisible = CurrentPage.HasSearchBox;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// public PageViewModel NullPage { get; private set; }
|
||||||
public ShellViewModel(
|
public ShellViewModel(
|
||||||
TaskScheduler scheduler,
|
TaskScheduler scheduler,
|
||||||
ILogger logger)
|
ILogger logger)
|
||||||
|
|
||||||
|
// IRootPageService rootPageService,
|
||||||
|
// IPageViewModelFactoryService pageViewModelFactory,
|
||||||
|
// IAppHostService appHostService)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
|
||||||
_scheduler = scheduler;
|
_scheduler = scheduler;
|
||||||
_isNested = false;
|
this.logger = logger;
|
||||||
|
|
||||||
|
// _pageViewModelFactory = pageViewModelFactory;
|
||||||
|
// _rootPageService = rootPageService;
|
||||||
|
// _appHostService = appHostService;
|
||||||
|
// NullPage = new NullPageViewModel(_scheduler, appHostService.GetDefaultHost());
|
||||||
|
// _currentPage = new LoadingPageViewModel(null, _scheduler, appHostService.GetDefaultHost());
|
||||||
|
|
||||||
|
// Register to receive messages
|
||||||
|
// WeakReferenceMessenger.Default.Register<PerformCommandMessage>(this);
|
||||||
|
// WeakReferenceMessenger.Default.Register<HandleCommandResultMessage>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
public async Task<bool> LoadAsync()
|
||||||
|
{
|
||||||
|
// First, do any loading that the root page service needs to do before we can
|
||||||
|
// display the root page. For example, this might include loading
|
||||||
|
// the built-in commands, or loading the settings.
|
||||||
|
// await _rootPageService.PreLoadAsync();
|
||||||
|
IsLoaded = true;
|
||||||
|
|
||||||
|
// Now that the basics are set up, we can load the root page.
|
||||||
|
// _rootPage = _rootPageService.GetRootPage();
|
||||||
|
// This sends a message to us to load the root page view model.
|
||||||
|
// WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(new ExtensionObject<ICommand>(_rootPage)));
|
||||||
|
// Now that the root page is loaded, do any post-load work that the root page service needs to do.
|
||||||
|
// This runs asynchronously, on a background thread.
|
||||||
|
// This might include starting extensions, for example.
|
||||||
|
// Note: We don't await this, so that we can return immediately.
|
||||||
|
// This is important because we don't want to block the UI thread.
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// await _rootPageService.PostLoadRootPageAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private async Task LoadPageViewModelAsync(PageViewModel viewModel, CancellationToken cancellationToken = default)
|
||||||
|
// {
|
||||||
|
// // Note: We removed the general loading state, extensions sometimes use their `IsLoading`, but it's inconsistently implemented it seems.
|
||||||
|
// // IsInitialized is our main indicator of the general overall state of loading props/items from a page we use for the progress bar
|
||||||
|
// // This triggers that load generally with the InitializeCommand asynchronously when we navigate to a page.
|
||||||
|
// // We could re-track the page loading status, if we need it more granularly below again, but between the initialized and error message, we can infer some status.
|
||||||
|
// // We could also maybe move this thread offloading we do for loading into PageViewModel and better communicate between the two... a few different options.
|
||||||
|
// ////LoadedState = ViewModelLoadedState.Loading;
|
||||||
|
// if (!viewModel.IsInitialized
|
||||||
|
// && viewModel.InitializeCommand is not null)
|
||||||
|
// {
|
||||||
|
// var outer = Task.Run(
|
||||||
|
// async () =>
|
||||||
|
// {
|
||||||
|
// // You know, this creates the situation where we wait for
|
||||||
|
// // both loading page properties, AND the items, before we
|
||||||
|
// // display anything.
|
||||||
|
// //
|
||||||
|
// // We almost need to do an async await on initialize, then
|
||||||
|
// // just a fire-and-forget on FetchItems.
|
||||||
|
// // RE: We do set the CurrentPage in ShellPage.xaml.cs as well, so, we kind of are doing two different things here.
|
||||||
|
// // Definitely some more clean-up to do, but at least its centralized to one spot now.
|
||||||
|
// viewModel.InitializeCommand.Execute(null);
|
||||||
|
// await viewModel.InitializeCommand.ExecutionTask!;
|
||||||
|
// if (viewModel.InitializeCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
|
||||||
|
// {
|
||||||
|
// if (viewModel.InitializeCommand.ExecutionTask.Exception is AggregateException ex)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogError(ex.ToString());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// var t = Task.Factory.StartNew(
|
||||||
|
// () =>
|
||||||
|
// {
|
||||||
|
// if (cancellationToken.IsCancellationRequested)
|
||||||
|
// {
|
||||||
|
// if (viewModel is IDisposable disposable)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// disposable.Dispose();
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogError(ex.ToString());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// CurrentPage = viewModel;
|
||||||
|
// },
|
||||||
|
// cancellationToken,
|
||||||
|
// TaskCreationOptions.None,
|
||||||
|
// _scheduler);
|
||||||
|
// await t;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// cancellationToken);
|
||||||
|
// await outer;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (cancellationToken.IsCancellationRequested)
|
||||||
|
// {
|
||||||
|
// if (viewModel is IDisposable disposable)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// disposable.Dispose();
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogError(ex.ToString());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// CurrentPage = viewModel;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
public void Receive(PerformCommandMessage message)
|
||||||
|
{
|
||||||
|
// PerformCommand(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void PerformCommand(PerformCommandMessage message)
|
||||||
|
// {
|
||||||
|
// // Create/replace the navigation cancellation token.
|
||||||
|
// // If one already exists, cancel and dispose it first.
|
||||||
|
// var newCts = new CancellationTokenSource();
|
||||||
|
// var oldCts = Interlocked.Exchange(ref _navigationCts, newCts);
|
||||||
|
// if (oldCts is not null)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// oldCts.Cancel();
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogError(ex.ToString());
|
||||||
|
// }
|
||||||
|
// finally
|
||||||
|
// {
|
||||||
|
// oldCts.Dispose();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// var navigationToken = newCts.Token;
|
||||||
|
// var command = message.Command.Unsafe;
|
||||||
|
// if (command is null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// var host = _appHostService.GetHostForCommand(message.Context, CurrentPage.ExtensionHost);
|
||||||
|
// _rootPageService.OnPerformCommand(message.Context, !CurrentPage.IsNested, host);
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// if (command is IPage page)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogDebug($"Navigating to page");
|
||||||
|
// var isMainPage = command == _rootPage;
|
||||||
|
// _isNested = !isMainPage;
|
||||||
|
// // Construct our ViewModel of the appropriate type and pass it the UI Thread context.
|
||||||
|
// var pageViewModel = _pageViewModelFactory.TryCreatePageViewModel(page, _isNested, host);
|
||||||
|
// if (pageViewModel is null)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogError($"Failed to create ViewModel for page {page.GetType().Name}");
|
||||||
|
// throw new NotSupportedException();
|
||||||
|
// }
|
||||||
|
// // Clear command bar, ViewModel initialization can already set new commands if it wants to
|
||||||
|
// OnUIThread(() => WeakReferenceMessenger.Default.Send<UpdateCommandBarMessage>(new(null)));
|
||||||
|
// // Kick off async loading of our ViewModel
|
||||||
|
// LoadPageViewModelAsync(pageViewModel, navigationToken)
|
||||||
|
// .ContinueWith(
|
||||||
|
// (Task t) =>
|
||||||
|
// {
|
||||||
|
// // clean up the navigation token if it's still ours
|
||||||
|
// if (Interlocked.CompareExchange(ref _navigationCts, null, newCts) == newCts)
|
||||||
|
// {
|
||||||
|
// newCts.Dispose();
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// navigationToken,
|
||||||
|
// TaskContinuationOptions.None,
|
||||||
|
// _scheduler);
|
||||||
|
// // While we're loading in the background, immediately move to the next page.
|
||||||
|
// WeakReferenceMessenger.Default.Send<NavigateToPageMessage>(new(pageViewModel, message.WithAnimation, navigationToken));
|
||||||
|
// // Note: Originally we set our page back in the ViewModel here, but that now happens in response to the Frame navigating triggered from the above
|
||||||
|
// // See RootFrame_Navigated event handler.
|
||||||
|
// }
|
||||||
|
// else if (command is IInvokableCommand invokable)
|
||||||
|
// {
|
||||||
|
// CoreLogger.LogDebug($"Invoking command");
|
||||||
|
// WeakReferenceMessenger.Default.Send<BeginInvokeMessage>();
|
||||||
|
// StartInvoke(message, invokable, host);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// // TODO: It would be better to do this as a page exception, rather
|
||||||
|
// // than a silent log message.
|
||||||
|
// host?.Log(ex.Message);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// private void StartInvoke(PerformCommandMessage message, IInvokableCommand invokable, AppExtensionHost? host)
|
||||||
|
// {
|
||||||
|
// // TODO GH #525 This needs more better locking.
|
||||||
|
// lock (_invokeLock)
|
||||||
|
// {
|
||||||
|
// if (_handleInvokeTask is not null)
|
||||||
|
// {
|
||||||
|
// // do nothing - a command is already doing a thing
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// _handleInvokeTask = Task.Run(() =>
|
||||||
|
// {
|
||||||
|
// SafeHandleInvokeCommandSynchronous(message, invokable, host);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// private void SafeHandleInvokeCommandSynchronous(PerformCommandMessage message, IInvokableCommand invokable, AppExtensionHost? host)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// // Call out to extension process.
|
||||||
|
// // * May fail!
|
||||||
|
// // * May never return!
|
||||||
|
// var result = invokable.Invoke(message.Context);
|
||||||
|
// // But if it did succeed, we need to handle the result.
|
||||||
|
// UnsafeHandleCommandResult(result);
|
||||||
|
// _handleInvokeTask = null;
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// _handleInvokeTask = null;
|
||||||
|
// // TODO: It would be better to do this as a page exception, rather
|
||||||
|
// // than a silent log message.
|
||||||
|
// host?.Log(ex.Message);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// private void UnsafeHandleCommandResult(ICommandResult? result)
|
||||||
|
// {
|
||||||
|
// if (result is null)
|
||||||
|
// {
|
||||||
|
// // No result, nothing to do.
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// var kind = result.Kind;
|
||||||
|
// CoreLogger.LogDebug($"handling {kind.ToString()}");
|
||||||
|
// WeakReferenceMessenger.Default.Send<CmdPalInvokeResultMessage>(new(kind));
|
||||||
|
// switch (kind)
|
||||||
|
// {
|
||||||
|
// case CommandResultKind.Dismiss:
|
||||||
|
// {
|
||||||
|
// // Reset the palette to the main page and dismiss
|
||||||
|
// GoHome(withAnimation: false, focusSearch: false);
|
||||||
|
// WeakReferenceMessenger.Default.Send<DismissMessage>();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case CommandResultKind.GoHome:
|
||||||
|
// {
|
||||||
|
// // Go back to the main page, but keep it open
|
||||||
|
// GoHome();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case CommandResultKind.GoBack:
|
||||||
|
// {
|
||||||
|
// GoBack();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case CommandResultKind.Hide:
|
||||||
|
// {
|
||||||
|
// // Keep this page open, but hide the palette.
|
||||||
|
// WeakReferenceMessenger.Default.Send<DismissMessage>();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case CommandResultKind.KeepOpen:
|
||||||
|
// {
|
||||||
|
// // Do nothing.
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case CommandResultKind.Confirm:
|
||||||
|
// {
|
||||||
|
// if (result.Args is IConfirmationArgs a)
|
||||||
|
// {
|
||||||
|
// WeakReferenceMessenger.Default.Send<ShowConfirmationMessage>(new(a));
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case CommandResultKind.ShowToast:
|
||||||
|
// {
|
||||||
|
// if (result.Args is IToastArgs a)
|
||||||
|
// {
|
||||||
|
// WeakReferenceMessenger.Default.Send<ShowToastMessage>(new(a.Message));
|
||||||
|
// UnsafeHandleCommandResult(a.Result);
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
public void GoHome(bool withAnimation = true, bool focusSearch = true)
|
public void GoHome(bool withAnimation = true, bool focusSearch = true)
|
||||||
{
|
{
|
||||||
|
// _rootPageService.GoHome();
|
||||||
|
WeakReferenceMessenger.Default.Send<GoHomeMessage>(new(withAnimation, focusSearch));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GoBack(bool withAnimation = true, bool focusSearch = true)
|
public void GoBack(bool withAnimation = true, bool focusSearch = true)
|
||||||
{
|
{
|
||||||
|
WeakReferenceMessenger.Default.Send<GoBackMessage>(new(withAnimation, focusSearch));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(HandleCommandResultMessage message)
|
||||||
|
{
|
||||||
|
// UnsafeHandleCommandResult(message.Result.Unsafe);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUIThread(Action action)
|
private void OnUIThread(Action action)
|
||||||
@@ -44,4 +401,9 @@ public partial class ShellViewModel : ObservableObject
|
|||||||
TaskCreationOptions.None,
|
TaskCreationOptions.None,
|
||||||
_scheduler);
|
_scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CancelNavigation()
|
||||||
|
{
|
||||||
|
// _navigationCts?.Cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.ViewModels;
|
namespace Microsoft.CommandPalette.UI.ViewModels;
|
||||||
|
|
||||||
public partial class ToastViewModel : ObservableObject
|
public partial class ToastViewModel : ObservableObject
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="Microsoft.CommandPalette.UI.Controls.CommandBar"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
|
||||||
|
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
Background="Transparent"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<converters:StringVisibilityConverter
|
||||||
|
x:Key="StringNotEmptyToVisibilityConverter"
|
||||||
|
EmptyValue="Collapsed"
|
||||||
|
NotEmptyValue="Visible" />
|
||||||
|
<converters:BoolToVisibilityConverter
|
||||||
|
x:Key="BoolToInvertedVisibilityConverter"
|
||||||
|
FalseValue="Visible"
|
||||||
|
TrueValue="Collapsed" />
|
||||||
|
|
||||||
|
<StackLayout
|
||||||
|
x:Name="VerticalStackLayout"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="4" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
MinHeight="40"
|
||||||
|
Padding="4"
|
||||||
|
ColumnSpacing="8">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
x:Name="IconRoot"
|
||||||
|
Margin="3,0,-5,0">
|
||||||
|
<Button
|
||||||
|
x:Name="StatusMessagesButton"
|
||||||
|
x:Uid="StatusMessagesButton"
|
||||||
|
Padding="4"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}">
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Button
|
||||||
|
x:Name="SettingsIconButton"
|
||||||
|
x:Uid="SettingsButton"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<FontIcon
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="16"
|
||||||
|
Glyph="" />
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="Settings" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="Title here"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
<StackPanel
|
||||||
|
Grid.Column="2"
|
||||||
|
Padding="0,0,4,0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="4">
|
||||||
|
<Button
|
||||||
|
x:Name="PrimaryButton"
|
||||||
|
Padding="6,4,4,4"
|
||||||
|
AutomationProperties.AutomationId="PrimaryCommandButton"
|
||||||
|
AutomationProperties.Name="Title here"
|
||||||
|
Background="Transparent"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBlock
|
||||||
|
MaxWidth="160"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
MaxLines="1"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
<Border Style="{StaticResource HotkeyStyle}">
|
||||||
|
<FontIcon Glyph="" Style="{StaticResource HotkeyFontIconStyle}" />
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
x:Name="SecondaryButton"
|
||||||
|
Padding="6,4,4,4"
|
||||||
|
x:Load="{x:Bind IsLoaded, Mode=OneWay}"
|
||||||
|
AutomationProperties.AutomationId="SecondaryCommandButton"
|
||||||
|
AutomationProperties.Name="Secondary"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBlock
|
||||||
|
MaxWidth="160"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
MaxLines="1"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="Secondary"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||||
|
<Border Padding="4,2,4,2" Style="{StaticResource HotkeyStyle}">
|
||||||
|
<TextBlock Style="{StaticResource HotkeyTextBlockStyle}" Text="Ctrl" />
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource HotkeyStyle}">
|
||||||
|
<FontIcon Glyph="" Style="{StaticResource HotkeyFontIconStyle}" />
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
x:Name="MoreCommandsButton"
|
||||||
|
x:Uid="MoreCommandsButton"
|
||||||
|
Padding="6,4,4,4"
|
||||||
|
AutomationProperties.AutomationId="MoreContextMenuButton"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}"
|
||||||
|
ToolTipService.ToolTip="Ctrl+K">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="More"
|
||||||
|
TextTrimming="WordEllipsis"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||||
|
<Border Padding="4,2,4,2" Style="{StaticResource HotkeyStyle}">
|
||||||
|
<TextBlock Style="{StaticResource HotkeyTextBlockStyle}" Text="Ctrl" />
|
||||||
|
</Border>
|
||||||
|
<Border Padding="4,2,4,2" Style="{StaticResource HotkeyStyle}">
|
||||||
|
<TextBlock Style="{StaticResource HotkeyTextBlockStyle}" Text="K" />
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.Controls;
|
||||||
|
|
||||||
|
public sealed partial class CommandBar : UserControl
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="Microsoft.CommandPalette.UI.Controls.SearchBar"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="using:Microsoft.CommandPalette.UI.Converters"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<!-- Search box -->
|
||||||
|
<TextBox
|
||||||
|
x:Name="SearchBox"
|
||||||
|
MinHeight="32"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
VerticalContentAlignment="Stretch"
|
||||||
|
AutomationProperties.AutomationId="MainSearchBox"
|
||||||
|
Style="{StaticResource SearchTextBoxStyle}" />
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.Controls;
|
||||||
|
|
||||||
|
public sealed partial class SearchBar : UserControl
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.CommandPalette.Extensions;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Windows.System;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Converters;
|
||||||
|
|
||||||
|
public partial class KeyChordToStringConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is KeyChord shortcut && (VirtualKey)shortcut.Vkey != VirtualKey.None)
|
||||||
|
{
|
||||||
|
var result = string.Empty;
|
||||||
|
|
||||||
|
if (shortcut.Modifiers.HasFlag(VirtualKeyModifiers.Control))
|
||||||
|
{
|
||||||
|
result += "Ctrl+";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shortcut.Modifiers.HasFlag(VirtualKeyModifiers.Shift))
|
||||||
|
{
|
||||||
|
result += "Shift+";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shortcut.Modifiers.HasFlag(VirtualKeyModifiers.Menu))
|
||||||
|
{
|
||||||
|
result += "Alt+";
|
||||||
|
}
|
||||||
|
|
||||||
|
result += (VirtualKey)shortcut.Vkey;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.CommandPalette.Extensions;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Converters;
|
||||||
|
|
||||||
|
public partial class MessageStateToSeverityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is MessageState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case MessageState.Info:
|
||||||
|
return InfoBarSeverity.Informational;
|
||||||
|
case MessageState.Success:
|
||||||
|
return InfoBarSeverity.Success;
|
||||||
|
case MessageState.Warning:
|
||||||
|
return InfoBarSeverity.Warning;
|
||||||
|
case MessageState.Error:
|
||||||
|
return InfoBarSeverity.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InfoBarSeverity.Informational;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using RS_ = Microsoft.CommandPalette.UI.Helpers.ResourceLoaderInstance;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Converters;
|
||||||
|
|
||||||
|
public partial class PlaceholderTextConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
return value is string placeholder && !string.IsNullOrEmpty(placeholder)
|
||||||
|
? placeholder
|
||||||
|
: (object)RS_.GetString("DefaultSearchPlaceholderText");
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
internal sealed partial class CompositeImageSourceProvider : IImageSourceProvider
|
||||||
|
{
|
||||||
|
private readonly IImageSourceProvider[] _imageProviders =
|
||||||
|
[
|
||||||
|
new HttpImageSourceProvider(),
|
||||||
|
new LocalImageSourceProvider(),
|
||||||
|
new DataImageSourceProvider()
|
||||||
|
];
|
||||||
|
|
||||||
|
public Task<ImageSourceInfo> GetImageSource(string url)
|
||||||
|
{
|
||||||
|
var provider = _imageProviders.FirstOrDefault(p => p.ShouldUseThisProvider(url));
|
||||||
|
if (provider == null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"No image provider found for URL: {url}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider.GetImageSource(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldUseThisProvider(string url)
|
||||||
|
{
|
||||||
|
return _imageProviders.Any(provider => provider.ShouldUseThisProvider(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// 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.Text;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
internal sealed partial class DataImageSourceProvider : IImageSourceProvider
|
||||||
|
{
|
||||||
|
private readonly ImageSourceFactory.ImageDecodeOptions _decodeOptions;
|
||||||
|
|
||||||
|
public DataImageSourceProvider(ImageSourceFactory.ImageDecodeOptions? decodeOptions = null)
|
||||||
|
=> _decodeOptions = decodeOptions ?? new ImageSourceFactory.ImageDecodeOptions();
|
||||||
|
|
||||||
|
public bool ShouldUseThisProvider(string url)
|
||||||
|
=> url.StartsWith("data:", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public async Task<ImageSourceInfo> GetImageSource(string url)
|
||||||
|
{
|
||||||
|
if (!ShouldUseThisProvider(url))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("URL is not a data: URI.", nameof(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// data:[<media type>][;base64],<data>
|
||||||
|
var comma = url.IndexOf(',');
|
||||||
|
if (comma < 0)
|
||||||
|
{
|
||||||
|
throw new FormatException("Invalid data URI: missing comma separator.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var header = url[5..comma]; // after "data:"
|
||||||
|
var payload = url[(comma + 1)..]; // after comma
|
||||||
|
|
||||||
|
// Parse header
|
||||||
|
string? contentType = null;
|
||||||
|
var isBase64 = false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(header))
|
||||||
|
{
|
||||||
|
var parts = header.Split(';');
|
||||||
|
|
||||||
|
// first token may be media type
|
||||||
|
if (!string.IsNullOrWhiteSpace(parts[0]) && parts[0].Contains('/'))
|
||||||
|
{
|
||||||
|
contentType = parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
isBase64 = parts.Any(static p => p.Equals("base64", StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = isBase64
|
||||||
|
? Convert.FromBase64String(payload)
|
||||||
|
: Encoding.UTF8.GetBytes(Uri.UnescapeDataString(payload));
|
||||||
|
|
||||||
|
var mem = new InMemoryRandomAccessStream();
|
||||||
|
using (var writer = new DataWriter(mem.GetOutputStreamAt(0)))
|
||||||
|
{
|
||||||
|
writer.WriteBytes(bytes);
|
||||||
|
await writer.StoreAsync()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.Seek(0);
|
||||||
|
|
||||||
|
var imagePayload = new ImageSourceFactory.ImagePayload(mem, contentType, null);
|
||||||
|
var imageSource = await ImageSourceFactory.CreateAsync(imagePayload, _decodeOptions);
|
||||||
|
return new ImageSourceInfo(imageSource, new ImageHints
|
||||||
|
{
|
||||||
|
DownscaleOnly = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// 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.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implementation of IImageProvider to handle http/https images, but adds
|
||||||
|
/// a new functionality to handle image scaling.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed partial class HttpImageSourceProvider : IImageSourceProvider
|
||||||
|
{
|
||||||
|
private readonly HttpClient _http;
|
||||||
|
|
||||||
|
public HttpImageSourceProvider(HttpClient? httpClient = null)
|
||||||
|
=> _http = httpClient ?? SharedHttpClient.Instance;
|
||||||
|
|
||||||
|
public bool ShouldUseThisProvider(string url)
|
||||||
|
=> Uri.TryCreate(url, UriKind.Absolute, out var uri)
|
||||||
|
&& (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps);
|
||||||
|
|
||||||
|
public async Task<ImageSourceInfo> GetImageSource(string url)
|
||||||
|
{
|
||||||
|
if (!ShouldUseThisProvider(url))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("URL must be absolute http/https.", nameof(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var req = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
|
|
||||||
|
req.Headers.TryAddWithoutValidation("Accept", "image/*,text/xml;q=0.9,application/xml;q=0.9,*/*;q=0.8");
|
||||||
|
|
||||||
|
using var resp = await _http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
resp.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var contentType = resp.Content.Headers.ContentType?.MediaType;
|
||||||
|
|
||||||
|
using var mem = new InMemoryRandomAccessStream();
|
||||||
|
await CopyToRandomAccessStreamAsync(resp, mem);
|
||||||
|
|
||||||
|
var hints = ImageHints.ParseHintsFromUri(new Uri(url));
|
||||||
|
var imageSource = await ImageSourceFactory.CreateAsync(
|
||||||
|
new ImageSourceFactory.ImagePayload(mem, contentType, new Uri(url)),
|
||||||
|
new ImageSourceFactory.ImageDecodeOptions { SniffContent = true });
|
||||||
|
|
||||||
|
return new ImageSourceInfo(imageSource, hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task CopyToRandomAccessStreamAsync(HttpResponseMessage resp, InMemoryRandomAccessStream mem)
|
||||||
|
{
|
||||||
|
var data = await resp.Content.ReadAsByteArrayAsync();
|
||||||
|
await mem.WriteAsync(data.AsBuffer());
|
||||||
|
mem.Seek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SharedHttpClient
|
||||||
|
{
|
||||||
|
public static readonly HttpClient Instance = Create();
|
||||||
|
|
||||||
|
private static HttpClient Create()
|
||||||
|
{
|
||||||
|
var c = new HttpClient
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(30),
|
||||||
|
};
|
||||||
|
c.DefaultRequestHeaders.UserAgent.ParseAdd("CommandPalette/1.0");
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
internal interface IImageSourceProvider
|
||||||
|
{
|
||||||
|
Task<ImageSourceInfo> GetImageSource(string url);
|
||||||
|
|
||||||
|
bool ShouldUseThisProvider(string url);
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
internal sealed class ImageHints
|
||||||
|
{
|
||||||
|
public static ImageHints Empty { get; } = new();
|
||||||
|
|
||||||
|
public double? DesiredPixelWidth { get; init; }
|
||||||
|
|
||||||
|
public double? DesiredPixelHeight { get; init; }
|
||||||
|
|
||||||
|
public double? MaxPixelWidth { get; init; }
|
||||||
|
|
||||||
|
public double? MaxPixelHeight { get; init; }
|
||||||
|
|
||||||
|
public bool? DownscaleOnly { get; init; }
|
||||||
|
|
||||||
|
public string? FitMode { get; init; } // fit=fit
|
||||||
|
|
||||||
|
public static ImageHints ParseHintsFromUri(Uri? uri)
|
||||||
|
{
|
||||||
|
if (uri is null || string.IsNullOrEmpty(uri.Query))
|
||||||
|
{
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var p in uri.Query.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
var kv = p.Split('=', 2);
|
||||||
|
var k = Uri.UnescapeDataString(kv[0]);
|
||||||
|
var v = kv.Length > 1 ? Uri.UnescapeDataString(kv[1]) : string.Empty;
|
||||||
|
dict[k] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ImageHints
|
||||||
|
{
|
||||||
|
DesiredPixelWidth = GetInt("--x-cmdpal-width"),
|
||||||
|
DesiredPixelHeight = GetInt("--x-cmdpal-height"),
|
||||||
|
MaxPixelWidth = GetInt("--x-cmdpal-maxwidth"),
|
||||||
|
MaxPixelHeight = GetInt("--x-cmdpal-maxheight"),
|
||||||
|
DownscaleOnly = GetBool("--x-cmdpal-downscaleOnly") ?? (GetBool("--x-cmdpal-upscale") is bool u ? !u : (bool?)null),
|
||||||
|
FitMode = dict.TryGetValue("--x-cmdpal-fit", out var f) ? f : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
int? GetInt(params string[] keys)
|
||||||
|
{
|
||||||
|
foreach (var k in keys)
|
||||||
|
{
|
||||||
|
if (dict.TryGetValue(k, out var v) && int.TryParse(v, out var n))
|
||||||
|
{
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool? GetBool(params string[] keys)
|
||||||
|
{
|
||||||
|
foreach (var k in keys)
|
||||||
|
{
|
||||||
|
if (dict.TryGetValue(k, out var v) && (v.Equals("true", StringComparison.OrdinalIgnoreCase) || v == "1"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (dict.TryGetValue(k, out var v2) && (v2.Equals("false", StringComparison.OrdinalIgnoreCase) || v2 == "0"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// 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.Globalization;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using CommunityToolkit.WinUI;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using Windows.Foundation;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new image source.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ImageSourceFactory
|
||||||
|
{
|
||||||
|
private static DispatcherQueue? _dispatcherQueue;
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed record ImagePayload(
|
||||||
|
IRandomAccessStream Stream,
|
||||||
|
string? ContentType,
|
||||||
|
Uri? SourceUri);
|
||||||
|
|
||||||
|
internal sealed class ImageDecodeOptions
|
||||||
|
{
|
||||||
|
public bool SniffContent { get; init; } = true;
|
||||||
|
|
||||||
|
public int? DecodePixelWidth { get; init; }
|
||||||
|
|
||||||
|
public int? DecodePixelHeight { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<ImageSource> CreateAsync(ImagePayload payload, ImageDecodeOptions? options = null)
|
||||||
|
{
|
||||||
|
options ??= new ImageDecodeOptions();
|
||||||
|
|
||||||
|
var isSvg =
|
||||||
|
IsSvgByHeaderOrUrl(payload.ContentType, payload.SourceUri) ||
|
||||||
|
(options.SniffContent && SniffSvg(payload.Stream));
|
||||||
|
|
||||||
|
payload.Stream.Seek(0);
|
||||||
|
|
||||||
|
return await _dispatcherQueue!.EnqueueAsync(async () =>
|
||||||
|
{
|
||||||
|
if (isSvg)
|
||||||
|
{
|
||||||
|
var size = GetSvgSize(payload.Stream.AsStreamForRead());
|
||||||
|
payload.Stream.Seek(0);
|
||||||
|
|
||||||
|
var svg = new SvgImageSource();
|
||||||
|
await svg.SetSourceAsync(payload.Stream);
|
||||||
|
svg.RasterizePixelWidth = size.Width;
|
||||||
|
svg.RasterizePixelHeight = size.Height;
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var bmp = new BitmapImage();
|
||||||
|
if (options.DecodePixelWidth is int w and > 0)
|
||||||
|
{
|
||||||
|
bmp.DecodePixelWidth = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.DecodePixelHeight is int h and > 0)
|
||||||
|
{
|
||||||
|
bmp.DecodePixelHeight = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
await bmp.SetSourceAsync(payload.Stream);
|
||||||
|
return (ImageSource)bmp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Size GetSvgSize(Stream stream)
|
||||||
|
{
|
||||||
|
// Parse the SVG string as an XML document
|
||||||
|
var svgDocument = XDocument.Load(stream);
|
||||||
|
|
||||||
|
// Get the root element of the document
|
||||||
|
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||||
|
var svgElement = svgDocument.Root;
|
||||||
|
|
||||||
|
// Get the height and width attributes of the root element
|
||||||
|
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||||
|
var heightAttribute = svgElement.Attribute("height");
|
||||||
|
#pragma warning restore CS8602 // Dereference of a possibly null reference.
|
||||||
|
var widthAttribute = svgElement.Attribute("width");
|
||||||
|
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||||
|
|
||||||
|
// Convert the attribute values to double
|
||||||
|
double.TryParse(heightAttribute?.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var height);
|
||||||
|
double.TryParse(widthAttribute?.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var width);
|
||||||
|
|
||||||
|
// Return the height and width as a tuple
|
||||||
|
return new(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSvgByHeaderOrUrl(string? contentType, Uri? uri)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(contentType) &&
|
||||||
|
contentType.StartsWith("image/svg+xml", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = uri?.ToString();
|
||||||
|
return !string.IsNullOrEmpty(s) && s.Contains(".svg", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool SniffSvg(IRandomAccessStream ras)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const int maxProbe = 1024;
|
||||||
|
ras.Seek(0);
|
||||||
|
var s = ras.AsStreamForRead();
|
||||||
|
var toRead = (int)Math.Min(ras.Size, maxProbe);
|
||||||
|
var buf = new byte[toRead];
|
||||||
|
var read = s.Read(buf, 0, toRead);
|
||||||
|
if (read <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var head = System.Text.Encoding.UTF8.GetString(buf, 0, read);
|
||||||
|
ras.Seek(0);
|
||||||
|
return head.Contains("<svg", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
ras.Seek(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
internal sealed record ImageSourceInfo(ImageSource ImageSource, ImageHints Hints);
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
internal sealed partial class LocalImageSourceProvider : IImageSourceProvider
|
||||||
|
{
|
||||||
|
private readonly ImageSourceFactory.ImageDecodeOptions _decodeOptions;
|
||||||
|
|
||||||
|
public LocalImageSourceProvider(ImageSourceFactory.ImageDecodeOptions? decodeOptions = null)
|
||||||
|
=> _decodeOptions = decodeOptions ?? new ImageSourceFactory.ImageDecodeOptions();
|
||||||
|
|
||||||
|
public bool ShouldUseThisProvider(string url)
|
||||||
|
{
|
||||||
|
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out var uri) && uri.IsAbsoluteUri)
|
||||||
|
{
|
||||||
|
var scheme = uri.Scheme.ToLowerInvariant();
|
||||||
|
return scheme is "file" or "ms-appx" or "ms-appdata";
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ImageSourceInfo> GetImageSource(string url)
|
||||||
|
{
|
||||||
|
if (!ShouldUseThisProvider(url))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Not a local URL/path (file, ms-appx, ms-appdata).", nameof(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Absolute URI?
|
||||||
|
if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && uri.IsAbsoluteUri)
|
||||||
|
{
|
||||||
|
var scheme = uri.Scheme.ToLowerInvariant();
|
||||||
|
|
||||||
|
var hints = ImageHints.ParseHintsFromUri(uri);
|
||||||
|
|
||||||
|
if (scheme is "ms-appx" or "ms-appdata")
|
||||||
|
{
|
||||||
|
// Load directly from the package/appdata URI
|
||||||
|
var rasRef = RandomAccessStreamReference.CreateFromUri(uri);
|
||||||
|
using var ras = await rasRef.OpenReadAsync();
|
||||||
|
var payload = new ImageSourceFactory.ImagePayload(ras, ImageContentTypeHelper.GuessFromPathOrUri(uri.AbsoluteUri), uri);
|
||||||
|
return new ImageSourceInfo(await ImageSourceFactory.CreateAsync(payload, _decodeOptions), hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scheme is "file")
|
||||||
|
{
|
||||||
|
var path = uri.LocalPath;
|
||||||
|
return new ImageSourceInfo(await FromFilePathAsync(path, uri, _decodeOptions), hints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("Unsupported local URL/path.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ImageSource> FromFilePathAsync(string path, Uri sourceUri, ImageSourceFactory.ImageDecodeOptions decodeOptions)
|
||||||
|
{
|
||||||
|
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 64 * 1024, useAsync: true);
|
||||||
|
using var mem = new InMemoryRandomAccessStream();
|
||||||
|
using var outStream = mem.AsStreamForWrite();
|
||||||
|
await fs.CopyToAsync(outStream).ConfigureAwait(true);
|
||||||
|
await outStream.FlushAsync().ConfigureAwait(true);
|
||||||
|
|
||||||
|
mem.Seek(0);
|
||||||
|
|
||||||
|
var payload = new ImageSourceFactory.ImagePayload(mem, ImageContentTypeHelper.GuessFromPathOrUri(path), sourceUri);
|
||||||
|
return await ImageSourceFactory.CreateAsync(payload, decodeOptions).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ImageContentTypeHelper
|
||||||
|
{
|
||||||
|
public static string? GuessFromPathOrUri(string? pathOrUri)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(pathOrUri))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get extension from path/uri
|
||||||
|
var ext = Path.GetExtension(pathOrUri);
|
||||||
|
if (string.IsNullOrEmpty(ext))
|
||||||
|
{
|
||||||
|
// try query-less URI path portion
|
||||||
|
if (Uri.TryCreate(pathOrUri, UriKind.RelativeOrAbsolute, out var u))
|
||||||
|
{
|
||||||
|
ext = Path.GetExtension(u.IsAbsoluteUri ? u.AbsolutePath : u.OriginalString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext = ext?.Trim().ToLowerInvariant();
|
||||||
|
return ext switch
|
||||||
|
{
|
||||||
|
".svg" => "image/svg+xml",
|
||||||
|
".png" => "image/png",
|
||||||
|
".jpg" => "image/jpeg",
|
||||||
|
".jpeg" => "image/jpeg",
|
||||||
|
".gif" => "image/gif",
|
||||||
|
".webp" => "image/webp",
|
||||||
|
".bmp" => "image/bmp",
|
||||||
|
".ico" => "image/x-icon",
|
||||||
|
".tif" => "image/tiff",
|
||||||
|
".tiff" => "image/tiff",
|
||||||
|
".avif" => "image/avif",
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using Windows.Foundation;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new image configured to behave well as an inline image in a RichTextBlock.
|
||||||
|
/// </summary>
|
||||||
|
internal static class RtbInlineImageFactory
|
||||||
|
{
|
||||||
|
public sealed class InlineImageOptions
|
||||||
|
{
|
||||||
|
public double? WidthDip { get; init; }
|
||||||
|
|
||||||
|
public double? HeightDip { get; init; }
|
||||||
|
|
||||||
|
public double? MaxWidthDip { get; init; }
|
||||||
|
|
||||||
|
public double? MaxHeightDip { get; init; }
|
||||||
|
|
||||||
|
public bool FitColumnWidth { get; init; } = true;
|
||||||
|
|
||||||
|
public bool DownscaleOnly { get; init; } = true;
|
||||||
|
|
||||||
|
public Stretch Stretch { get; init; } = Stretch.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Image Create(ImageSource source, InlineImageOptions? options = null)
|
||||||
|
{
|
||||||
|
options ??= new InlineImageOptions();
|
||||||
|
|
||||||
|
var img = new Image
|
||||||
|
{
|
||||||
|
Source = source,
|
||||||
|
Stretch = options.Stretch,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Track host RTB and subscribe once
|
||||||
|
RichTextBlock? rtb = null;
|
||||||
|
long paddingToken = 0;
|
||||||
|
var paddingSubscribed = false;
|
||||||
|
|
||||||
|
SizeChangedEventHandler? rtbSizeChangedHandler = null;
|
||||||
|
TypedEventHandler<XamlRoot, XamlRootChangedEventArgs>? xamlRootChangedHandler = null;
|
||||||
|
|
||||||
|
// If Source is replaced later, recompute
|
||||||
|
var sourceToken = img.RegisterPropertyChangedCallback(Image.SourceProperty!, (_, __) => Update());
|
||||||
|
|
||||||
|
img.Loaded += OnLoaded;
|
||||||
|
img.Unloaded += OnUnloaded;
|
||||||
|
img.ImageOpened += (_, __) => Update();
|
||||||
|
|
||||||
|
return img;
|
||||||
|
|
||||||
|
void OnLoaded(object? s, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// store image initial Width and Height if they are force upon the image by
|
||||||
|
// MarkdownControl itself as result of parsing <img width="123" height="123" />
|
||||||
|
// If user sets Width/Height in options, that takes precedence
|
||||||
|
img.Tag ??= (img.Width, img.Height);
|
||||||
|
|
||||||
|
rtb ??= FindAncestor<RichTextBlock>(img);
|
||||||
|
if (rtb != null && !paddingSubscribed)
|
||||||
|
{
|
||||||
|
rtbSizeChangedHandler ??= (_, __) => Update();
|
||||||
|
rtb.SizeChanged += rtbSizeChangedHandler;
|
||||||
|
|
||||||
|
paddingToken = rtb.RegisterPropertyChangedCallback(Control.PaddingProperty!, (_, __) => Update());
|
||||||
|
paddingSubscribed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.XamlRoot != null)
|
||||||
|
{
|
||||||
|
xamlRootChangedHandler ??= (_, __) => Update();
|
||||||
|
img.XamlRoot.Changed += xamlRootChangedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnUnloaded(object? s, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (rtb != null && rtbSizeChangedHandler is not null)
|
||||||
|
{
|
||||||
|
rtb.SizeChanged -= rtbSizeChangedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtb != null && paddingSubscribed)
|
||||||
|
{
|
||||||
|
rtb.UnregisterPropertyChangedCallback(Control.PaddingProperty!, paddingToken);
|
||||||
|
paddingSubscribed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.XamlRoot != null && xamlRootChangedHandler is not null)
|
||||||
|
{
|
||||||
|
img.XamlRoot.Changed -= xamlRootChangedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.UnregisterPropertyChangedCallback(Image.SourceProperty!, sourceToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (rtb is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double? externalWidth = null;
|
||||||
|
double? externalHeight = null;
|
||||||
|
if (img.Tag != null)
|
||||||
|
{
|
||||||
|
(externalWidth, externalHeight) = ((double, double))img.Tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad = rtb.Padding;
|
||||||
|
var columnDip = Math.Max(0.0, rtb.ActualWidth - pad.Left - pad.Right);
|
||||||
|
var scale = img.XamlRoot?.RasterizationScale is double s1 and > 0 ? s1 : 1.0;
|
||||||
|
|
||||||
|
var isSvg = img.Source is SvgImageSource;
|
||||||
|
var naturalPxW = GetNaturalPixelWidth(img.Source);
|
||||||
|
var naturalDipW = naturalPxW > 0 && naturalPxW != int.MaxValue ? naturalPxW / scale : double.PositiveInfinity; // SVG => ∞
|
||||||
|
|
||||||
|
double? desiredWidth = null;
|
||||||
|
if (externalWidth.HasValue && !double.IsNaN(externalWidth.Value))
|
||||||
|
{
|
||||||
|
img.Width = externalWidth.Value;
|
||||||
|
desiredWidth = externalWidth.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (options.WidthDip is double forcedW)
|
||||||
|
{
|
||||||
|
desiredWidth = options.DownscaleOnly && naturalPxW != int.MaxValue
|
||||||
|
? Math.Min(forcedW, naturalPxW)
|
||||||
|
: forcedW;
|
||||||
|
}
|
||||||
|
else if (options.FitColumnWidth)
|
||||||
|
{
|
||||||
|
desiredWidth = options.DownscaleOnly && naturalPxW != int.MaxValue
|
||||||
|
? Math.Min(columnDip, naturalPxW)
|
||||||
|
: columnDip;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
desiredWidth = naturalPxW;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply MaxWidth (never exceed column width by default)
|
||||||
|
double maxW;
|
||||||
|
var maxConstraint = options.FitColumnWidth ? columnDip : (isSvg ? 256 : double.PositiveInfinity);
|
||||||
|
if (options.MaxWidthDip.HasValue)
|
||||||
|
{
|
||||||
|
maxW = Math.Min(options.MaxWidthDip.Value, maxConstraint);
|
||||||
|
}
|
||||||
|
else if (options.DownscaleOnly)
|
||||||
|
{
|
||||||
|
maxW = Math.Min(naturalPxW, maxConstraint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maxW = maxConstraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit sizes
|
||||||
|
if (desiredWidth.HasValue)
|
||||||
|
{
|
||||||
|
img.Width = Math.Max(0, desiredWidth.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
img.MaxWidth = maxW > 0 ? maxW : maxConstraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (externalHeight.HasValue && !double.IsNaN(externalHeight.Value))
|
||||||
|
{
|
||||||
|
img.Height = externalHeight.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ---- Height & MaxHeight ----
|
||||||
|
var desiredHeight = options.HeightDip;
|
||||||
|
var maxH = options.MaxHeightDip;
|
||||||
|
|
||||||
|
if (desiredHeight is double h)
|
||||||
|
{
|
||||||
|
img.Height = Math.Max(0, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxH is double mh && mh > 0)
|
||||||
|
{
|
||||||
|
img.MaxHeight = mh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.FitColumnWidth
|
||||||
|
|| options.WidthDip is not null
|
||||||
|
|| options.HeightDip is not null
|
||||||
|
|| options.MaxWidthDip is not null
|
||||||
|
|| options.MaxHeightDip is not null
|
||||||
|
|| externalWidth.HasValue
|
||||||
|
|| externalHeight.HasValue)
|
||||||
|
{
|
||||||
|
img.Stretch = Stretch.Uniform;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
img.Stretch = Stretch.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode/rasterization hints
|
||||||
|
if (isSvg && img.Source is SvgImageSource svg)
|
||||||
|
{
|
||||||
|
var targetW = desiredWidth ?? Math.Min(img.MaxWidth, columnDip);
|
||||||
|
var pxW = Math.Max(1, (int)Math.Round(targetW * scale));
|
||||||
|
if ((int)svg.RasterizePixelWidth != pxW)
|
||||||
|
{
|
||||||
|
svg.RasterizePixelWidth = pxW;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.HeightDip is double forcedH)
|
||||||
|
{
|
||||||
|
var pxH = Math.Max(1, (int)Math.Round(forcedH * scale));
|
||||||
|
if ((int)svg.RasterizePixelHeight != pxH)
|
||||||
|
{
|
||||||
|
svg.RasterizePixelHeight = pxH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (img.Source is BitmapImage bi && naturalPxW > 0)
|
||||||
|
{
|
||||||
|
var widthToUse = desiredWidth ?? Math.Min(img.MaxWidth, columnDip);
|
||||||
|
if (widthToUse > 0)
|
||||||
|
{
|
||||||
|
var desiredPx = (int)Math.Round(Math.Min(naturalPxW, widthToUse * scale));
|
||||||
|
if (desiredPx > 0 && bi.DecodePixelWidth != desiredPx)
|
||||||
|
{
|
||||||
|
bi.DecodePixelWidth = desiredPx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetNaturalPixelWidth(ImageSource? src) => src switch
|
||||||
|
{
|
||||||
|
BitmapSource bs when bs.PixelWidth > 0 => bs.PixelWidth, // raster
|
||||||
|
SvgImageSource sis => sis.RasterizePixelWidth > 0 ? (int)sis.RasterizePixelWidth : int.MaxValue, // vector => infinite
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static T? FindAncestor<T>(DependencyObject start)
|
||||||
|
where T : DependencyObject
|
||||||
|
{
|
||||||
|
var cur = (DependencyObject?)start;
|
||||||
|
while (cur != null)
|
||||||
|
{
|
||||||
|
cur = VisualTreeHelper.GetParent(cur);
|
||||||
|
if (cur is T hit)
|
||||||
|
{
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,10 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using ManagedCommon;
|
|
||||||
using Microsoft.CmdPal.UI.Events;
|
|
||||||
using Microsoft.CommandPalette.UI.Controls;
|
using Microsoft.CommandPalette.UI.Controls;
|
||||||
using Microsoft.CommandPalette.UI.Helpers;
|
using Microsoft.CommandPalette.UI.Helpers;
|
||||||
using Microsoft.CommandPalette.UI.Models;
|
using Microsoft.CommandPalette.UI.Models;
|
||||||
|
using Microsoft.CommandPalette.UI.Models.Events;
|
||||||
using Microsoft.CommandPalette.UI.Models.Messages;
|
using Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
using Microsoft.CommandPalette.UI.Pages;
|
using Microsoft.CommandPalette.UI.Pages;
|
||||||
using Microsoft.CommandPalette.UI.ViewModels.Helpers;
|
using Microsoft.CommandPalette.UI.ViewModels.Helpers;
|
||||||
@@ -230,7 +229,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newRect = EnsureWindowIsVisible(savedPosition.ToPhysicalWindowRectangle(), new SizeInt32(savedPosition.ScreenWidth, savedPosition.ScreenHeight), savedPosition.Dpi);
|
var newRect = EnsureWindowIsVisible(savedPosition.ToPhysicalWindowRectangle(), new SizeInt32(savedPosition.ScreenWidth, savedPosition.ScreenHeight), savedPosition.Dpi, logger);
|
||||||
AppWindow.MoveAndResize(newRect);
|
AppWindow.MoveAndResize(newRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,14 +342,13 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
if (PInvoke.IsIconic(hwnd))
|
if (PInvoke.IsIconic(hwnd))
|
||||||
{
|
{
|
||||||
// Make sure our HWND is cloaked before any possible window manipulations
|
// Make sure our HWND is cloaked before any possible window manipulations
|
||||||
Cloak();
|
// Cloak();
|
||||||
|
|
||||||
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
|
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target == MonitorBehavior.ToLast)
|
if (target == MonitorBehavior.ToLast)
|
||||||
{
|
{
|
||||||
var newRect = EnsureWindowIsVisible(_currentWindowPosition.ToPhysicalWindowRectangle(), new SizeInt32(_currentWindowPosition.ScreenWidth, _currentWindowPosition.ScreenHeight), _currentWindowPosition.Dpi);
|
var newRect = EnsureWindowIsVisible(_currentWindowPosition.ToPhysicalWindowRectangle(), new SizeInt32(_currentWindowPosition.ScreenWidth, _currentWindowPosition.ScreenHeight), _currentWindowPosition.Dpi, logger);
|
||||||
AppWindow.MoveAndResize(newRect);
|
AppWindow.MoveAndResize(newRect);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -390,7 +388,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
/// A window rectangle in physical pixels, moved to the nearest display and resized
|
/// A window rectangle in physical pixels, moved to the nearest display and resized
|
||||||
/// if the DPI has changed.
|
/// if the DPI has changed.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
private static RectInt32 EnsureWindowIsVisible(RectInt32 windowRect, SizeInt32 originalScreen, int originalDpi)
|
private static RectInt32 EnsureWindowIsVisible(RectInt32 windowRect, SizeInt32 originalScreen, int originalDpi, ILogger logger)
|
||||||
{
|
{
|
||||||
var displayArea = DisplayArea.GetFromRect(windowRect, DisplayAreaFallback.Nearest);
|
var displayArea = DisplayArea.GetFromRect(windowRect, DisplayAreaFallback.Nearest);
|
||||||
if (displayArea is null)
|
if (displayArea is null)
|
||||||
@@ -405,7 +403,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
return windowRect;
|
return windowRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
var effectiveDpi = GetEffectiveDpiFromDisplayId(displayArea);
|
var effectiveDpi = GetEffectiveDpiFromDisplayId(displayArea, logger);
|
||||||
if (originalDpi <= 0)
|
if (originalDpi <= 0)
|
||||||
{
|
{
|
||||||
originalDpi = effectiveDpi; // use current DPI as baseline (no scaling adjustment needed)
|
originalDpi = effectiveDpi; // use current DPI as baseline (no scaling adjustment needed)
|
||||||
@@ -462,7 +460,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
return new RectInt32(targetX, targetY, targetWidth, targetHeight);
|
return new RectInt32(targetX, targetY, targetWidth, targetHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetEffectiveDpiFromDisplayId(DisplayArea displayArea)
|
private static int GetEffectiveDpiFromDisplayId(DisplayArea displayArea, ILogger logger)
|
||||||
{
|
{
|
||||||
var effectiveDpi = 96;
|
var effectiveDpi = 96;
|
||||||
|
|
||||||
@@ -476,7 +474,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"GetDpiForMonitor failed with HRESULT: 0x{hr.Value:X8} on display {displayArea.DisplayId}");
|
Log_GetDpiForMonitorFailed(logger, hr, displayArea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -935,7 +933,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
{
|
{
|
||||||
// ... then manually hide our window. When debugged, we won't get the cool cloaking,
|
// ... then manually hide our window. When debugged, we won't get the cool cloaking,
|
||||||
// but that's the price to pay for having the HWND not light-dismiss while we're debugging.
|
// but that's the price to pay for having the HWND not light-dismiss while we're debugging.
|
||||||
Cloak();
|
// Cloak();
|
||||||
this.Hide();
|
this.Hide();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -993,7 +991,7 @@ public sealed partial class MainWindow : WindowEx,
|
|||||||
partial void Log_ExternalReloadDisabled();
|
partial void Log_ExternalReloadDisabled();
|
||||||
|
|
||||||
[LoggerMessage(level: LogLevel.Warning, Message = "GetDpiForMonitor failed with HRESULT: 0x{hr.Value:X8} on display {DisplayArea.DisplayId}")]
|
[LoggerMessage(level: LogLevel.Warning, Message = "GetDpiForMonitor failed with HRESULT: 0x{hr.Value:X8} on display {DisplayArea.DisplayId}")]
|
||||||
partial void LogWarning_GetDpiForMonitorFailed(HRESULT hr, DisplayArea displayArea);
|
static partial void Log_GetDpiForMonitorFailed(ILogger logger, HRESULT hr, DisplayArea displayArea);
|
||||||
|
|
||||||
[LoggerMessage(level: LogLevel.Warning, Message = "DWM cloaking of the main window failed. HRESULT: {hr.Value}.")]
|
[LoggerMessage(level: LogLevel.Warning, Message = "DWM cloaking of the main window failed. HRESULT: {hr.Value}.")]
|
||||||
partial void Log_DwmCloakingFailed(HRESULT hr);
|
partial void Log_DwmCloakingFailed(HRESULT hr);
|
||||||
|
|||||||
@@ -164,14 +164,12 @@
|
|||||||
<ProjectReference Include="..\Microsoft.CommandPalette.UI.Models\Microsoft.CommandPalette.UI.Models.csproj" />
|
<ProjectReference Include="..\Microsoft.CommandPalette.UI.Models\Microsoft.CommandPalette.UI.Models.csproj" />
|
||||||
<ProjectReference Include="..\Microsoft.CommandPalette.UI.ViewModels\Microsoft.CommandPalette.UI.ViewModels.csproj" />
|
<ProjectReference Include="..\Microsoft.CommandPalette.UI.ViewModels\Microsoft.CommandPalette.UI.ViewModels.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Update="ToastWindow.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="Pages\ShellPage.xaml">
|
<Page Update="Pages\ShellPage.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Selectors\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Page
|
||||||
|
x:Class="Microsoft.CommandPalette.UI.Pages.LoadingPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<ProgressRing
|
||||||
|
Width="36"
|
||||||
|
Height="36"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsIndeterminate="True" />
|
||||||
|
<TextBlock Text="Here we go"/>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.CommandPalette.UI.ViewModels;
|
||||||
|
using Microsoft.CommandPalette.ViewModels;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.UI.Pages;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// We use this page to do initialization of our extensions and cache loading to hydrate our ViewModels.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class LoadingPage : Page
|
||||||
|
{
|
||||||
|
private readonly DispatcherQueue _queue = DispatcherQueue.GetForCurrentThread();
|
||||||
|
|
||||||
|
public LoadingPage()
|
||||||
|
{
|
||||||
|
this.InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Parameter is not AsyncNavigationRequest request)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Invalid navigation parameter: {nameof(e.Parameter)} must be {nameof(AsyncNavigationRequest)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.TargetViewModel is not ShellViewModel shellVM)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Invalid navigation target: AsyncNavigationRequest.{nameof(AsyncNavigationRequest.TargetViewModel)} must be {nameof(ShellViewModel)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will load the built-in commands, then navigate to the main page.
|
||||||
|
// Once the mainpage loads, we'll start loading extensions.
|
||||||
|
shellVM.LoadCommand.Execute(null);
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await shellVM.LoadCommand.ExecutionTask!;
|
||||||
|
|
||||||
|
if (shellVM.LoadCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
|
||||||
|
{
|
||||||
|
// TODO: Handle failure case
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
base.OnNavigatedTo(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,79 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<Page
|
<Page
|
||||||
x:Class="Microsoft.CommandPalette.UI.Pages.ShellPage"
|
x:Class="Microsoft.CommandPalette.UI.Pages.ShellPage"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
|
|
||||||
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:Microsoft.CommandPalette.UI.Pages"
|
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
|
||||||
|
xmlns:cmdpalUI="using:Microsoft.CommandPalette.UI"
|
||||||
|
xmlns:cmdpalConverters="using:Microsoft.CommandPalette.UI.Converters"
|
||||||
|
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
||||||
|
xmlns:cpcontrols="using:Microsoft.CommandPalette.UI.Controls"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:help="using:Microsoft.CommandPalette.UI.Helpers"
|
||||||
|
xmlns:markdownImageProviders="using:Microsoft.CommandPalette.UI.Helpers.MarkdownImageProviders"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||||
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
|
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
Background="Transparent"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
<!--xmlns:labToolkit="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"-->
|
||||||
|
|
||||||
<Page.Resources>
|
<Page.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
|
<converters:StringVisibilityConverter
|
||||||
|
x:Key="StringNotEmptyToVisibilityConverter"
|
||||||
|
EmptyValue="Collapsed"
|
||||||
|
NotEmptyValue="Visible" />
|
||||||
|
|
||||||
|
<cmdpalConverters:MessageStateToSeverityConverter x:Key="MessageStateToSeverityConverter" />
|
||||||
|
|
||||||
|
<!--<selectors:DetailsDataTemplateSelector
|
||||||
|
x:Key="DetailsDataTemplateSelector"
|
||||||
|
CommandTemplate="{StaticResource DetailsCommandsTemplate}"
|
||||||
|
LinkTemplate="{StaticResource DetailsLinkTemplate}"
|
||||||
|
SeparatorTemplate="{StaticResource DetailsSeparatorTemplate}"
|
||||||
|
TagTemplate="{StaticResource DetailsTagsTemplate}" />-->
|
||||||
|
|
||||||
<converters:BoolToVisibilityConverter
|
<converters:BoolToVisibilityConverter
|
||||||
x:Key="BoolToInvertedVisibilityConverter"
|
x:Key="BoolToInvertedVisibilityConverter"
|
||||||
FalseValue="Visible"
|
FalseValue="Visible"
|
||||||
TrueValue="Collapsed" />
|
TrueValue="Collapsed" />
|
||||||
|
|
||||||
|
<Style
|
||||||
|
x:Key="DetailKeyTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="IsTextSelectionEnabled" Value="True" />
|
||||||
|
<Setter Property="TextWrapping" Value="WrapWholeWords" />
|
||||||
|
<Setter Property="Foreground" Value="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style
|
||||||
|
x:Key="SeparatorKeyTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="IsTextSelectionEnabled" Value="True" />
|
||||||
|
<Setter Property="TextWrapping" Value="WrapWholeWords" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style
|
||||||
|
x:Key="DetailValueTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BodyTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="IsTextSelectionEnabled" Value="True" />
|
||||||
|
<Setter Property="TextWrapping" Value="WrapWholeWords" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<tkcontrols:MarkdownThemes
|
||||||
|
x:Key="DefaultMarkdownThemeConfig"
|
||||||
|
H3FontSize="12"
|
||||||
|
H3FontWeight="Normal" />
|
||||||
|
<!--<markdownImageProviders:ImageProvider x:Key="ImageProvider" />-->
|
||||||
|
<tkcontrols:MarkdownConfig
|
||||||
|
x:Key="DefaultMarkdownConfig"
|
||||||
|
ImageProvider="{StaticResource ImageProvider}"
|
||||||
|
Themes="{StaticResource DefaultMarkdownThemeConfig}" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Page.Resources>
|
</Page.Resources>
|
||||||
|
|
||||||
@@ -35,6 +90,340 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Back button and search box -->
|
||||||
|
<Grid
|
||||||
|
x:Name="TopBarGrid"
|
||||||
|
Padding="0,12,0,12"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="0,0,0,1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Back button -->
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image
|
||||||
|
Width="20"
|
||||||
|
Margin="20,0,6,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5"
|
||||||
|
AutomationProperties.AccessibilityView="Raw"
|
||||||
|
Source="ms-appx:///Assets/icon.svg">
|
||||||
|
<!--
|
||||||
|
Visibility="{x:Bind viewModel.CurrentPage.IsNested, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}"-->
|
||||||
|
<animations:Implicit.ShowAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
EasingMode="EaseIn"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="0"
|
||||||
|
To="1.0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
<animations:ScaleAnimation
|
||||||
|
EasingMode="EaseIn"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="0.5"
|
||||||
|
To="1"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
</animations:Implicit.ShowAnimations>
|
||||||
|
<animations:Implicit.HideAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
EasingMode="EaseOut"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="1.0"
|
||||||
|
To="0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
<animations:ScaleAnimation
|
||||||
|
EasingMode="EaseOut"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="1"
|
||||||
|
To="0.5"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
</animations:Implicit.HideAnimations>
|
||||||
|
</Image>
|
||||||
|
<!--<Button
|
||||||
|
x:Name="BackButton"
|
||||||
|
x:Uid="BackButton"
|
||||||
|
Margin="4,0,4,0"
|
||||||
|
Padding="4"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5"
|
||||||
|
Click="BackButton_Clicked"
|
||||||
|
Content="{ui:FontIcon Glyph=,
|
||||||
|
FontSize=14}"
|
||||||
|
FontSize="16"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}"
|
||||||
|
Visibility="{x:Bind viewModel.CurrentPage.IsNested, Mode=OneWay}">
|
||||||
|
<animations:Implicit.ShowAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
EasingMode="EaseIn"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="0"
|
||||||
|
To="1.0"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:ScaleAnimation
|
||||||
|
From="0.5"
|
||||||
|
To="1"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:TranslationAnimation
|
||||||
|
From="16,0,0"
|
||||||
|
To="0,0,0"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
</animations:Implicit.ShowAnimations>
|
||||||
|
<animations:Implicit.HideAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
EasingMode="EaseOut"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="1.0"
|
||||||
|
To="0"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:ScaleAnimation
|
||||||
|
EasingMode="EaseOut"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="1"
|
||||||
|
To="0.5"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:TranslationAnimation
|
||||||
|
EasingMode="EaseOut"
|
||||||
|
EasingType="Cubic"
|
||||||
|
From="0,0,0"
|
||||||
|
To="16,0,0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
</animations:Implicit.HideAnimations>
|
||||||
|
</Button>-->
|
||||||
|
<!--<cpcontrols:IconBox
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="20"
|
||||||
|
Margin="4,0,4,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
SourceKey="{x:Bind viewModel.CurrentPage.Icon, Mode=OneWay}"
|
||||||
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}"
|
||||||
|
Visibility="{x:Bind viewModel.CurrentPage.IsNested, Mode=OneWay}">
|
||||||
|
<animations:Implicit.ShowAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
From="0"
|
||||||
|
To="1.0"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:ScaleAnimation
|
||||||
|
From="0.8"
|
||||||
|
To="1"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:TranslationAnimation
|
||||||
|
From="8,0,0"
|
||||||
|
To="0,0,0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
</animations:Implicit.ShowAnimations>
|
||||||
|
<animations:Implicit.HideAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
From="1.0"
|
||||||
|
To="0"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:ScaleAnimation
|
||||||
|
From="1"
|
||||||
|
To="0.8"
|
||||||
|
Duration="0:0:0.333" />
|
||||||
|
<animations:TranslationAnimation
|
||||||
|
From="0,0,0"
|
||||||
|
To="8,0,0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
</animations:Implicit.HideAnimations>
|
||||||
|
</cpcontrols:IconBox>-->
|
||||||
|
</StackPanel>
|
||||||
|
<!-- Search box: wrapped in a grid to enable RepositionThemeTransitions -->
|
||||||
|
<Grid Grid.Column="1" HorizontalAlignment="Stretch">
|
||||||
|
<cpcontrols:SearchBar
|
||||||
|
x:Name="SearchBox"
|
||||||
|
HorizontalAlignment="Stretch" />
|
||||||
|
<!--CurrentPageviewModel="{x:Bind viewModel.CurrentPage, Mode=OneWay}" />-->
|
||||||
|
<Grid.Transitions>
|
||||||
|
<TransitionCollection>
|
||||||
|
<RepositionThemeTransition />
|
||||||
|
</TransitionCollection>
|
||||||
|
</Grid.Transitions>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid x:Name="ContentGrid" Grid.Row="1">
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="3*" />
|
||||||
|
<ColumnDefinition x:Name="DetailsColumn" Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Frame
|
||||||
|
Name="RootFrame"
|
||||||
|
AutomationProperties.AutomationControlType="Pane"
|
||||||
|
AutomationProperties.LandmarkType="Navigation"
|
||||||
|
AutomationProperties.LiveSetting="Assertive"
|
||||||
|
IsNavigationStackEnabled="True" />
|
||||||
|
|
||||||
|
<ScrollViewer
|
||||||
|
x:Name="DetailsContent"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="4"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5"
|
||||||
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
|
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<animations:Implicit.ShowAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
From="0"
|
||||||
|
To="1.0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
<animations:TranslationAnimation
|
||||||
|
From="24,0,0"
|
||||||
|
To="0,0,0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
</animations:Implicit.ShowAnimations>
|
||||||
|
<animations:Implicit.HideAnimations>
|
||||||
|
<animations:OpacityAnimation
|
||||||
|
From="1.0"
|
||||||
|
To="0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
<animations:TranslationAnimation
|
||||||
|
From="0,0,0"
|
||||||
|
To="24,0,0"
|
||||||
|
Duration="0:0:0.187" />
|
||||||
|
</animations:Implicit.HideAnimations>
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!--<cpcontrols:IconBox
|
||||||
|
x:Name="HeroImageBorder"
|
||||||
|
Width="64"
|
||||||
|
Margin="0,0,16,16"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
AutomationProperties.AccessibilityView="Raw"
|
||||||
|
SourceKey="{x:Bind viewModel.Details.HeroImage, Mode=OneWay}"
|
||||||
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}"
|
||||||
|
Visibility="{x:Bind HasHeroImage, Mode=OneWay}" />-->
|
||||||
|
|
||||||
|
<!--<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
FontSize="18"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="{x:Bind viewModel.Details.Title, Mode=OneWay}"
|
||||||
|
TextWrapping="WrapWholeWords"
|
||||||
|
Visibility="{x:Bind viewModel.Details.Title, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}" />
|
||||||
|
|
||||||
|
<tkcontrols:MarkdownTextBlock
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="0,4,0,24"
|
||||||
|
Background="Transparent"
|
||||||
|
Config="{StaticResource DefaultMarkdownConfig}"
|
||||||
|
Text="{x:Bind viewModel.Details.Body, Mode=OneWay}"
|
||||||
|
UseEmphasisExtras="True"
|
||||||
|
UsePipeTables="True" />
|
||||||
|
|
||||||
|
<ItemsRepeater
|
||||||
|
Grid.Row="3"
|
||||||
|
ItemTemplate="{StaticResource DetailsDataTemplateSelector}"
|
||||||
|
ItemsSource="{x:Bind viewModel.Details.Metadata, Mode=OneWay}">
|
||||||
|
<ItemsRepeater.Layout>
|
||||||
|
<StackLayout Spacing="12" />
|
||||||
|
</ItemsRepeater.Layout>
|
||||||
|
</ItemsRepeater>-->
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
<!-- /DetailsContent -->
|
||||||
|
</Grid>
|
||||||
|
<!--<ScrollView
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
MaxHeight="120"
|
||||||
|
Background="{ThemeResource SystemControlErrorBackgroundColor}"
|
||||||
|
BorderBrush="{ThemeResource SystemControlErrorTextForegroundBrush}"
|
||||||
|
BorderThickness="0,1,0,1"
|
||||||
|
CornerRadius="0"
|
||||||
|
Visibility="{x:Bind viewModel.CurrentPage.ErrorMessage, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}">
|
||||||
|
<TextBlock Margin="16,8,16,16" IsTextSelectionEnabled="True">
|
||||||
|
<Run
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{ThemeResource SystemErrorTextColor}"
|
||||||
|
Text="Error(s) on page:" />
|
||||||
|
<LineBreak /><Run Text="{x:Bind viewModel.CurrentPage.ErrorMessage, Mode=OneWay}" />
|
||||||
|
</TextBlock>
|
||||||
|
</ScrollView>-->
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Horrifying: You may ask yourself - why is there a Background on this InfoBar?
|
||||||
|
|
||||||
|
Well, as it turns out, the Informational InfoBar has a transparent
|
||||||
|
background. It just cannot be bothered. So, we need to manually give
|
||||||
|
it one to actually obscure the text beneath it. And you can't just give
|
||||||
|
the InfoBar itself a Background, because then the other Severity's
|
||||||
|
won't get colorized.
|
||||||
|
|
||||||
|
See https://github.com/microsoft/microsoft-ui-xaml/issues/5741
|
||||||
|
-->
|
||||||
|
<!--<StackPanel
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="16,8,16,8"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||||
|
CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||||
|
<InfoBar
|
||||||
|
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||||
|
IsOpen="{x:Bind viewModel.CurrentPage.HasStatusMessage, Mode=OneWay}"
|
||||||
|
Message="{x:Bind viewModel.CurrentPage.MostRecentStatusMessage.Message, Mode=OneWay}"
|
||||||
|
Severity="{x:Bind viewModel.CurrentPage.MostRecentStatusMessage.State, Mode=OneWay, Converter={StaticResource MessageStateToSeverityConverter}}">
|
||||||
|
|
||||||
|
<InfoBar.Content>
|
||||||
|
<ProgressBar
|
||||||
|
Margin="0,-20,0,0"
|
||||||
|
IsIndeterminate="{x:Bind viewModel.CurrentPage.MostRecentStatusMessage.Progress.IsIndeterminate, Mode=OneWay}"
|
||||||
|
Visibility="{x:Bind viewModel.CurrentPage.MostRecentStatusMessage.HasProgress, Mode=OneWay}"
|
||||||
|
Value="{x:Bind viewModel.CurrentPage.MostRecentStatusMessage.Progress.ProgressPercent, Mode=OneWay}" />
|
||||||
|
--><!-- Margin="0,0,0,6" MaxWidth="200"/> --><!--
|
||||||
|
</InfoBar.Content>
|
||||||
|
</InfoBar>
|
||||||
|
</StackPanel>-->
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
Background="{ThemeResource LayerOnAcrylicSecondaryBackgroundBrush}"
|
||||||
|
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="0,1,0,0">
|
||||||
|
<cpcontrols:CommandBar />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup>
|
||||||
|
<!--
|
||||||
|
Why disable it and make it transparent?
|
||||||
|
To prevent the container from collapsing, ensuring the layout metrics remain unchanged.
|
||||||
|
-->
|
||||||
|
<VisualStateGroup.Transitions>
|
||||||
|
<VisualTransition GeneratedDuration="0:0:0.187" To="SearchBarCollapsed" />
|
||||||
|
</VisualStateGroup.Transitions>
|
||||||
|
<VisualState x:Name="SearchBarVisible" />
|
||||||
|
<!--<VisualState x:Name="SearchBarCollapsed">
|
||||||
|
<VisualState.StateTriggers>
|
||||||
|
<StateTrigger IsActive="{x:Bind h:BindTransformers.Negate(viewModel.IsSearchBoxVisible), Mode=OneWay}" />
|
||||||
|
</VisualState.StateTriggers>
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="SearchBox.IsEnabled" Value="False" />
|
||||||
|
<Setter Target="SearchBox.Opacity" Value="0" />
|
||||||
|
<Setter Target="TopBarGrid.BorderBrush" Value="Transparent" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>-->
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
@@ -2,38 +2,129 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Microsoft.CommandPalette.UI.Helpers;
|
||||||
|
using Microsoft.CommandPalette.UI.Models;
|
||||||
|
using Microsoft.CommandPalette.UI.Models.Messages;
|
||||||
|
using Microsoft.CommandPalette.UI.ViewModels;
|
||||||
using Microsoft.CommandPalette.ViewModels;
|
using Microsoft.CommandPalette.ViewModels;
|
||||||
using Windows.System;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.Pages;
|
namespace Microsoft.CommandPalette.UI.Pages;
|
||||||
|
|
||||||
public sealed partial class ShellPage : Page
|
public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page
|
||||||
{
|
{
|
||||||
private readonly ShellViewModel viewModel;
|
private readonly ShellViewModel viewModel;
|
||||||
|
private readonly SettingsModel _settingsModel;
|
||||||
|
private readonly ILogger logger;
|
||||||
private readonly DispatcherQueue _queue = DispatcherQueue.GetForCurrentThread();
|
private readonly DispatcherQueue _queue = DispatcherQueue.GetForCurrentThread();
|
||||||
private readonly DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
private readonly Microsoft.UI.Dispatching.DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||||
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||||
|
|
||||||
private readonly SlideNavigationTransitionInfo _slideRightTransition = new() { Effect = SlideNavigationTransitionEffect.FromRight };
|
private readonly SlideNavigationTransitionInfo _slideRightTransition = new() { Effect = SlideNavigationTransitionEffect.FromRight };
|
||||||
private readonly SuppressNavigationTransitionInfo _noAnimation = new();
|
private readonly SuppressNavigationTransitionInfo _noAnimation = new();
|
||||||
|
|
||||||
private readonly ToastWindow _toast = new();
|
|
||||||
|
|
||||||
private readonly CompositeFormat _pageNavigatedAnnouncement;
|
private readonly CompositeFormat _pageNavigatedAnnouncement;
|
||||||
|
|
||||||
|
public ShellPage(ShellViewModel viewModel, SettingsModel settingsModel, ILogger logger)
|
||||||
private CancellationTokenSource? _focusAfterLoadedCts;
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
private WeakReference<Page>? _lastNavigatedPageRef;
|
|
||||||
private SettingsWindow? _settingsWindow;
|
|
||||||
|
|
||||||
public ShellPage(ShellViewModel viewModel)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
this.viewModel = viewModel;
|
this.viewModel = viewModel;
|
||||||
|
_settingsModel = settingsModel;
|
||||||
|
this.logger = logger;
|
||||||
|
|
||||||
|
// AddHandler(PreviewKeyDownEvent, new KeyEventHandler(ShellPage_OnPreviewKeyDown), true);
|
||||||
|
// AddHandler(KeyDownEvent, new KeyEventHandler(ShellPage_OnKeyDown), false);
|
||||||
|
// AddHandler(PointerPressedEvent, new PointerEventHandler(ShellPage_OnPointerPressed), true);
|
||||||
|
RootFrame.Navigate(typeof(LoadingPage), new AsyncNavigationRequest(viewModel, CancellationToken.None));
|
||||||
|
|
||||||
|
var pageAnnouncementFormat = ResourceLoaderInstance.GetString("ScreenReader_Announcement_NavigatedToPage0");
|
||||||
|
_pageNavigatedAnnouncement = CompositeFormat.Parse(pageAnnouncementFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default page animation, depending on the settings
|
||||||
|
/// </summary>
|
||||||
|
private NavigationTransitionInfo DefaultPageAnimation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _settingsModel.DisableAnimations ? _noAnimation : _slideRightTransition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SummonOnUiThread(HotkeySummonMessage message)
|
||||||
|
{
|
||||||
|
var commandId = message.CommandId;
|
||||||
|
var isRoot = string.IsNullOrEmpty(commandId);
|
||||||
|
if (isRoot)
|
||||||
|
{
|
||||||
|
// If this is the hotkey for the root level, then always show us
|
||||||
|
WeakReferenceMessenger.Default.Send<ShowWindowMessage>(new(message.Hwnd));
|
||||||
|
|
||||||
|
// Depending on the settings, either
|
||||||
|
// * Go home, or
|
||||||
|
// * Select the search text (if we should remain open on this page)
|
||||||
|
if (_settingsModel.AutoGoHomeInterval == TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
// GoHome(false);
|
||||||
|
}
|
||||||
|
else if (_settingsModel.HighlightSearchOnActivate)
|
||||||
|
{
|
||||||
|
// SearchBox.SelectSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// // For a hotkey bound to a command, first lookup the
|
||||||
|
// // command from our list of toplevel commands.
|
||||||
|
// var tlcManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||||
|
// var topLevelCommand = tlcManager.LookupCommand(commandId);
|
||||||
|
// if (topLevelCommand is not null)
|
||||||
|
// {
|
||||||
|
// var command = topLevelCommand.CommandViewModel.Model.Unsafe;
|
||||||
|
// var isPage = command is not IInvokableCommand;
|
||||||
|
// // If the bound command is an invokable command, then
|
||||||
|
// // we don't want to open the window at all - we want to
|
||||||
|
// // just do it.
|
||||||
|
// if (isPage)
|
||||||
|
// {
|
||||||
|
// // If we're here, then the bound command was a page
|
||||||
|
// // of some kind. Let's pop the stack, show the window, and navigate to it.
|
||||||
|
// GoHome(false);
|
||||||
|
// WeakReferenceMessenger.Default.Send<ShowWindowMessage>(new(message.Hwnd));
|
||||||
|
// }
|
||||||
|
// var msg = topLevelCommand.GetPerformCommandMessage();
|
||||||
|
// msg.WithAnimation = false;
|
||||||
|
// WeakReferenceMessenger.Default.Send<PerformCommandMessage>(msg);
|
||||||
|
// // we can't necessarily SelectSearch() here, because when the page is loaded,
|
||||||
|
// // we'll fetch the SearchText from the page itself, and that'll stomp the
|
||||||
|
// // selection we start now.
|
||||||
|
// // That's probably okay though.
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// catch
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
WeakReferenceMessenger.Default.Send<FocusSearchBoxMessage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BackButton_Clicked(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) => WeakReferenceMessenger.Default.Send<NavigateBackMessage>(new());
|
||||||
|
|
||||||
|
[LoggerMessage(Level = LogLevel.Error)]
|
||||||
|
partial void Log_ShowConfirmationMessageError(Exception ex);
|
||||||
|
|
||||||
|
[LoggerMessage(Level = LogLevel.Warning, Message = "Invalid navigation target: AsyncNavigationRequest.{targetViewModel} must be {pageViewModel}")]
|
||||||
|
partial void Log_InvalidNavigationTarget(string targetViewModel, string pageViewModel);
|
||||||
|
|
||||||
|
[LoggerMessage(Level = LogLevel.Warning, Message = "Unrecognized target for shell navigation: {parameter}")]
|
||||||
|
partial void Log_UnrecognizedNavigationTarget(NavigationEventArgs parameter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using Microsoft.CmdPal.UI.Events;
|
using Microsoft.CommandPalette.UI.Models.Events;
|
||||||
using Microsoft.CommandPalette.UI.Services;
|
using Microsoft.CommandPalette.UI.Services;
|
||||||
using Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
using Windows.Win32;
|
using Windows.Win32;
|
||||||
@@ -38,7 +38,7 @@ internal sealed partial class Program
|
|||||||
}
|
}
|
||||||
|
|
||||||
Log_AppStart(logger, DateTime.UtcNow);
|
Log_AppStart(logger, DateTime.UtcNow);
|
||||||
TelemetryService.WriteEvent(new ProcessStartedEvent());
|
PowerToysTelemetry.Log.WriteEvent(new ProcessStartedEvent());
|
||||||
|
|
||||||
WinRT.ComWrappersSupport.InitializeComWrappers();
|
WinRT.ComWrappersSupport.InitializeComWrappers();
|
||||||
var isRedirect = DecideRedirection();
|
var isRedirect = DecideRedirection();
|
||||||
@@ -63,13 +63,13 @@ internal sealed partial class Program
|
|||||||
|
|
||||||
if (keyInstance.IsCurrent)
|
if (keyInstance.IsCurrent)
|
||||||
{
|
{
|
||||||
TelemetryService.WriteEvent(new ColdLaunchEvent());
|
PowerToysTelemetry.Log.WriteEvent(new ColdLaunchEvent());
|
||||||
keyInstance.Activated += OnActivated;
|
keyInstance.Activated += OnActivated;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
isRedirect = true;
|
isRedirect = true;
|
||||||
TelemetryService.WriteEvent(new ReactivateInstanceEvent());
|
PowerToysTelemetry.Log.WriteEvent(new ReactivateInstanceEvent());
|
||||||
RedirectActivationTo(args, keyInstance);
|
RedirectActivationTo(args, keyInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
|
||||||
using Microsoft.PowerToys.Telemetry.Events;
|
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
|
|
||||||
public abstract class TelemetryEventBase : EventBase, IEvent
|
|
||||||
{
|
|
||||||
// Overridden in derived classes
|
|
||||||
public abstract PartA_PrivTags PartA_PrivTags { get; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI.Services.Telemetry;
|
|
||||||
|
|
||||||
internal static class TelemetryService
|
|
||||||
{
|
|
||||||
public static void WriteEvent(TelemetryEventBase telemetryEvent) => PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<winuiex:WindowEx
|
|
||||||
x:Class="Microsoft.CommandPalette.UI.ToastWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:local="using:Microsoft.CommandPalette.UI"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
|
||||||
xmlns:winuiex="using:WinUIEx"
|
|
||||||
Title="Command Palette Toast"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
<winuiex:WindowEx.SystemBackdrop>
|
|
||||||
<DesktopAcrylicBackdrop />
|
|
||||||
</winuiex:WindowEx.SystemBackdrop>
|
|
||||||
<Grid x:Name="ToastGrid">
|
|
||||||
<!-- This padding is used to calculate the dimensions of the ToastWindow -->
|
|
||||||
<TextBlock
|
|
||||||
x:Name="ToastText"
|
|
||||||
Padding="12,12,24,20"
|
|
||||||
Text="{x:Bind ViewModel.ToastMessage, Mode=OneWay}"
|
|
||||||
TextAlignment="Center" />
|
|
||||||
</Grid>
|
|
||||||
</winuiex:WindowEx>
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using ManagedCommon;
|
|
||||||
using Microsoft.CommandPalette.UI.Models.Messages;
|
|
||||||
using Windows.System;
|
|
||||||
using RS_ = Microsoft.CommandPalette.UI.Helpers.ResourceLoaderInstance;
|
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.UI;
|
|
||||||
|
|
||||||
public sealed partial class ToastWindow : WindowEx,
|
|
||||||
IRecipient<QuitMessage>
|
|
||||||
{
|
|
||||||
private readonly HWND _hwnd;
|
|
||||||
private readonly DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
|
||||||
private readonly HiddenOwnerWindowBehavior _hiddenOwnerWindowBehavior = new();
|
|
||||||
|
|
||||||
public ToastViewModel ViewModel { get; } = new();
|
|
||||||
|
|
||||||
public ToastWindow()
|
|
||||||
{
|
|
||||||
this.InitializeComponent();
|
|
||||||
AppWindow.Hide();
|
|
||||||
ExtendsContentIntoTitleBar = true;
|
|
||||||
AppWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
|
|
||||||
this.SetIcon();
|
|
||||||
AppWindow.Title = RS_.GetString("ToastWindowTitle");
|
|
||||||
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
|
|
||||||
_hiddenOwnerWindowBehavior.ShowInTaskbar(this, false);
|
|
||||||
|
|
||||||
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
|
|
||||||
PInvoke.EnableWindow(_hwnd, false);
|
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double GetScaleFactor(HWND hwnd)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var monitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
|
||||||
_ = PInvoke.GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out _);
|
|
||||||
return dpiX / 96.0;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError($"Failed to get scale factor, error: {ex.Message}");
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PositionCentered()
|
|
||||||
{
|
|
||||||
this.SetWindowSize(ToastText.ActualWidth, ToastText.ActualHeight);
|
|
||||||
|
|
||||||
var displayArea = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest);
|
|
||||||
if (displayArea is not null)
|
|
||||||
{
|
|
||||||
var centeredPosition = AppWindow.Position;
|
|
||||||
centeredPosition.X = (displayArea.WorkArea.Width - AppWindow.Size.Width) / 2;
|
|
||||||
|
|
||||||
var monitorHeight = displayArea.WorkArea.Height;
|
|
||||||
var windowHeight = AppWindow.Size.Height;
|
|
||||||
centeredPosition.Y = monitorHeight - (windowHeight + 8); // Align with other shell toasts, like the volume indicator.
|
|
||||||
AppWindow.Move(centeredPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShowToast(string message)
|
|
||||||
{
|
|
||||||
ViewModel.ToastMessage = message;
|
|
||||||
DispatcherQueue.TryEnqueue(
|
|
||||||
DispatcherQueuePriority.Low,
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
PositionCentered();
|
|
||||||
|
|
||||||
// SW_SHOWNA prevents us from getting activated (and stealing FG)
|
|
||||||
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_SHOWNA);
|
|
||||||
|
|
||||||
_debounceTimer.Debounce(
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
AppWindow.Hide();
|
|
||||||
},
|
|
||||||
interval: TimeSpan.FromMilliseconds(2500),
|
|
||||||
immediate: false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Receive(QuitMessage message)
|
|
||||||
{
|
|
||||||
// This might come in on a background thread
|
|
||||||
DispatcherQueue.TryEnqueue(() => Close());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user