mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
CmdPal: Did someone say grid pages? (#40832)
Closes #38269 Still working on this one, but essentially allows a list page to become a grid page by specifying a `GridProperties` property like so: ```C# public AllAppsPage() { PlaceholderText = Resources.search_installed_apps_placeholder; GridProperties = new MediumGridLayout(); } ``` > This is a very early version and very subject to change. Much to clean, but feedback & suggestions are welcome. ## Current preview ### SmallGridLayout <img width="998" height="607" alt="image" src="https://github.com/user-attachments/assets/ebdf11fd-6c86-4fc3-bf49-bcbb5d32caa4" /> ### MediumGridLayout <img width="976" height="586" alt="image" src="https://github.com/user-attachments/assets/82daa2e9-548e-4864-8885-1c486ca9f891" /> ### GalleryGridLayout <img width="988" height="600" alt="image" src="https://github.com/user-attachments/assets/23ca486a-35c7-467a-b200-4f6ee5f4a95c" /> --------- Co-authored-by: Mike Griese <migrie@microsoft.com>
This commit is contained in:
2
.github/actions/spell-check/allow/code.txt
vendored
2
.github/actions/spell-check/allow/code.txt
vendored
@@ -320,4 +320,4 @@ MRUINFO
|
|||||||
REGSTR
|
REGSTR
|
||||||
|
|
||||||
# Misc Win32 APIs and PInvokes
|
# Misc Win32 APIs and PInvokes
|
||||||
INVOKEIDLIST
|
INVOKEIDLIST
|
||||||
|
|||||||
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -1451,7 +1451,6 @@ rstringalnum
|
|||||||
rstringalpha
|
rstringalpha
|
||||||
rstringdigit
|
rstringdigit
|
||||||
rtb
|
rtb
|
||||||
RTB
|
|
||||||
RTLREADING
|
RTLREADING
|
||||||
rtm
|
rtm
|
||||||
runas
|
runas
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// 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.CmdPal.Core.ViewModels.Models;
|
||||||
|
using Microsoft.CommandPalette.Extensions;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||||
|
|
||||||
|
public class GalleryGridPropertiesViewModel : IGridPropertiesViewModel
|
||||||
|
{
|
||||||
|
private readonly ExtensionObject<IGalleryGridLayout> _model;
|
||||||
|
|
||||||
|
public GalleryGridPropertiesViewModel(IGalleryGridLayout galleryGridLayout)
|
||||||
|
{
|
||||||
|
_model = new(galleryGridLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowTitle { get; set; }
|
||||||
|
|
||||||
|
public bool ShowSubtitle { get; set; }
|
||||||
|
|
||||||
|
public void InitializeProperties()
|
||||||
|
{
|
||||||
|
var model = _model.Unsafe;
|
||||||
|
if (model is null)
|
||||||
|
{
|
||||||
|
return; // throw?
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowTitle = model.ShowTitle;
|
||||||
|
ShowSubtitle = model.ShowSubtitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// 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.CmdPal.Core.ViewModels;
|
||||||
|
|
||||||
|
public interface IGridPropertiesViewModel
|
||||||
|
{
|
||||||
|
void InitializeProperties();
|
||||||
|
}
|
||||||
@@ -45,6 +45,10 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
|||||||
(!_isFetching) &&
|
(!_isFetching) &&
|
||||||
IsLoading == false;
|
IsLoading == false;
|
||||||
|
|
||||||
|
public bool IsGridView { get; private set; }
|
||||||
|
|
||||||
|
public IGridPropertiesViewModel? GridProperties { get; private set; }
|
||||||
|
|
||||||
// Remember - "observable" properties from the model (via PropChanged)
|
// Remember - "observable" properties from the model (via PropChanged)
|
||||||
// cannot be marked [ObservableProperty]
|
// cannot be marked [ObservableProperty]
|
||||||
public bool ShowDetails { get; private set; }
|
public bool ShowDetails { get; private set; }
|
||||||
@@ -516,6 +520,13 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
|||||||
|
|
||||||
_isDynamic = model is IDynamicListPage;
|
_isDynamic = model is IDynamicListPage;
|
||||||
|
|
||||||
|
IsGridView = model.GridProperties is not null;
|
||||||
|
UpdateProperty(nameof(IsGridView));
|
||||||
|
|
||||||
|
GridProperties = LoadGridPropertiesViewModel(model.GridProperties);
|
||||||
|
GridProperties?.InitializeProperties();
|
||||||
|
UpdateProperty(nameof(GridProperties));
|
||||||
|
|
||||||
ShowDetails = model.ShowDetails;
|
ShowDetails = model.ShowDetails;
|
||||||
UpdateProperty(nameof(ShowDetails));
|
UpdateProperty(nameof(ShowDetails));
|
||||||
|
|
||||||
@@ -537,9 +548,27 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
|||||||
model.ItemsChanged += Model_ItemsChanged;
|
model.ItemsChanged += Model_ItemsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IGridPropertiesViewModel? LoadGridPropertiesViewModel(IGridProperties? gridProperties)
|
||||||
|
{
|
||||||
|
if (gridProperties is IMediumGridLayout mediumGridLayout)
|
||||||
|
{
|
||||||
|
return new MediumGridPropertiesViewModel(mediumGridLayout);
|
||||||
|
}
|
||||||
|
else if (gridProperties is IGalleryGridLayout galleryGridLayout)
|
||||||
|
{
|
||||||
|
return new GalleryGridPropertiesViewModel(galleryGridLayout);
|
||||||
|
}
|
||||||
|
else if (gridProperties is ISmallGridLayout smallGridLayout)
|
||||||
|
{
|
||||||
|
return new SmallGridPropertiesViewModel(smallGridLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void LoadMoreIfNeeded()
|
public void LoadMoreIfNeeded()
|
||||||
{
|
{
|
||||||
var model = this._model.Unsafe;
|
var model = _model.Unsafe;
|
||||||
if (model is null)
|
if (model is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -583,7 +612,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
|||||||
{
|
{
|
||||||
base.FetchProperty(propertyName);
|
base.FetchProperty(propertyName);
|
||||||
|
|
||||||
var model = this._model.Unsafe;
|
var model = _model.Unsafe;
|
||||||
if (model is null)
|
if (model is null)
|
||||||
{
|
{
|
||||||
return; // throw?
|
return; // throw?
|
||||||
@@ -591,14 +620,20 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
|||||||
|
|
||||||
switch (propertyName)
|
switch (propertyName)
|
||||||
{
|
{
|
||||||
|
case nameof(GridProperties):
|
||||||
|
IsGridView = model.GridProperties is not null;
|
||||||
|
GridProperties = LoadGridPropertiesViewModel(model.GridProperties);
|
||||||
|
GridProperties?.InitializeProperties();
|
||||||
|
UpdateProperty(nameof(IsGridView));
|
||||||
|
break;
|
||||||
case nameof(ShowDetails):
|
case nameof(ShowDetails):
|
||||||
this.ShowDetails = model.ShowDetails;
|
ShowDetails = model.ShowDetails;
|
||||||
break;
|
break;
|
||||||
case nameof(PlaceholderText):
|
case nameof(PlaceholderText):
|
||||||
this._modelPlaceholderText = model.PlaceholderText;
|
_modelPlaceholderText = model.PlaceholderText;
|
||||||
break;
|
break;
|
||||||
case nameof(SearchText):
|
case nameof(SearchText):
|
||||||
this.SearchText = model.SearchText;
|
SearchText = model.SearchText;
|
||||||
break;
|
break;
|
||||||
case nameof(EmptyContent):
|
case nameof(EmptyContent):
|
||||||
EmptyContent = new(new(model.EmptyContent), PageContext);
|
EmptyContent = new(new(model.EmptyContent), PageContext);
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
using Microsoft.CmdPal.Core.ViewModels.Models;
|
||||||
|
using Microsoft.CommandPalette.Extensions;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||||
|
|
||||||
|
public class MediumGridPropertiesViewModel : IGridPropertiesViewModel
|
||||||
|
{
|
||||||
|
private readonly ExtensionObject<IMediumGridLayout> _model;
|
||||||
|
|
||||||
|
public MediumGridPropertiesViewModel(IMediumGridLayout mediumGridLayout)
|
||||||
|
{
|
||||||
|
_model = new(mediumGridLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowTitle { get; set; }
|
||||||
|
|
||||||
|
public void InitializeProperties()
|
||||||
|
{
|
||||||
|
var model = _model.Unsafe;
|
||||||
|
if (model is null)
|
||||||
|
{
|
||||||
|
return; // throw?
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowTitle = model.ShowTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// 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.CmdPal.Core.ViewModels.Models;
|
||||||
|
using Microsoft.CommandPalette.Extensions;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||||
|
|
||||||
|
public class SmallGridPropertiesViewModel : IGridPropertiesViewModel
|
||||||
|
{
|
||||||
|
private readonly ExtensionObject<ISmallGridLayout> _model;
|
||||||
|
|
||||||
|
public SmallGridPropertiesViewModel(ISmallGridLayout smallGridLayout)
|
||||||
|
{
|
||||||
|
_model = new(smallGridLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeProperties()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// 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.CmdPal.Core.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI;
|
||||||
|
|
||||||
|
internal sealed partial class GridItemTemplateSelector : DataTemplateSelector
|
||||||
|
{
|
||||||
|
public IGridPropertiesViewModel? GridProperties { get; set; }
|
||||||
|
|
||||||
|
public DataTemplate? Small { get; set; }
|
||||||
|
|
||||||
|
public DataTemplate? Medium { get; set; }
|
||||||
|
|
||||||
|
public DataTemplate? Gallery { get; set; }
|
||||||
|
|
||||||
|
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject dependencyObject)
|
||||||
|
{
|
||||||
|
DataTemplate? dataTemplate = Medium;
|
||||||
|
|
||||||
|
if (GridProperties is SmallGridPropertiesViewModel)
|
||||||
|
{
|
||||||
|
dataTemplate = Small;
|
||||||
|
}
|
||||||
|
else if (GridProperties is MediumGridPropertiesViewModel)
|
||||||
|
{
|
||||||
|
dataTemplate = Medium;
|
||||||
|
}
|
||||||
|
else if (GridProperties is GalleryGridPropertiesViewModel)
|
||||||
|
{
|
||||||
|
dataTemplate = Gallery;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
x:Class="Microsoft.CmdPal.UI.ListPage"
|
x:Class="Microsoft.CmdPal.UI.ListPage"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:cmdpalUI="using:Microsoft.CmdPal.UI"
|
||||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||||
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
||||||
xmlns:coreViewModels="using:Microsoft.CmdPal.Core.ViewModels"
|
xmlns:coreViewModels="using:Microsoft.CmdPal.Core.ViewModels"
|
||||||
@@ -11,8 +12,11 @@
|
|||||||
xmlns:help="using:Microsoft.CmdPal.UI.Helpers"
|
xmlns:help="using:Microsoft.CmdPal.UI.Helpers"
|
||||||
xmlns:local="using:Microsoft.CmdPal.UI"
|
xmlns:local="using:Microsoft.CmdPal.UI"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
|
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
|
||||||
|
x:Name="PageRoot"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
|
DataContext="{x:Bind ViewModel, Mode=OneWay}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Page.Resources>
|
<Page.Resources>
|
||||||
@@ -23,6 +27,7 @@
|
|||||||
IsSourceGrouped="True"
|
IsSourceGrouped="True"
|
||||||
Source="{x:Bind ViewModel.Items, Mode=OneWay}" />-->
|
Source="{x:Bind ViewModel.Items, Mode=OneWay}" />-->
|
||||||
|
|
||||||
|
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||||
<converters:StringVisibilityConverter
|
<converters:StringVisibilityConverter
|
||||||
x:Key="StringVisibilityConverter"
|
x:Key="StringVisibilityConverter"
|
||||||
EmptyValue="Collapsed"
|
EmptyValue="Collapsed"
|
||||||
@@ -39,6 +44,14 @@
|
|||||||
ToolTipService.ToolTip="{x:Bind ToolTip, Mode=OneWay}" />
|
ToolTipService.ToolTip="{x:Bind ToolTip, Mode=OneWay}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<cmdpalUI:GridItemTemplateSelector
|
||||||
|
x:Key="GridItemTemplateSelector"
|
||||||
|
x:DataType="coreViewModels:ListItemViewModel"
|
||||||
|
Gallery="{StaticResource GalleryGridItemViewModelTemplate}"
|
||||||
|
GridProperties="{x:Bind ViewModel.GridProperties}"
|
||||||
|
Medium="{StaticResource MediumGridItemViewModelTemplate}"
|
||||||
|
Small="{StaticResource SmallGridItemViewModelTemplate}" />
|
||||||
|
|
||||||
<!-- https://learn.microsoft.com/windows/apps/design/controls/itemsview#specify-the-look-of-the-items -->
|
<!-- https://learn.microsoft.com/windows/apps/design/controls/itemsview#specify-the-look-of-the-items -->
|
||||||
<DataTemplate x:Key="ListItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
<DataTemplate x:Key="ListItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
||||||
<Grid
|
<Grid
|
||||||
@@ -102,6 +115,145 @@
|
|||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Grid item templates for visual grid representation -->
|
||||||
|
<DataTemplate x:Key="SmallGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
||||||
|
<StackPanel
|
||||||
|
Width="60"
|
||||||
|
Height="60"
|
||||||
|
Padding="8,16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
AutomationProperties.Name="{x:Bind Title}"
|
||||||
|
BorderThickness="0"
|
||||||
|
CornerRadius="8"
|
||||||
|
Orientation="Vertical"
|
||||||
|
ToolTipService.ToolTip="{x:Bind Title}">
|
||||||
|
|
||||||
|
<cpcontrols:IconBox
|
||||||
|
x:Name="GridIconBorder"
|
||||||
|
Width="28"
|
||||||
|
Height="28"
|
||||||
|
Margin="0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{ThemeResource TextFillColorPrimary}"
|
||||||
|
SourceKey="{x:Bind Icon, Mode=OneWay}"
|
||||||
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="MediumGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
||||||
|
<StackPanel
|
||||||
|
Width="100"
|
||||||
|
Height="100"
|
||||||
|
Padding="8,16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
AutomationProperties.Name="{x:Bind Title}"
|
||||||
|
BorderThickness="0"
|
||||||
|
CornerRadius="8"
|
||||||
|
Orientation="Vertical"
|
||||||
|
ToolTipService.ToolTip="{x:Bind Title}">
|
||||||
|
|
||||||
|
<cpcontrols:IconBox
|
||||||
|
x:Name="GridIconBorder"
|
||||||
|
Width="36"
|
||||||
|
Height="36"
|
||||||
|
Margin="0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CharacterSpacing="12"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="{ThemeResource TextFillColorPrimary}"
|
||||||
|
SourceKey="{x:Bind Icon, Mode=OneWay}"
|
||||||
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
x:Name="TitleTextBlock"
|
||||||
|
MaxHeight="40"
|
||||||
|
Margin="0,8,0,4"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CharacterSpacing="12"
|
||||||
|
FontSize="12"
|
||||||
|
Text="{x:Bind Title}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextTrimming="WordEllipsis"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Visibility="{Binding ElementName=PageRoot, Path=DataContext.GridProperties.ShowTitle, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="GalleryGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
||||||
|
<StackPanel
|
||||||
|
Width="160"
|
||||||
|
Margin="4"
|
||||||
|
Padding="0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
AutomationProperties.Name="{x:Bind Title}"
|
||||||
|
BorderThickness="0"
|
||||||
|
CornerRadius="4"
|
||||||
|
Orientation="Vertical"
|
||||||
|
ToolTipService.ToolTip="{x:Bind Title}">
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Width="160"
|
||||||
|
Height="160"
|
||||||
|
Margin="0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CornerRadius="4">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Viewbox
|
||||||
|
Grid.Row="1"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Stretch="UniformToFill"
|
||||||
|
StretchDirection="Both">
|
||||||
|
<cpcontrols:IconBox
|
||||||
|
CornerRadius="4"
|
||||||
|
Foreground="{ThemeResource TextFillColorPrimary}"
|
||||||
|
SourceKey="{x:Bind Icon, Mode=OneWay}"
|
||||||
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
||||||
|
</Viewbox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Padding="4" Orientation="Vertical">
|
||||||
|
<TextBlock
|
||||||
|
x:Name="TitleTextBlock"
|
||||||
|
MaxWidth="152"
|
||||||
|
MaxHeight="40"
|
||||||
|
Margin="0"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CharacterSpacing="12"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="{ThemeResource TextFillColorPrimary}"
|
||||||
|
Text="{x:Bind Title}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextTrimming="WordEllipsis"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
<TextBlock
|
||||||
|
x:Name="SubTitleTextBlock"
|
||||||
|
MaxWidth="152"
|
||||||
|
MaxHeight="40"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CharacterSpacing="11"
|
||||||
|
FontSize="11"
|
||||||
|
Foreground="{ThemeResource TextFillColorTertiary}"
|
||||||
|
Text="{x:Bind Subtitle}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextTrimming="WordEllipsis"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
</Page.Resources>
|
</Page.Resources>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -110,66 +262,50 @@
|
|||||||
TargetType="x:Boolean"
|
TargetType="x:Boolean"
|
||||||
Value="{x:Bind ViewModel.ShowEmptyContent, Mode=OneWay}">
|
Value="{x:Bind ViewModel.ShowEmptyContent, Mode=OneWay}">
|
||||||
<controls:Case Value="False">
|
<controls:Case Value="False">
|
||||||
<ListView
|
<controls:SwitchPresenter
|
||||||
x:Name="ItemsList"
|
HorizontalAlignment="Stretch"
|
||||||
Padding="0,2,0,0"
|
TargetType="x:Boolean"
|
||||||
ContextCanceled="ItemsList_OnContextCanceled"
|
Value="{x:Bind ViewModel.IsGridView, Mode=OneWay}">
|
||||||
ContextRequested="ItemsList_OnContextRequested"
|
<controls:Case Value="False">
|
||||||
DoubleTapped="ItemsList_DoubleTapped"
|
<ListView
|
||||||
IsDoubleTapEnabled="True"
|
x:Name="ItemsList"
|
||||||
IsItemClickEnabled="True"
|
Padding="0,2,0,0"
|
||||||
ItemClick="ItemsList_ItemClick"
|
ContextCanceled="Items_OnContextCanceled"
|
||||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
ContextRequested="Items_OnContextRequested"
|
||||||
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
DoubleTapped="Items_DoubleTapped"
|
||||||
SelectionChanged="ItemsList_SelectionChanged">
|
IsDoubleTapEnabled="True"
|
||||||
<ListView.ItemContainerTransitions>
|
IsItemClickEnabled="True"
|
||||||
<TransitionCollection />
|
ItemClick="Items_ItemClick"
|
||||||
</ListView.ItemContainerTransitions>
|
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
||||||
<!--<ListView.GroupStyle>
|
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
||||||
<GroupStyle HidesIfEmpty="True">
|
RightTapped="Items_RightTapped"
|
||||||
<GroupStyle.HeaderTemplate>
|
SelectionChanged="Items_SelectionChanged">
|
||||||
<DataTemplate>
|
<ListView.ItemContainerTransitions>
|
||||||
<TextBlock
|
<TransitionCollection />
|
||||||
Margin="0,16,0,0"
|
</ListView.ItemContainerTransitions>
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
</ListView>
|
||||||
Text="{Binding Key, Mode=OneWay}" />
|
</controls:Case>
|
||||||
</DataTemplate>
|
<controls:Case Value="True">
|
||||||
</GroupStyle.HeaderTemplate>
|
<GridView
|
||||||
</GroupStyle>
|
x:Name="ItemsGrid"
|
||||||
</ListView.GroupStyle>-->
|
Padding="8"
|
||||||
</ListView>
|
ContextCanceled="Items_OnContextCanceled"
|
||||||
</controls:Case>
|
ContextRequested="Items_OnContextRequested"
|
||||||
<controls:Case Value="True">
|
DoubleTapped="Items_DoubleTapped"
|
||||||
<StackPanel
|
IsDoubleTapEnabled="True"
|
||||||
Margin="24"
|
IsItemClickEnabled="True"
|
||||||
HorizontalAlignment="Center"
|
ItemClick="Items_ItemClick"
|
||||||
VerticalAlignment="Center"
|
ItemTemplateSelector="{StaticResource GridItemTemplateSelector}"
|
||||||
Orientation="Vertical"
|
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
||||||
Spacing="4">
|
RightTapped="Items_RightTapped"
|
||||||
<cpcontrols:IconBox
|
SelectionChanged="Items_SelectionChanged">
|
||||||
x:Name="IconBorder"
|
<GridView.ItemContainerTransitions>
|
||||||
Width="48"
|
<TransitionCollection />
|
||||||
Height="48"
|
</GridView.ItemContainerTransitions>
|
||||||
Margin="8"
|
</GridView>
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
</controls:Case>
|
||||||
SourceKey="{x:Bind ViewModel.EmptyContent.Icon, Mode=OneWay}"
|
</controls:SwitchPresenter>
|
||||||
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
|
||||||
<TextBlock
|
|
||||||
Margin="0,4,0,0"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Text="{x:Bind ViewModel.EmptyContent.Title, Mode=OneWay}"
|
|
||||||
TextAlignment="Center"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
Text="{x:Bind ViewModel.EmptyContent.Subtitle, Mode=OneWay}"
|
|
||||||
TextAlignment="Center"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</StackPanel>
|
|
||||||
</controls:Case>
|
</controls:Case>
|
||||||
</controls:SwitchPresenter>
|
</controls:SwitchPresenter>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using Microsoft.CmdPal.UI.ViewModels;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
using Microsoft.UI.Xaml.Input;
|
using Microsoft.UI.Xaml.Input;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
@@ -38,13 +39,21 @@ public sealed partial class ListPage : Page,
|
|||||||
public static readonly DependencyProperty ViewModelProperty =
|
public static readonly DependencyProperty ViewModelProperty =
|
||||||
DependencyProperty.Register(nameof(ViewModel), typeof(ListViewModel), typeof(ListPage), new PropertyMetadata(null, OnViewModelChanged));
|
DependencyProperty.Register(nameof(ViewModel), typeof(ListViewModel), typeof(ListPage), new PropertyMetadata(null, OnViewModelChanged));
|
||||||
|
|
||||||
|
private ListViewBase ItemView
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ViewModel?.IsGridView == true ? ItemsGrid : ItemsList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ListPage()
|
public ListPage()
|
||||||
{
|
{
|
||||||
this.InitializeComponent();
|
this.InitializeComponent();
|
||||||
this.NavigationCacheMode = NavigationCacheMode.Disabled;
|
this.NavigationCacheMode = NavigationCacheMode.Disabled;
|
||||||
this.ItemsList.Loaded += ItemsList_Loaded;
|
this.ItemView.Loaded += Items_Loaded;
|
||||||
this.ItemsList.PreviewKeyDown += ItemsList_PreviewKeyDown;
|
this.ItemView.PreviewKeyDown += Items_PreviewKeyDown;
|
||||||
this.ItemsList.PointerPressed += ItemsList_PointerPressed;
|
this.ItemView.PointerPressed += Items_PointerPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
@@ -55,11 +64,11 @@ public sealed partial class ListPage : Page,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e.NavigationMode == NavigationMode.Back
|
if (e.NavigationMode == NavigationMode.Back
|
||||||
|| (e.NavigationMode == NavigationMode.New && ItemsList.Items.Count > 0))
|
|| (e.NavigationMode == NavigationMode.New && ItemView.Items.Count > 0))
|
||||||
{
|
{
|
||||||
// Upon navigating _back_ to this page, immediately select the
|
// Upon navigating _back_ to this page, immediately select the
|
||||||
// first item in the list
|
// first item in the list
|
||||||
ItemsList.SelectedIndex = 0;
|
ItemView.SelectedIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterAll isn't AOT compatible
|
// RegisterAll isn't AOT compatible
|
||||||
@@ -90,7 +99,6 @@ public sealed partial class ListPage : Page,
|
|||||||
{
|
{
|
||||||
ViewModel?.SafeCleanup();
|
ViewModel?.SafeCleanup();
|
||||||
CleanupHelper.Cleanup(this);
|
CleanupHelper.Cleanup(this);
|
||||||
Bindings.StopTracking();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean-up event listeners
|
// Clean-up event listeners
|
||||||
@@ -100,7 +108,7 @@ public sealed partial class ListPage : Page,
|
|||||||
}
|
}
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
||||||
private void ItemsList_ItemClick(object sender, ItemClickEventArgs e)
|
private void Items_ItemClick(object sender, ItemClickEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.ClickedItem is ListItemViewModel item)
|
if (e.ClickedItem is ListItemViewModel item)
|
||||||
{
|
{
|
||||||
@@ -123,9 +131,9 @@ public sealed partial class ListPage : Page,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ItemsList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
private void Items_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (ItemsList.SelectedItem is ListItemViewModel vm)
|
if (ItemView.SelectedItem is ListItemViewModel vm)
|
||||||
{
|
{
|
||||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||||
if (!settings.SingleClickActivates)
|
if (!settings.SingleClickActivates)
|
||||||
@@ -136,10 +144,10 @@ public sealed partial class ListPage : Page,
|
|||||||
}
|
}
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
||||||
private void ItemsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void Items_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var vm = ViewModel;
|
var vm = ViewModel;
|
||||||
var li = ItemsList.SelectedItem as ListItemViewModel;
|
var li = ItemView.SelectedItem as ListItemViewModel;
|
||||||
_ = Task.Run(() =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
vm?.UpdateSelectedItemCommand.Execute(li);
|
vm?.UpdateSelectedItemCommand.Execute(li);
|
||||||
@@ -154,12 +162,12 @@ public sealed partial class ListPage : Page,
|
|||||||
// here, then in Page_ItemsUpdated trying to select that cached item if
|
// here, then in Page_ItemsUpdated trying to select that cached item if
|
||||||
// it's in the list (otherwise, clear the cache), but that seems
|
// it's in the list (otherwise, clear the cache), but that seems
|
||||||
// aggressively BODGY for something that mostly just works today.
|
// aggressively BODGY for something that mostly just works today.
|
||||||
if (ItemsList.SelectedItem is not null)
|
if (ItemView.SelectedItem is not null)
|
||||||
{
|
{
|
||||||
ItemsList.ScrollIntoView(ItemsList.SelectedItem);
|
ItemView.ScrollIntoView(ItemView.SelectedItem);
|
||||||
|
|
||||||
// Automation notification for screen readers
|
// Automation notification for screen readers
|
||||||
var listViewPeer = Microsoft.UI.Xaml.Automation.Peers.ListViewAutomationPeer.CreatePeerForElement(ItemsList);
|
var listViewPeer = Microsoft.UI.Xaml.Automation.Peers.ListViewAutomationPeer.CreatePeerForElement(ItemView);
|
||||||
if (listViewPeer is not null && li is not null)
|
if (listViewPeer is not null && li is not null)
|
||||||
{
|
{
|
||||||
var notificationText = li.Title;
|
var notificationText = li.Title;
|
||||||
@@ -172,10 +180,37 @@ public sealed partial class ListPage : Page,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ItemsList_Loaded(object sender, RoutedEventArgs e)
|
private void Items_RightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
// Find the ScrollViewer in the ListView
|
if (e.OriginalSource is FrameworkElement element &&
|
||||||
var listViewScrollViewer = FindScrollViewer(this.ItemsList);
|
element.DataContext is ListItemViewModel item)
|
||||||
|
{
|
||||||
|
if (ItemView.SelectedItem != item)
|
||||||
|
{
|
||||||
|
ItemView.SelectedItem = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewModel?.UpdateSelectedItemCommand.Execute(item);
|
||||||
|
|
||||||
|
var pos = e.GetPosition(element);
|
||||||
|
|
||||||
|
_ = DispatcherQueue.TryEnqueue(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
WeakReferenceMessenger.Default.Send<OpenContextMenuMessage>(
|
||||||
|
new OpenContextMenuMessage(
|
||||||
|
element,
|
||||||
|
Microsoft.UI.Xaml.Controls.Primitives.FlyoutPlacementMode.BottomEdgeAlignedLeft,
|
||||||
|
pos,
|
||||||
|
ContextMenuFilterLocation.Top));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Items_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Find the ScrollViewer in the ItemView (ItemsList or ItemsGrid)
|
||||||
|
var listViewScrollViewer = FindScrollViewer(this.ItemView);
|
||||||
|
|
||||||
if (listViewScrollViewer is not null)
|
if (listViewScrollViewer is not null)
|
||||||
{
|
{
|
||||||
@@ -207,25 +242,25 @@ public sealed partial class ListPage : Page,
|
|||||||
// And then have these commands manipulate that state being bound to the UI instead
|
// And then have these commands manipulate that state being bound to the UI instead
|
||||||
// We may want to see how other non-list UIs need to behave to make this decision
|
// We may want to see how other non-list UIs need to behave to make this decision
|
||||||
// At least it's decoupled from the SearchBox now :)
|
// At least it's decoupled from the SearchBox now :)
|
||||||
if (ItemsList.SelectedIndex < ItemsList.Items.Count - 1)
|
if (ItemView.SelectedIndex < ItemView.Items.Count - 1)
|
||||||
{
|
{
|
||||||
ItemsList.SelectedIndex++;
|
ItemView.SelectedIndex++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ItemsList.SelectedIndex = 0;
|
ItemView.SelectedIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(NavigatePreviousCommand message)
|
public void Receive(NavigatePreviousCommand message)
|
||||||
{
|
{
|
||||||
if (ItemsList.SelectedIndex > 0)
|
if (ItemView.SelectedIndex > 0)
|
||||||
{
|
{
|
||||||
ItemsList.SelectedIndex--;
|
ItemView.SelectedIndex--;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ItemsList.SelectedIndex = ItemsList.Items.Count - 1;
|
ItemView.SelectedIndex = ItemView.Items.Count - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +270,7 @@ public sealed partial class ListPage : Page,
|
|||||||
{
|
{
|
||||||
ViewModel?.InvokeItemCommand.Execute(null);
|
ViewModel?.InvokeItemCommand.Execute(null);
|
||||||
}
|
}
|
||||||
else if (ItemsList.SelectedItem is ListItemViewModel item)
|
else if (ItemView.SelectedItem is ListItemViewModel item)
|
||||||
{
|
{
|
||||||
ViewModel?.InvokeItemCommand.Execute(item);
|
ViewModel?.InvokeItemCommand.Execute(item);
|
||||||
}
|
}
|
||||||
@@ -247,7 +282,7 @@ public sealed partial class ListPage : Page,
|
|||||||
{
|
{
|
||||||
ViewModel?.InvokeSecondaryCommandCommand.Execute(null);
|
ViewModel?.InvokeSecondaryCommandCommand.Execute(null);
|
||||||
}
|
}
|
||||||
else if (ItemsList.SelectedItem is ListItemViewModel item)
|
else if (ItemView.SelectedItem is ListItemViewModel item)
|
||||||
{
|
{
|
||||||
ViewModel?.InvokeSecondaryCommandCommand.Execute(item);
|
ViewModel?.InvokeSecondaryCommandCommand.Execute(item);
|
||||||
}
|
}
|
||||||
@@ -283,19 +318,19 @@ public sealed partial class ListPage : Page,
|
|||||||
//
|
//
|
||||||
// It's important to do this here, because once there's no selection
|
// It's important to do this here, because once there's no selection
|
||||||
// (which can happen as the list updates) we won't get an
|
// (which can happen as the list updates) we won't get an
|
||||||
// ItemsList_SelectionChanged again to give us another chance to change
|
// ItemView_SelectionChanged again to give us another chance to change
|
||||||
// the selection from null -> something. Better to just update the
|
// the selection from null -> something. Better to just update the
|
||||||
// selection once, at the end of all the updating.
|
// selection once, at the end of all the updating.
|
||||||
if (ItemsList.SelectedItem is null)
|
if (ItemView.SelectedItem is null)
|
||||||
{
|
{
|
||||||
ItemsList.SelectedIndex = 0;
|
ItemView.SelectedIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always reset the selected item when the top-level list page changes
|
// Always reset the selected item when the top-level list page changes
|
||||||
// its items
|
// its items
|
||||||
if (!sender.IsNested)
|
if (!sender.IsNested)
|
||||||
{
|
{
|
||||||
ItemsList.SelectedIndex = 0;
|
ItemView.SelectedIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +339,7 @@ public sealed partial class ListPage : Page,
|
|||||||
var prop = e.PropertyName;
|
var prop = e.PropertyName;
|
||||||
if (prop == nameof(ViewModel.FilteredItems))
|
if (prop == nameof(ViewModel.FilteredItems))
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"ViewModel.FilteredItems {ItemsList.SelectedItem}");
|
Debug.WriteLine($"ViewModel.FilteredItems {ItemView.SelectedItem}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,12 +363,12 @@ public sealed partial class ListPage : Page,
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ItemsList_OnContextRequested(UIElement sender, ContextRequestedEventArgs e)
|
private void Items_OnContextRequested(UIElement sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
var (item, element) = e.OriginalSource switch
|
var (item, element) = e.OriginalSource switch
|
||||||
{
|
{
|
||||||
// caused by keyboard shortcut (e.g. Context menu key or Shift+F10)
|
// caused by keyboard shortcut (e.g. Context menu key or Shift+F10)
|
||||||
ListViewItem listViewItem => (ItemsList.ItemFromContainer(listViewItem) as ListItemViewModel, listViewItem),
|
SelectorItem selectorItem => (ItemView.ItemFromContainer(selectorItem) as ListItemViewModel, selectorItem),
|
||||||
|
|
||||||
// caused by right-click on the ListViewItem
|
// caused by right-click on the ListViewItem
|
||||||
FrameworkElement { DataContext: ListItemViewModel itemViewModel } frameworkElement => (itemViewModel, frameworkElement),
|
FrameworkElement { DataContext: ListItemViewModel itemViewModel } frameworkElement => (itemViewModel, frameworkElement),
|
||||||
@@ -346,9 +381,9 @@ public sealed partial class ListPage : Page,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ItemsList.SelectedItem != item)
|
if (ItemView.SelectedItem != item)
|
||||||
{
|
{
|
||||||
ItemsList.SelectedItem = item;
|
ItemView.SelectedItem = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewModel?.UpdateSelectedItemCommand.Execute(item);
|
ViewModel?.UpdateSelectedItemCommand.Execute(item);
|
||||||
@@ -371,14 +406,14 @@ public sealed partial class ListPage : Page,
|
|||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ItemsList_OnContextCanceled(UIElement sender, RoutedEventArgs e)
|
private void Items_OnContextCanceled(UIElement sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_ = DispatcherQueue.TryEnqueue(() => WeakReferenceMessenger.Default.Send<CloseContextMenuMessage>());
|
_ = DispatcherQueue.TryEnqueue(() => WeakReferenceMessenger.Default.Send<CloseContextMenuMessage>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ItemsList_PointerPressed(object sender, PointerRoutedEventArgs e) => _lastInputSource = InputSource.Pointer;
|
private void Items_PointerPressed(object sender, PointerRoutedEventArgs e) => _lastInputSource = InputSource.Pointer;
|
||||||
|
|
||||||
private void ItemsList_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
private void Items_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key is VirtualKey.Enter or VirtualKey.Space)
|
if (e.Key is VirtualKey.Enter or VirtualKey.Space)
|
||||||
{
|
{
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 316 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 584 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 613 KiB |
@@ -0,0 +1,66 @@
|
|||||||
|
// 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.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
|
namespace SamplePagesExtension;
|
||||||
|
|
||||||
|
internal sealed partial class SampleGalleryListPage : ListPage
|
||||||
|
{
|
||||||
|
public SampleGalleryListPage()
|
||||||
|
{
|
||||||
|
Icon = new IconInfo("\uE7C5");
|
||||||
|
Name = "Sample Gallery List Page";
|
||||||
|
GridProperties = new GalleryGridLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IListItem[] GetItems()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Sample Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/RedRectangle.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Another Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Swirls.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "More Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Win-Digital.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Stop With The Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/RedRectangle.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Another Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Space.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "More Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Swirls.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Stop With The Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Win-Digital.png"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,11 @@ public partial class SamplesListPage : ListPage
|
|||||||
Title = "Dynamic List Page Command",
|
Title = "Dynamic List Page Command",
|
||||||
Subtitle = "Changes the list of items in response to the typed query",
|
Subtitle = "Changes the list of items in response to the typed query",
|
||||||
},
|
},
|
||||||
|
new ListItem(new SampleGalleryListPage())
|
||||||
|
{
|
||||||
|
Title = "Gallery List Page Command",
|
||||||
|
Subtitle = "Displays items as a gallery",
|
||||||
|
},
|
||||||
new ListItem(new OnLoadPage())
|
new ListItem(new OnLoadPage())
|
||||||
{
|
{
|
||||||
Title = "Demo of OnLoad/OnUnload",
|
Title = "Demo of OnLoad/OnUnload",
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// 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.Extensions.Toolkit;
|
||||||
|
|
||||||
|
public partial class GalleryGridLayout : BaseObservable, IGalleryGridLayout
|
||||||
|
{
|
||||||
|
public virtual bool ShowTitle
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(nameof(ShowTitle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
= true;
|
||||||
|
|
||||||
|
public virtual bool ShowSubtitle
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(nameof(ShowSubtitle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
= true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// 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.Extensions.Toolkit;
|
||||||
|
|
||||||
|
public partial class MediumGridLayout : BaseObservable, IMediumGridLayout
|
||||||
|
{
|
||||||
|
public virtual bool ShowTitle
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(nameof(ShowTitle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
= true;
|
||||||
|
}
|
||||||
@@ -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.Extensions.Toolkit;
|
||||||
|
|
||||||
|
public partial class SmallGridLayout : BaseObservable, ISmallGridLayout
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -273,10 +273,26 @@ namespace Microsoft.CommandPalette.Extensions
|
|||||||
String Section { get; };
|
String Section { get; };
|
||||||
String TextToSuggest { get; };
|
String TextToSuggest { get; };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[uuid("50C6F080-1CBE-4CE4-B92F-DA2F116ED524")]
|
||||||
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
|
interface IGridProperties requires INotifyPropChanged { }
|
||||||
|
|
||||||
|
[uuid("05914D59-6ECB-4992-9CF2-5982B5120A26")]
|
||||||
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
|
interface ISmallGridLayout requires IGridProperties { }
|
||||||
|
|
||||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
interface IGridProperties {
|
interface IMediumGridLayout requires IGridProperties
|
||||||
Windows.Foundation.Size TileSize { get; };
|
{
|
||||||
|
Boolean ShowTitle { get; };
|
||||||
|
}
|
||||||
|
|
||||||
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
|
interface IGalleryGridLayout requires IGridProperties
|
||||||
|
{
|
||||||
|
Boolean ShowTitle { get; };
|
||||||
|
Boolean ShowSubtitle { get; };
|
||||||
}
|
}
|
||||||
|
|
||||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
|
|||||||
Reference in New Issue
Block a user