Introduce zoom, bell and progress indicators to ATS (#9041)

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/8912
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

## Detailed Description of the Pull Request / Additional comments
* Introduced separate DataTemplate for rendering TabPaletteItem
* Introduced DataTemplateSelector assigning this template
* Introduced indicators as observable properties of the terminal Tab
* Bound TabPaletteItem to these properties
* Ensured that BindingUpdate is always called on CmdPal to avoid races
This commit is contained in:
Don-Vito 2021-02-08 19:37:58 +02:00 committed by GitHub
parent b009d06bc3
commit a3c8beaba0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 302 additions and 78 deletions

View file

@ -52,11 +52,8 @@ namespace winrt::TerminalApp::implementation
RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) {
if (Visibility() == Visibility::Visible)
{
if (_filteredActionsView().Items().Size() == 0 && _filteredActions.Size() > 0)
{
// Force immediate binding update so we can select an item
Bindings->Update();
}
// Force immediate binding update so we can select an item
Bindings->Update();
if (_currentMode == CommandPaletteMode::TabSwitchMode)
{

View file

@ -36,6 +36,151 @@ the MIT License. See LICENSE in the project root for license information. -->
<local:HasNestedCommandsVisibilityConverter x:Key="HasNestedCommandsVisibilityConverter"/>
<model:IconPathConverter x:Key="IconSourceConverter"/>
<DataTemplate x:Key="GeneralItemTemplate" x:DataType="local:FilteredCommand">
<!-- This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line -->
<ListViewItem HorizontalContentAlignment="Stretch"
IsTabStop="False"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
<Grid HorizontalAlignment="Stretch" ColumnSpacing="8" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16"/>
<!-- icon -->
<ColumnDefinition Width="Auto"/>
<!-- command label -->
<ColumnDefinition Width="*"/>
<!-- key chord -->
<ColumnDefinition Width="16"/>
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<IconSourceElement
Grid.Column="0"
Width="16"
Height="16"
IconSource="{x:Bind Item.Icon,
Mode=OneWay,
Converter={StaticResource IconSourceConverter}}"/>
<local:HighlightedTextControl
Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}"/>
<!-- The block for the key chord is only visible
when there's actual text set as the label. See
CommandKeyChordVisibilityConverter for details. -->
<Border
Grid.Column="2"
Visibility="{x:Bind Item.KeyChordText,
Mode=OneWay,
Converter={StaticResource CommandKeyChordVisibilityConverter}}"
Style="{ThemeResource KeyChordBorderStyle}"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<TextBlock
Style="{ThemeResource KeyChordTextBlockStyle}"
FontSize="12"
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
</Border>
<!-- xE70E is ChevronUp. Rotated 90 degrees, it's _ChevronRight_ -->
<FontIcon
FontFamily="Segoe MDL2 Assets"
Glyph="&#xE70E;"
HorizontalAlignment="Right"
Visibility="{x:Bind Item,
Mode=OneWay,
Converter={StaticResource HasNestedCommandsVisibilityConverter}}"
Grid.Column="2">
<FontIcon.RenderTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="90"/>
</FontIcon.RenderTransform>
</FontIcon>
</Grid>
</ListViewItem>
</DataTemplate>
<DataTemplate x:Key="TabItemTemplate" x:DataType="local:FilteredCommand">
<!-- This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line -->
<ListViewItem HorizontalContentAlignment="Stretch"
IsTabStop="False"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
<Grid HorizontalAlignment="Stretch" ColumnSpacing="8" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16"/>
<!-- icon / progress -->
<ColumnDefinition Width="Auto"/>
<!-- command label -->
<ColumnDefinition Width="*"/>
<!-- gutter for indicators -->
<ColumnDefinition Width="Auto"/>
<!-- Indicators -->
<ColumnDefinition Width="16"/>
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<mux:ProgressRing
Grid.Column="0"
IsActive="{x:Bind Item.(local:TabPaletteItem.IsProgressRingActive), Mode=OneWay}"
Visibility="{x:Bind Item.(local:TabPaletteItem.IsProgressRingActive), Mode=OneWay}"
IsIndeterminate="{x:Bind Item.(local:TabPaletteItem.IsProgressRingIndeterminate), Mode=OneWay}"
Value="{x:Bind Item.(local:TabPaletteItem.ProgressValue), Mode=OneWay}"
MinHeight="0"
MinWidth="0"
Height="15"
Width="15"/>
<IconSourceElement
Grid.Column="0"
Width="16"
Height="16"
IconSource="{x:Bind Item.Icon,
Mode=OneWay,
Converter={StaticResource IconSourceConverter}}"/>
<local:HighlightedTextControl
Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}"/>
<StackPanel
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Orientation="Horizontal">
<FontIcon
FontFamily="Segoe MDL2 Assets"
Visibility="{x:Bind Item.(local:TabPaletteItem.BellIndicator), Mode=OneWay}"
Glyph="&#xEA8F;"
FontSize="12"
Margin="0,0,8,0"/>
<FontIcon
FontFamily="Segoe MDL2 Assets"
Visibility="{x:Bind Item.(local:TabPaletteItem.IsPaneZoomed), Mode=OneWay}"
Glyph="&#xE8A3;"
FontSize="12"
Margin="0,0,8,0"/>
</StackPanel>
</Grid>
</ListViewItem>
</DataTemplate>
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector" TabItemTemplate="{StaticResource TabItemTemplate}" GeneralItemTemplate="{StaticResource GeneralItemTemplate}"/>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<Style x:Key="CommandPaletteBackground" TargetType="Grid">
@ -279,77 +424,8 @@ the MIT License. See LICENSE in the project root for license information. -->
IsItemClickEnabled="True"
ItemClick="_listItemClicked"
PreviewKeyDown="_keyDownHandler"
ItemsSource="{x:Bind FilteredActions}">
<ItemsControl.ItemTemplate >
<DataTemplate x:DataType="local:FilteredCommand">
<!-- This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line -->
<ListViewItem HorizontalContentAlignment="Stretch"
IsTabStop="False"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
<Grid HorizontalAlignment="Stretch" ColumnSpacing="8" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16"/> <!-- icon -->
<ColumnDefinition Width="Auto"/> <!-- command label -->
<ColumnDefinition Width="*"/> <!-- key chord -->
<ColumnDefinition Width="16"/> <!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<IconSourceElement
Grid.Column="0"
Width="16"
Height="16"
IconSource="{x:Bind Item.Icon,
Mode=OneWay,
Converter={StaticResource IconSourceConverter}}"/>
<local:HighlightedTextControl
Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}"/>
<!-- The block for the key chord is only visible
when there's actual text set as the label. See
CommandKeyChordVisibilityConverter for details. -->
<Border
Grid.Column="2"
Visibility="{x:Bind Item.KeyChordText,
Mode=OneWay,
Converter={StaticResource CommandKeyChordVisibilityConverter}}"
Style="{ThemeResource KeyChordBorderStyle}"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<TextBlock
Style="{ThemeResource KeyChordTextBlockStyle}"
FontSize="12"
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
</Border>
<!-- xE70E is ChevronUp. Rotated 90 degrees, it's _ChevronRight_ -->
<FontIcon
FontFamily="Segoe MDL2 Assets"
Glyph="&#xE70E;"
HorizontalAlignment="Right"
Visibility="{x:Bind Item,
Mode=OneWay,
Converter={StaticResource HasNestedCommandsVisibilityConverter}}"
Grid.Column="2">
<FontIcon.RenderTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="90"/>
</FontIcon.RenderTransform>
</FontIcon>
</Grid>
</ListViewItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
ItemsSource="{x:Bind FilteredActions}"
ItemTemplateSelector="{StaticResource PaletteItemTemplateSelector}">
</ListView>
</Grid>

View file

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TabPaletteItem.h"
#include "PaletteItemTemplateSelector.h"
#include "PaletteItemTemplateSelector.g.cpp"
namespace winrt::TerminalApp::implementation
{
Windows::UI::Xaml::DataTemplate PaletteItemTemplateSelector::SelectTemplateCore(winrt::Windows::Foundation::IInspectable const& item, winrt::Windows::UI::Xaml::DependencyObject const& /*container*/)
{
return SelectTemplateCore(item);
}
// Method Description:
// - This method is called once command palette decides how to render a filtered command.
// Currently we support two ways to render command, that depend on its palette item type:
// - For TabPalette item we render an icon, a title, and some tab-related indicators like progress bar (as defined by TabItemTemplate)
// - All other items are currently rendered with icon, title and optional key-chord (as defined by GeneralItemTemplate)
// Arguments:
// - item - an instance of filtered command to render
// Return Value:
// - data template to use for rendering
Windows::UI::Xaml::DataTemplate PaletteItemTemplateSelector::SelectTemplateCore(winrt::Windows::Foundation::IInspectable const& item)
{
if (const auto filteredCommand{ item.try_as<winrt::TerminalApp::FilteredCommand>() })
{
if (filteredCommand.Item().try_as<winrt::TerminalApp::TabPaletteItem>())
{
return TabItemTemplate();
}
}
return GeneralItemTemplate();
}
}

View file

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "PaletteItemTemplateSelector.g.h"
#include "inc/cppwinrt_utils.h"
namespace winrt::TerminalApp::implementation
{
struct PaletteItemTemplateSelector : PaletteItemTemplateSelectorT<PaletteItemTemplateSelector>
{
PaletteItemTemplateSelector() = default;
Windows::UI::Xaml::DataTemplate SelectTemplateCore(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::DependencyObject const&);
Windows::UI::Xaml::DataTemplate SelectTemplateCore(winrt::Windows::Foundation::IInspectable const&);
GETSET_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, TabItemTemplate);
GETSET_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, GeneralItemTemplate);
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(PaletteItemTemplateSelector);
}

View file

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
[default_interface] runtimeclass PaletteItemTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector
{
PaletteItemTemplateSelector();
Windows.UI.Xaml.DataTemplate TabItemTemplate;
Windows.UI.Xaml.DataTemplate GeneralItemTemplate;
}
}

View file

@ -3,6 +3,7 @@
#include "pch.h"
#include "TabPaletteItem.h"
#include "TerminalTab.h"
#include <LibraryResources.h>
#include "TabPaletteItem.g.cpp"
@ -24,9 +25,21 @@ namespace winrt::TerminalApp::implementation
Name(tab.Title());
Icon(tab.Icon());
if (const auto terminalTab{ tab.try_as<winrt::TerminalApp::TerminalTab>() })
{
if (const auto tabImpl{ winrt::get_self<winrt::TerminalApp::implementation::TerminalTab>(terminalTab) })
{
IsProgressRingActive(tabImpl->IsProgressRingActive());
IsProgressRingIndeterminate(tabImpl->IsProgressRingIndeterminate());
ProgressValue(tabImpl->ProgressValue());
BellIndicator(tabImpl->BellIndicator());
IsPaneZoomed(tabImpl->IsPaneZoomed());
}
}
_tabChangedRevoker = tab.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) {
auto item{ weakThis.get() };
auto senderTab{ sender.try_as<TabBase>() };
auto senderTab{ sender.try_as<winrt::TerminalApp::TabBase>() };
if (item && senderTab)
{
@ -39,6 +52,33 @@ namespace winrt::TerminalApp::implementation
{
item->Icon(senderTab.Icon());
}
if (const auto terminalTab{ senderTab.try_as<winrt::TerminalApp::TerminalTab>() })
{
if (const auto tabImpl{ winrt::get_self<winrt::TerminalApp::implementation::TerminalTab>(terminalTab) })
{
if (changedProperty == L"IsProgressRingActive")
{
item->IsProgressRingActive(tabImpl->IsProgressRingActive());
}
else if (changedProperty == L"IsProgressRingIndeterminate")
{
item->IsProgressRingIndeterminate(tabImpl->IsProgressRingIndeterminate());
}
else if (changedProperty == L"ProgressValue")
{
item->ProgressValue(tabImpl->ProgressValue());
}
else if (changedProperty == L"IsPaneZoomed")
{
item->IsPaneZoomed(tabImpl->IsPaneZoomed());
}
else if (changedProperty == L"BellIndicator")
{
item->BellIndicator(tabImpl->BellIndicator());
}
}
}
}
});
}

View file

@ -19,9 +19,16 @@ namespace winrt::TerminalApp::implementation
return _tab.get();
}
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
private:
winrt::weak_ref<winrt::TerminalApp::TabBase> _tab;
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _tabChangedRevoker;
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _tabHeaderChangedRevoker;
};
}

View file

@ -11,5 +11,11 @@ namespace TerminalApp
TabPaletteItem(TabBase tab);
TabBase Tab { get; };
Boolean IsProgressRingActive { get; set; };
Boolean IsProgressRingIndeterminate { get; set; };
UInt32 ProgressValue { get; set; };
Boolean IsPaneZoomed { get; set; };
Boolean BellIndicator { get; set; };
}
}

View file

@ -78,6 +78,10 @@
<ClInclude Include="MinMaxCloseControl.h">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="PaletteItemTemplateSelector.h">
<DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="SettingsTab.h">
<DependentUpon>SettingsTab.idl</DependentUpon>
</ClInclude>
@ -150,6 +154,10 @@
<ClCompile Include="MinMaxCloseControl.cpp">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="PaletteItemTemplateSelector.cpp">
<DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="SettingsTab.cpp">
<DependentUpon>SettingsTab.idl</DependentUpon>
</ClCompile>
@ -228,6 +236,9 @@
<Midl Include="App.idl">
<DependentUpon>App.xaml</DependentUpon>
</Midl>
<Midl Include="PaletteItemTemplateSelector.idl">
<SubType>Designer</SubType>
</Midl>
<Midl Include="SettingsTab.idl" />
<Midl Include="PaletteItem.idl" />
<Midl Include="ShortcutActionDispatch.idl" />
@ -378,4 +389,4 @@
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
</Project>

View file

@ -289,6 +289,7 @@ namespace winrt::TerminalApp::implementation
if (auto tab{ weakThis.get() })
{
tab->_headerControl.BellIndicator(show);
BellIndicator(show);
}
}
@ -589,12 +590,14 @@ namespace winrt::TerminalApp::implementation
// Hide the tab icon (the progress ring is placed over it)
tab->HideIcon(true);
tab->_headerControl.IsProgressRingActive(true);
tab->IsProgressRingActive(true);
}
else
{
// Show the tab icon
tab->HideIcon(false);
tab->_headerControl.IsProgressRingActive(false);
tab->IsProgressRingActive(false);
}
}
});
@ -1094,6 +1097,7 @@ namespace winrt::TerminalApp::implementation
_rootPane->Maximize(_zoomedPane);
// Update the tab header to show the magnifying glass
_headerControl.IsPaneZoomed(true);
IsPaneZoomed(true);
Content(_zoomedPane->GetRootElement());
}
void TerminalTab::ExitZoom()
@ -1102,6 +1106,7 @@ namespace winrt::TerminalApp::implementation
_zoomedPane = nullptr;
// Update the tab header to hide the magnifying glass
_headerControl.IsPaneZoomed(false);
IsPaneZoomed(false);
Content(_rootPane->GetRootElement());
}

View file

@ -82,6 +82,12 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>);
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
OBSERVABLE_GETSET_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
private:
std::shared_ptr<Pane> _rootPane{ nullptr };
std::shared_ptr<Pane> _activePane{ nullptr };