move controls to lib

Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
This commit is contained in:
Shawn Yuan (from Dev Box)
2025-12-01 09:37:47 +08:00
parent bb180a166a
commit b3b99d6d11
18 changed files with 658 additions and 308 deletions

View File

@@ -64,45 +64,12 @@
</Grid>
<Grid Grid.Row="1">
<ScrollViewer>
<ItemsControl
<controls:QuickAccessList
Margin="12,26,12,24"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
ItemsSource="{x:Bind ViewModel.FlyoutMenuItems}"
TabNavigation="Local">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<tkcontrols:WrapPanel HorizontalAlignment="Stretch" VerticalSpacing="12" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="viewModels:FlyoutMenuItem">
<controls:FlyoutMenuButton
AutomationProperties.Name="{x:Bind Label}"
Click="ModuleButton_Click"
Tag="{x:Bind Tag}"
Visibility="{x:Bind Visible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BoolToVisibilityConverter}}">
<controls:FlyoutMenuButton.Content>
<TextBlock
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Label}"
TextAlignment="Center"
TextWrapping="Wrap" />
</controls:FlyoutMenuButton.Content>
<controls:FlyoutMenuButton.Icon>
<Image>
<Image.Source>
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
</Image.Source>
</Image>
</controls:FlyoutMenuButton.Icon>
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind ToolTip}" Visibility="{x:Bind ToolTip, Converter={StaticResource StringVisibilityConverter}}" />
</ToolTipService.ToolTip>
</controls:FlyoutMenuButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
TabNavigation="Local" />
</ScrollViewer>
</Grid>
</Grid>

View File

@@ -44,128 +44,6 @@ public sealed partial class LaunchPage : Page
}
}
private void ModuleButton_Click(object sender, RoutedEventArgs e)
{
if (sender is not FlyoutMenuButton selectedModuleBtn)
{
return;
}
if (selectedModuleBtn.Tag is not ModuleType moduleType)
{
return;
}
bool moduleRun = true;
switch (moduleType)
{
case ModuleType.ColorPicker:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.EnvironmentVariables:
{
bool launchAdmin = SettingsRepository<EnvironmentVariablesSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator;
bool isElevated = _coordinator?.IsRunnerElevated ?? false;
string eventName = !isElevated && launchAdmin
? Constants.ShowEnvironmentVariablesAdminSharedEvent()
: Constants.ShowEnvironmentVariablesSharedEvent();
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
{
eventHandle.Set();
}
}
break;
case ModuleType.FancyZones:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.FZEToggleEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.Hosts:
{
bool launchAdmin = SettingsRepository<HostsSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator;
bool isElevated = _coordinator?.IsRunnerElevated ?? false;
string eventName = !isElevated && launchAdmin
? Constants.ShowHostsAdminSharedEvent()
: Constants.ShowHostsSharedEvent();
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
{
eventHandle.Set();
}
}
break;
case ModuleType.PowerLauncher:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerLauncherSharedEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.PowerOCR:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.RegistryPreview:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RegistryPreviewTriggerEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.MeasureTool:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.ShortcutGuide:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShortcutGuideTriggerEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.CmdPal:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowCmdPalEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.Workspaces:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.WorkspacesLaunchEditorEvent()))
{
eventHandle.Set();
}
break;
default:
moduleRun = false;
break;
}
if (moduleRun)
{
_coordinator?.OnModuleLaunched(moduleType);
}
_coordinator?.HideFlyout();
}
private void SettingsBtn_Click(object sender, RoutedEventArgs e)
{
_coordinator?.OpenSettings();

View File

@@ -4,15 +4,19 @@
using System;
using System.Collections.ObjectModel;
using System.Threading;
using global::PowerToys.GPOWrapper;
using ManagedCommon;
using Microsoft.PowerToys.QuickAccess.Helpers;
using Microsoft.PowerToys.QuickAccess.Services;
using Microsoft.PowerToys.Settings.UI.Controls;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
using Microsoft.UI.Dispatching;
using Microsoft.Windows.ApplicationModel.Resources;
using PowerToys.Interop;
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
@@ -24,7 +28,7 @@ public sealed class LauncherViewModel : Observable
private readonly DispatcherQueue _dispatcherQueue;
private GeneralSettings _generalSettings;
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; }
public ObservableCollection<QuickAccessItem> FlyoutMenuItems { get; }
public bool IsUpdateAvailable { get; private set; }
@@ -39,7 +43,7 @@ public sealed class LauncherViewModel : Observable
_settingsRepository.SettingsChanged += OnSettingsChanged;
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
FlyoutMenuItems = new ObservableCollection<QuickAccessItem>();
AddFlyoutMenuItem(ModuleType.ColorPicker);
AddFlyoutMenuItem(ModuleType.CmdPal);
@@ -72,16 +76,129 @@ public sealed class LauncherViewModel : Observable
return;
}
FlyoutMenuItems.Add(new FlyoutMenuItem
FlyoutMenuItems.Add(new QuickAccessItem
{
Label = _resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)),
Title = _resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)),
Tag = moduleType,
Visible = ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType),
ToolTip = GetModuleToolTip(moduleType),
Description = GetModuleToolTip(moduleType),
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
Command = new RelayCommand(() => LaunchModule(moduleType)),
});
}
private void LaunchModule(ModuleType moduleType)
{
bool moduleRun = true;
switch (moduleType)
{
case ModuleType.ColorPicker:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.EnvironmentVariables:
{
bool launchAdmin = SettingsRepository<EnvironmentVariablesSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator;
bool isElevated = _coordinator?.IsRunnerElevated ?? false;
string eventName = !isElevated && launchAdmin
? Constants.ShowEnvironmentVariablesAdminSharedEvent()
: Constants.ShowEnvironmentVariablesSharedEvent();
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
{
eventHandle.Set();
}
}
break;
case ModuleType.FancyZones:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.FZEToggleEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.Hosts:
{
bool launchAdmin = SettingsRepository<HostsSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator;
bool isElevated = _coordinator?.IsRunnerElevated ?? false;
string eventName = !isElevated && launchAdmin
? Constants.ShowHostsAdminSharedEvent()
: Constants.ShowHostsSharedEvent();
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
{
eventHandle.Set();
}
}
break;
case ModuleType.PowerLauncher:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerLauncherSharedEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.PowerOCR:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.RegistryPreview:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RegistryPreviewTriggerEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.MeasureTool:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.ShortcutGuide:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShortcutGuideTriggerEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.CmdPal:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowCmdPalEvent()))
{
eventHandle.Set();
}
break;
case ModuleType.Workspaces:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.WorkspacesLaunchEditorEvent()))
{
eventHandle.Set();
}
break;
default:
moduleRun = false;
break;
}
if (moduleRun)
{
_coordinator?.OnModuleLaunched(moduleType);
}
_coordinator?.HideFlyout();
}
private string GetModuleToolTip(ModuleType moduleType)
{
return moduleType switch
@@ -111,7 +228,10 @@ public sealed class LauncherViewModel : Observable
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
foreach (var item in FlyoutMenuItems)
{
item.Visible = ModuleHelper.GetIsModuleEnabled(_generalSettings, item.Tag);
if (item.Tag is ModuleType moduleType)
{
item.Visible = ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType);
}
}
}
}

View File

@@ -0,0 +1,38 @@
// 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;
using Microsoft.UI.Xaml.Data;
namespace Microsoft.PowerToys.Settings.UI.Controls.Converters
{
public partial class ModuleListSortOptionToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is ModuleListSortOption sortOption && parameter is string paramString)
{
if (Enum.TryParse(typeof(ModuleListSortOption), paramString, out object? result) && result != null)
{
return sortOption == (ModuleListSortOption)result;
}
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is bool isChecked && isChecked && parameter is string paramString)
{
if (Enum.TryParse(typeof(ModuleListSortOption), paramString, out object? result) && result != null)
{
return (ModuleListSortOption)result;
}
}
return ModuleListSortOption.Alphabetical;
}
}
}

View File

@@ -0,0 +1,70 @@
<UserControl
x:Class="Microsoft.PowerToys.Settings.UI.Controls.ModuleList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Controls.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.Resources>
<converters:ModuleListSortOptionToBooleanConverter x:Key="ModuleListSortOptionToBooleanConverter" />
</UserControl.Resources>
<controls:Card
x:Name="ModulesCard"
Title="{x:Bind Title, Mode=OneWay}"
MinWidth="400"
Padding="0"
VerticalAlignment="Top"
DividerVisibility="Collapsed">
<controls:Card.TitleContent>
<Button
x:Uid="Dashboard_SortBy"
Margin="0,0,4,0"
VerticalAlignment="Center"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="Dashboard_SortBy_ToolTip" />
</ToolTipService.ToolTip>
<Button.Content>
<FontIcon FontSize="16" Glyph="&#xE8CB;" />
</Button.Content>
<Button.Flyout>
<MenuFlyout>
<ToggleMenuFlyoutItem
x:Uid="Dashboard_SortAlphabetical"
Click="SortAlphabetical_Click"
IsChecked="{x:Bind SortOption, Mode=OneWay, Converter={StaticResource ModuleListSortOptionToBooleanConverter}, ConverterParameter=Alphabetical}" />
<ToggleMenuFlyoutItem
x:Uid="Dashboard_SortByStatus"
Click="SortByStatus_Click"
IsChecked="{x:Bind SortOption, Mode=OneWay, Converter={StaticResource ModuleListSortOptionToBooleanConverter}, ConverterParameter=ByStatus}" />
</MenuFlyout>
</Button.Flyout>
</Button>
</controls:Card.TitleContent>
<ItemsRepeater
x:Name="DashboardView"
Grid.Row="1"
ItemsSource="{x:Bind ItemsSource, Mode=OneWay}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" Spacing="0" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="controls:ModuleListItem">
<controls:ModuleSettingsCard
Click="ModuleSettingsCard_Click"
Icon="{x:Bind Icon}"
IsLocked="{x:Bind IsLocked}"
IsNew="{x:Bind IsNew}"
IsOn="{x:Bind IsEnabled, Mode=TwoWay}"
Label="{x:Bind Label}"
Tag="{x:Bind Tag}" />
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</controls:Card>
</UserControl>

View File

@@ -0,0 +1,59 @@
// 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;
namespace Microsoft.PowerToys.Settings.UI.Controls
{
public sealed partial class ModuleList : UserControl
{
public ModuleList()
{
this.InitializeComponent();
}
public object ItemsSource
{
get => (object)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(ModuleList), new PropertyMetadata(null));
public ModuleListSortOption SortOption
{
get => (ModuleListSortOption)GetValue(SortOptionProperty);
set => SetValue(SortOptionProperty, value);
}
public static readonly DependencyProperty SortOptionProperty = DependencyProperty.Register(nameof(SortOption), typeof(ModuleListSortOption), typeof(ModuleList), new PropertyMetadata(ModuleListSortOption.Alphabetical));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(ModuleList), new PropertyMetadata(default(string)));
private void SortAlphabetical_Click(object sender, RoutedEventArgs e)
{
SortOption = ModuleListSortOption.Alphabetical;
}
private void SortByStatus_Click(object sender, RoutedEventArgs e)
{
SortOption = ModuleListSortOption.ByStatus;
}
private void ModuleSettingsCard_Click(object sender, RoutedEventArgs e)
{
if (sender is ModuleSettingsCard card && card.DataContext is ModuleListItem item)
{
item.ClickCommand?.Execute(item.Tag);
}
}
}
}

View File

@@ -0,0 +1,119 @@
// 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.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace Microsoft.PowerToys.Settings.UI.Controls
{
public class ModuleListItem : INotifyPropertyChanged
{
private bool _isEnabled;
private string _label = string.Empty;
private string _icon = string.Empty;
private bool _isNew;
private bool _isLocked;
private object? _tag;
private ICommand? _clickCommand;
public virtual string Label
{
get => _label;
set
{
if (_label != value)
{
_label = value;
OnPropertyChanged();
}
}
}
public virtual string Icon
{
get => _icon;
set
{
if (_icon != value)
{
_icon = value;
OnPropertyChanged();
}
}
}
public virtual bool IsNew
{
get => _isNew;
set
{
if (_isNew != value)
{
_isNew = value;
OnPropertyChanged();
}
}
}
public virtual bool IsLocked
{
get => _isLocked;
set
{
if (_isLocked != value)
{
_isLocked = value;
OnPropertyChanged();
}
}
}
public virtual bool IsEnabled
{
get => _isEnabled;
set
{
if (_isEnabled != value)
{
_isEnabled = value;
OnPropertyChanged();
}
}
}
public virtual object? Tag
{
get => _tag;
set
{
if (_tag != value)
{
_tag = value;
OnPropertyChanged();
}
}
}
public virtual ICommand? ClickCommand
{
get => _clickCommand;
set
{
if (_clickCommand != value)
{
_clickCommand = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -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.PowerToys.Settings.UI.Controls
{
public enum ModuleListSortOption
{
Alphabetical,
ByStatus,
}
}

View File

@@ -0,0 +1,68 @@
// 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.Windows.Input;
using Microsoft.UI.Xaml;
namespace Microsoft.PowerToys.Settings.UI.Controls
{
public sealed class QuickAccessItem : DependencyObject
{
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(QuickAccessItem), new PropertyMetadata(string.Empty));
public string Description
{
get => (string)GetValue(DescriptionProperty);
set => SetValue(DescriptionProperty, value);
}
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(nameof(Description), typeof(string), typeof(QuickAccessItem), new PropertyMetadata(string.Empty));
public string Icon
{
get => (string)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(string), typeof(QuickAccessItem), new PropertyMetadata(string.Empty));
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(QuickAccessItem), new PropertyMetadata(null));
public object CommandParameter
{
get => (object)GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(nameof(CommandParameter), typeof(object), typeof(QuickAccessItem), new PropertyMetadata(null));
public bool Visible
{
get => (bool)GetValue(VisibleProperty);
set => SetValue(VisibleProperty, value);
}
public static readonly DependencyProperty VisibleProperty = DependencyProperty.Register(nameof(Visible), typeof(bool), typeof(QuickAccessItem), new PropertyMetadata(true));
public object Tag
{
get => (object)GetValue(TagProperty);
set => SetValue(TagProperty, value);
}
public static readonly DependencyProperty TagProperty = DependencyProperty.Register(nameof(Tag), typeof(object), typeof(QuickAccessItem), new PropertyMetadata(null));
}
}

View File

@@ -0,0 +1,54 @@
<UserControl
x:Class="Microsoft.PowerToys.Settings.UI.Controls.QuickAccessList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters">
<UserControl.Resources>
<tkconverters:BoolToVisibilityConverter
x:Key="BoolToVisibilityConverter"
FalseValue="Collapsed"
TrueValue="Visible" />
<tkconverters:StringVisibilityConverter x:Key="StringVisibilityConverter" />
</UserControl.Resources>
<ItemsControl ItemsSource="{x:Bind ItemsSource, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<tkcontrols:WrapPanel
Padding="12"
HorizontalSpacing="16"
VerticalSpacing="24" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:QuickAccessItem">
<local:FlyoutMenuButton
AutomationProperties.Name="{x:Bind Title}"
Command="{x:Bind Command}"
CommandParameter="{x:Bind CommandParameter}"
Visibility="{x:Bind Visible, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<local:FlyoutMenuButton.Content>
<TextBlock
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Title}"
TextAlignment="Center"
TextWrapping="Wrap" />
</local:FlyoutMenuButton.Content>
<local:FlyoutMenuButton.Icon>
<Image Width="24">
<Image.Source>
<BitmapImage UriSource="{x:Bind Icon}" />
</Image.Source>
</Image>
</local:FlyoutMenuButton.Icon>
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind Description}" Visibility="{x:Bind Description, Converter={StaticResource StringVisibilityConverter}}" />
</ToolTipService.ToolTip>
</local:FlyoutMenuButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>

View File

@@ -0,0 +1,26 @@
// 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.Collections.Generic;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.Controls
{
public sealed partial class QuickAccessList : UserControl
{
public QuickAccessList()
{
this.InitializeComponent();
}
public object ItemsSource
{
get => (object)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(QuickAccessList), new PropertyMetadata(null));
}
}

View File

@@ -0,0 +1,44 @@
// 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;
using Microsoft.PowerToys.Settings.UI.Controls;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Xaml.Data;
namespace Microsoft.PowerToys.Settings.UI.Converters
{
public partial class EnumToModuleListSortOptionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is DashboardSortOrder sortOrder)
{
return sortOrder switch
{
DashboardSortOrder.Alphabetical => ModuleListSortOption.Alphabetical,
DashboardSortOrder.ByStatus => ModuleListSortOption.ByStatus,
_ => ModuleListSortOption.Alphabetical,
};
}
return ModuleListSortOption.Alphabetical;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is ModuleListSortOption sortOption)
{
return sortOption switch
{
ModuleListSortOption.Alphabetical => DashboardSortOrder.Alphabetical,
ModuleListSortOption.ByStatus => DashboardSortOrder.ByStatus,
_ => DashboardSortOrder.Alphabetical,
};
}
return DashboardSortOrder.Alphabetical;
}
}
}

View File

@@ -21,6 +21,7 @@
ActivationTemplate="{StaticResource ModuleItemActivationTemplate}"
ShortcutTemplate="{StaticResource ModuleItemShortcutTemplate}" />
<controlConverters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
<converters:EnumToModuleListSortOptionConverter x:Key="EnumToModuleListSortOptionConverter" />
<DataTemplate x:Key="ModuleItemShortcutTemplate" x:DataType="viewmodels:DashboardModuleShortcutItem">
<Grid MinHeight="36" ColumnSpacing="12">
<Grid.ColumnDefinitions>
@@ -159,64 +160,18 @@
</Grid.RowDefinitions>
<controls:Card x:Uid="QuickAccessTitle" VerticalAlignment="Top">
<Grid>
<ItemsControl
<controls:QuickAccessList
x:Name="QuickAccessItemsControl"
Margin="8,0,12,12"
ItemsSource="{x:Bind ViewModel.ActionModules, Mode=OneWay}"
Visibility="{x:Bind ViewModel.ActionModules.Count, Mode=OneWay, Converter={StaticResource DoubleToVisibilityConverter}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<tkcontrols:WrapPanel
Padding="12"
HorizontalSpacing="16"
VerticalSpacing="24" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="viewmodels:DashboardListItem">
<ItemsControl IsTabStop="False" ItemsSource="{x:Bind DashboardModuleItems, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="viewmodels:DashboardModuleButtonItem">
<controls:FlyoutMenuButton AutomationProperties.Name="{x:Bind ButtonTitle}" Click="{x:Bind ButtonClickHandler}">
<controls:FlyoutMenuButton.Content>
<TextBlock
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ButtonTitle}"
TextAlignment="Center"
TextWrapping="Wrap" />
</controls:FlyoutMenuButton.Content>
<controls:FlyoutMenuButton.Icon>
<Image Width="24">
<Image.Source>
<BitmapImage UriSource="{x:Bind ButtonGlyph}" />
</Image.Source>
</Image>
</controls:FlyoutMenuButton.Icon>
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind ButtonDescription}" />
</ToolTipService.ToolTip>
</controls:FlyoutMenuButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ItemsSource="{x:Bind ViewModel.QuickAccessItems, Mode=OneWay}"
Visibility="{x:Bind ViewModel.QuickAccessItems.Count, Mode=OneWay, Converter={StaticResource DoubleToVisibilityConverter}}" />
<TextBlock
x:Uid="NoActionsToShow"
Margin="12"
HorizontalAlignment="Left"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Visibility="{x:Bind ViewModel.ActionModules.Count, Mode=OneWay, Converter={StaticResource DoubleToInvertedVisibilityConverter}}" />
Visibility="{x:Bind ViewModel.QuickAccessItems.Count, Mode=OneWay, Converter={StaticResource DoubleToInvertedVisibilityConverter}}" />
</Grid>
</controls:Card>
<controls:Card
@@ -269,63 +224,13 @@
Visibility="{x:Bind ViewModel.ShortcutModules.Count, Mode=OneWay, Converter={StaticResource DoubleToInvertedVisibilityConverter}}" />
</Grid>
</controls:Card>
<controls:Card
<controls:ModuleList
x:Name="ModulesCard"
x:Uid="UtilitiesHeader"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="400"
Padding="0"
VerticalAlignment="Top"
DividerVisibility="Collapsed">
<controls:Card.TitleContent>
<Button
x:Uid="Dashboard_SortBy"
Margin="0,0,4,0"
VerticalAlignment="Center"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="Dashboard_SortBy_ToolTip" />
</ToolTipService.ToolTip>
<Button.Content>
<FontIcon FontSize="16" Glyph="&#xE8CB;" />
</Button.Content>
<Button.Flyout>
<MenuFlyout>
<ToggleMenuFlyoutItem
x:Uid="Dashboard_SortAlphabetical"
Click="SortAlphabetical_Click"
IsChecked="{x:Bind ViewModel.DashboardSortOrder, Mode=OneWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Alphabetical}" />
<ToggleMenuFlyoutItem
x:Uid="Dashboard_SortByStatus"
Click="SortByStatus_Click"
IsChecked="{x:Bind ViewModel.DashboardSortOrder, Mode=OneWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=ByStatus}" />
</MenuFlyout>
</Button.Flyout>
</Button>
</controls:Card.TitleContent>
<ItemsRepeater
x:Name="DashboardView"
Grid.Row="1"
ItemsSource="{x:Bind ViewModel.AllModules, Mode=OneWay}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" Spacing="0" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="viewmodels:DashboardListItem">
<controls:ModuleSettingsCard
Click="DashboardListItemClick"
Icon="{x:Bind Icon}"
IsLocked="{x:Bind IsLocked}"
IsNew="{x:Bind IsNew}"
IsOn="{x:Bind IsEnabled, Mode=TwoWay}"
Label="{x:Bind Label}"
Tag="{x:Bind Tag}" />
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</controls:Card>
ItemsSource="{x:Bind ViewModel.AllModules, Mode=OneWay}"
SortOption="{x:Bind ViewModel.DashboardSortOrder, Mode=TwoWay, Converter={StaticResource EnumToModuleListSortOptionConverter}}" />
</Grid>
</Grid>
</ScrollViewer>

View File

@@ -48,11 +48,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
ViewModel.ModuleEnabledChangedOnSettingsPage();
}
private void DashboardListItemClick(object sender, RoutedEventArgs e)
{
ViewModel.DashboardListItemClick(sender);
}
private void WhatsNewButton_Click(object sender, RoutedEventArgs e)
{
if (App.GetOobeWindow() == null)
@@ -66,15 +61,5 @@ namespace Microsoft.PowerToys.Settings.UI.Views
App.GetOobeWindow().Activate();
}
private void SortAlphabetical_Click(object sender, RoutedEventArgs e)
{
ViewModel.DashboardSortOrder = DashboardSortOrder.Alphabetical;
}
private void SortByStatus_Click(object sender, RoutedEventArgs e)
{
ViewModel.DashboardSortOrder = DashboardSortOrder.ByStatus;
}
}
}

View File

@@ -8,44 +8,39 @@ using System.ComponentModel;
using System.Runtime.CompilerServices;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Controls;
using Microsoft.UI;
using Windows.UI;
namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public partial class DashboardListItem : INotifyPropertyChanged
public partial class DashboardListItem : ModuleListItem
{
private bool _visible;
private bool _isEnabled;
public string Label { get; set; }
public bool IsNew { get; set; }
public string Icon { get; set; }
public string ToolTip { get; set; }
public ModuleType Tag { get; set; }
public bool IsLocked { get; set; }
public bool IsEnabled
public new ModuleType Tag
{
get => _isEnabled;
get => (ModuleType)base.Tag!;
set => base.Tag = value;
}
public Action<DashboardListItem> EnabledChangedCallback { get; set; }
public override bool IsEnabled
{
get => base.IsEnabled;
set
{
if (_isEnabled != value)
if (base.IsEnabled != value)
{
_isEnabled = value;
OnPropertyChanged();
base.IsEnabled = value;
EnabledChangedCallback?.Invoke(this);
}
}
}
public Action<DashboardListItem> EnabledChangedCallback { get; set; }
public bool Visible
{
get => _visible;
@@ -59,13 +54,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<DashboardModuleItem> DashboardModuleItems { get; set; } = new ObservableCollection<DashboardModuleItem>();
}
}

View File

@@ -12,6 +12,7 @@ using System.Windows.Threading;
using CommunityToolkit.WinUI.Controls;
using global::PowerToys.GPOWrapper;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Controls;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
@@ -40,6 +41,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
public ObservableCollection<DashboardListItem> ActionModules { get; set; } = new ObservableCollection<DashboardListItem>();
public ObservableCollection<QuickAccessItem> QuickAccessItems { get; set; } = new ObservableCollection<QuickAccessItem>();
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
public AllHotkeyConflictsData AllHotkeyConflictsData
@@ -158,6 +161,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
IsNew = moduleType == ModuleType.CursorWrap,
DashboardModuleItems = GetModuleItems(moduleType),
ClickCommand = new RelayCommand<object>(DashboardListItemClick),
};
newItem.EnabledChangedCallback = EnabledChangedOnUI;
moduleItems.Add(newItem);
@@ -213,6 +217,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
ShortcutModules.Clear();
ActionModules.Clear();
QuickAccessItems.Clear();
foreach (var x in AllModules.Where(x => x.IsEnabled))
{
@@ -257,6 +262,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
ActionModules.Add(newItem);
newItem.EnabledChangedCallback = x.EnabledChangedCallback;
foreach (DashboardModuleButtonItem item in filteredItems)
{
QuickAccessItems.Add(new QuickAccessItem
{
Title = item.ButtonTitle,
Description = item.ButtonDescription,
Icon = item.ButtonGlyph,
Command = new RelayCommand(() => item.ButtonClickHandler?.Invoke(null, null)),
Tag = x.Tag,
});
}
}
}
}
@@ -596,7 +613,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
internal void DashboardListItemClick(object sender)
{
if (sender is FrameworkElement element && element.Tag is ModuleType moduleType)
if (sender is ModuleType moduleType)
{
NavigationService.Navigate(ModuleHelper.GetModulePageType(moduleType));
}