mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-21 06:18:34 +00:00
fix .Items and StyleExtensions
This commit is contained in:
@@ -179,6 +179,9 @@
|
||||
<ClInclude Include="SettingsExpander.h">
|
||||
<DependentUpon>SettingsExpander.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StyleExtensions.h">
|
||||
<DependentUpon>StyleExtensions.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControlSizeTrigger.h">
|
||||
<DependentUpon>ControlSizeTrigger.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@@ -409,6 +412,9 @@
|
||||
<ClCompile Include="SettingsExpander.cpp">
|
||||
<DependentUpon>SettingsExpander.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StyleExtensions.cpp">
|
||||
<DependentUpon>StyleExtensions.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControlSizeTrigger.cpp">
|
||||
<DependentUpon>ControlSizeTrigger.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -531,6 +537,9 @@
|
||||
<Midl Include="SettingsExpander.idl">
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="StyleExtensions.idl">
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="ControlSizeTrigger.idl">
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<Midl Include="SettingContainer.idl" />
|
||||
<Midl Include="SettingsCard.idl" />
|
||||
<Midl Include="SettingsExpander.idl" />
|
||||
<Midl Include="StyleExtensions.idl" />
|
||||
<Midl Include="ControlSizeTrigger.idl" />
|
||||
<Midl Include="CornerRadiusFilterConverters.idl" />
|
||||
<Midl Include="StringDefaultTemplateSelector.idl" />
|
||||
|
||||
@@ -5,11 +5,25 @@
|
||||
Default styles for SettingsCard and SettingsExpander.
|
||||
|
||||
These were ported from the Windows Community Toolkit
|
||||
(components/SettingsControls) and trimmed for use in this project:
|
||||
(components/SettingsControls) and adapted for this project's WinUI 2 host:
|
||||
- The Card's responsive RightWrapped/RightWrappedNoIcon visual states are
|
||||
omitted (they depend on tk:ControlSizeTrigger).
|
||||
- The Expander uses muxc:Expander's built-in template rather than the
|
||||
custom 500+ line template the toolkit ships.
|
||||
driven by local:ControlSizeTrigger (which this project ports from the
|
||||
toolkit's tk:ControlSizeTrigger).
|
||||
- The Card's PointerOver/Pressed visual states do NOT toggle
|
||||
muxc:AnimatedIcon.State on PART_RootGrid. The toolkit uses this to drive
|
||||
a Lottie chevron animation for the ActionIcon; WinUI 2 doesn't ship the
|
||||
underlying AnimatedChevronUpDownSmallVisualSource, so we use a static
|
||||
FontIcon ActionIcon throughout.
|
||||
- The Expander uses a hand-rolled muxc:Expander template
|
||||
(SettingsExpanderExpanderStyle below) so the header can pick up the
|
||||
matching SettingsCard hover/press brushes; the toolkit's WinUI 3 version
|
||||
also rolls its own template for the same reason.
|
||||
- The Expander template's ExpandDirection=Up branch is intentionally
|
||||
abridged because it references a WinUI 3 system style
|
||||
(ExpanderHeaderUpStyle) that isn't shipped in WinUI 2.
|
||||
- The chevron rotation is a RotateTransform driven by a DoubleAnimation
|
||||
(instead of the toolkit's Lottie AnimatedChevronUpDownSmallVisualSource,
|
||||
which is again WinUI 3 only).
|
||||
-->
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
@@ -111,6 +125,7 @@
|
||||
<x:Double x:Key="SettingsCardMinHeight">68</x:Double>
|
||||
<x:Double x:Key="SettingsCardDescriptionFontSize">12</x:Double>
|
||||
<x:Double x:Key="SettingsCardHeaderIconMaxSize">20</x:Double>
|
||||
<x:Double x:Key="SettingsCardLeftIndention">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardContentMinWidth">120</x:Double>
|
||||
<Thickness x:Key="SettingsCardHeaderIconMargin">2,0,20,0</Thickness>
|
||||
<Thickness x:Key="SettingsCardActionIconMargin">14,0,0,0</Thickness>
|
||||
@@ -208,12 +223,16 @@
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Grid.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</Grid.BackgroundTransition>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto"
|
||||
MinWidth="{StaticResource SettingsCardLeftIndention}" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -475,21 +494,34 @@
|
||||
IsClickEnabled="False" />
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<Grid>
|
||||
<!-- CornerRadius is filtered to bottom-only so the items panel visually stitches to the rounded bottom of the expander (matches WCT). -->
|
||||
<Grid CornerRadius="{Binding CornerRadius, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BottomCornerRadiusFilterConverter}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ContentPresenter Content="{TemplateBinding ItemsHeader}" />
|
||||
<muxc:ItemsRepeater x:Name="PART_ItemsRepeater"
|
||||
<!--
|
||||
Use ItemsControl + StackPanel rather than ItemsRepeater so every
|
||||
Items entry is realized eagerly. ItemsRepeater inside an Expander
|
||||
inside the Settings page's outer ScrollViewer only realized
|
||||
index 0 — the EffectiveViewport propagated through the ScrollViewer
|
||||
was 0 px at the moment the expand storyboard ran, and StackLayout's
|
||||
cache length is relative to viewport so 0 * anything is still 0.
|
||||
Settings expanders rarely hold more than a handful of cards so
|
||||
losing virtualization is a non-issue.
|
||||
-->
|
||||
<ItemsControl x:Name="PART_ItemsHost"
|
||||
Grid.Row="1"
|
||||
ItemTemplate="{Binding ItemTemplate, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
TabFocusNavigation="Local">
|
||||
<muxc:ItemsRepeater.Layout>
|
||||
<muxc:StackLayout Orientation="Vertical" />
|
||||
</muxc:ItemsRepeater.Layout>
|
||||
</muxc:ItemsRepeater>
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{Binding ItemTemplate, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
<ContentPresenter Grid.Row="2"
|
||||
Content="{TemplateBinding ItemsFooter}" />
|
||||
</Grid>
|
||||
@@ -589,6 +621,7 @@
|
||||
<VisualState x:Name="ExpandUp">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ExpanderHeader.CornerRadius" Value="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BottomCornerRadiusFilterConverter}}" />
|
||||
<Setter Target="ExpanderContent.Visibility" Value="Visible" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
@@ -609,13 +642,11 @@
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="CollapseDown">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ExpanderContent.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExpanderContent"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0:0:0.2"
|
||||
Value="Collapsed" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ExpanderContent"
|
||||
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)">
|
||||
<DiscreteDoubleKeyFrame KeyTime="0"
|
||||
@@ -630,14 +661,10 @@
|
||||
<VisualState x:Name="ExpandDown">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ExpanderHeader.CornerRadius" Value="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource TopCornerRadiusFilterConverter}}" />
|
||||
<Setter Target="ExpanderContent.Visibility" Value="Visible" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExpanderContent"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="Visible" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ExpanderContent"
|
||||
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)">
|
||||
<DiscreteDoubleKeyFrame KeyTime="0"
|
||||
@@ -650,13 +677,11 @@
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="CollapseUp">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ExpanderContent.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExpanderContent"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0:0:0.167"
|
||||
Value="Collapsed" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ExpanderContent"
|
||||
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)">
|
||||
<DiscreteDoubleKeyFrame KeyTime="0"
|
||||
|
||||
@@ -14,8 +14,6 @@ using namespace winrt::Windows::UI::Xaml::Automation;
|
||||
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
|
||||
namespace MUXC = winrt::Microsoft::UI::Xaml::Controls;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
DependencyProperty SettingsExpander::_HeaderProperty{ nullptr };
|
||||
@@ -30,13 +28,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
DependencyProperty SettingsExpander::_ItemTemplateProperty{ nullptr };
|
||||
DependencyProperty SettingsExpander::_ItemContainerStyleSelectorProperty{ nullptr };
|
||||
|
||||
static constexpr std::wstring_view PART_ItemsRepeater{ L"PART_ItemsRepeater" };
|
||||
static constexpr std::wstring_view PART_ItemsHost{ L"PART_ItemsHost" };
|
||||
|
||||
SettingsExpander::SettingsExpander()
|
||||
{
|
||||
_InitializeProperties();
|
||||
|
||||
Items(single_threaded_vector<IInspectable>());
|
||||
// Items is backed by an observable vector so post-construction mutations
|
||||
// (e.g. user code that adds cards after the expander is on screen) also
|
||||
// refresh the inner ItemsControl. The XAML parser populates Items via
|
||||
// Append before OnApplyTemplate runs, so the eager population path also
|
||||
// works.
|
||||
Items(single_threaded_observable_vector<IInspectable>());
|
||||
}
|
||||
|
||||
void SettingsExpander::_InitializeProperties()
|
||||
@@ -140,20 +143,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
_SetAccessibleName();
|
||||
|
||||
// Drop the prior template's repeater hookups before locating the new one.
|
||||
_elementPreparedRevoker.revoke();
|
||||
_itemsRepeater = nullptr;
|
||||
// Drop the prior template's host before locating the new one.
|
||||
_itemsHost = nullptr;
|
||||
|
||||
if (const auto child{ GetTemplateChild(hstring{ PART_ItemsRepeater }) })
|
||||
if (const auto child{ GetTemplateChild(hstring{ PART_ItemsHost }) })
|
||||
{
|
||||
_itemsRepeater = child.try_as<MUXC::ItemsRepeater>();
|
||||
_itemsHost = child.try_as<Controls::ItemsControl>();
|
||||
}
|
||||
|
||||
if (_itemsRepeater)
|
||||
if (_itemsHost)
|
||||
{
|
||||
_elementPreparedRevoker = _itemsRepeater.ElementPrepared(winrt::auto_revoke, { get_weak(), &SettingsExpander::_ItemsRepeater_ElementPrepared });
|
||||
|
||||
// Push our initial ItemsSource through to the repeater.
|
||||
// Push our initial ItemsSource through to the host and stamp item-container styles.
|
||||
_UpdateItemsSource();
|
||||
}
|
||||
}
|
||||
@@ -172,18 +172,75 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void SettingsExpander::_UpdateItemsSource()
|
||||
{
|
||||
if (!_itemsRepeater)
|
||||
if (!_itemsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// ItemsSource wins when set; otherwise fall back to the inline Items collection.
|
||||
if (const auto source{ ItemsSource() })
|
||||
{
|
||||
_itemsRepeater.ItemsSource(source);
|
||||
_itemsHost.ItemsSource(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
_itemsRepeater.ItemsSource(Items());
|
||||
_itemsHost.ItemsSource(Items());
|
||||
}
|
||||
|
||||
_ApplyItemContainerStyles();
|
||||
_SubscribeToItemsVectorChanged();
|
||||
}
|
||||
|
||||
// Watch our inline Items vector for changes so containers added after
|
||||
// OnApplyTemplate also get the proper SettingsCard item style. (When
|
||||
// ItemsSource is set, this is a no-op since the parser only touches Items.)
|
||||
void SettingsExpander::_SubscribeToItemsVectorChanged()
|
||||
{
|
||||
_itemsVectorChangedRevoker.revoke();
|
||||
|
||||
if (ItemsSource())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto observable{ Items().try_as<IObservableVector<IInspectable>>() })
|
||||
{
|
||||
_itemsVectorChangedRevoker = observable.VectorChanged(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) {
|
||||
if (const auto strongThis{ weakThis.get() })
|
||||
{
|
||||
strongThis->_ApplyItemContainerStyles();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the per-item style produced by ItemContainerStyleSelector. ItemsControl
|
||||
// only generates ContentPresenter containers for non-UIElement items, so when
|
||||
// SettingsCards are added directly the cards themselves are the "containers"
|
||||
// and we have to set Style on them ourselves. Mirrors the ElementPrepared path
|
||||
// we used when this was an ItemsRepeater.
|
||||
void SettingsExpander::_ApplyItemContainerStyles()
|
||||
{
|
||||
const auto selector{ ItemContainerStyleSelector() };
|
||||
if (!selector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto items{ Items() };
|
||||
if (!items)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < items.Size(); ++i)
|
||||
{
|
||||
if (const auto element{ items.GetAt(i).try_as<FrameworkElement>() })
|
||||
{
|
||||
if (element.ReadLocalValue(FrameworkElement::StyleProperty()) == DependencyProperty::UnsetValue())
|
||||
{
|
||||
element.Style(selector.SelectStyle(items.GetAt(i), element));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,22 +252,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsExpander::_ItemsRepeater_ElementPrepared(const MUXC::ItemsRepeater& /*sender*/, const MUXC::ItemsRepeaterElementPreparedEventArgs& args)
|
||||
{
|
||||
const auto selector{ ItemContainerStyleSelector() };
|
||||
if (!selector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (const auto element{ args.Element().try_as<FrameworkElement>() })
|
||||
{
|
||||
if (element.ReadLocalValue(FrameworkElement::StyleProperty()) == DependencyProperty::UnsetValue())
|
||||
{
|
||||
element.Style(selector.SelectStyle(nullptr, element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsExpander::_OnIsExpandedChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& e)
|
||||
{
|
||||
const auto obj{ d.try_as<Editor::SettingsExpander>() };
|
||||
|
||||
@@ -54,10 +54,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void _SetAccessibleName();
|
||||
void _UpdateItemsSource();
|
||||
void _ItemsRepeater_ElementPrepared(const Microsoft::UI::Xaml::Controls::ItemsRepeater& sender, const Microsoft::UI::Xaml::Controls::ItemsRepeaterElementPreparedEventArgs& args);
|
||||
void _SubscribeToItemsVectorChanged();
|
||||
void _ApplyItemContainerStyles();
|
||||
|
||||
Microsoft::UI::Xaml::Controls::ItemsRepeater _itemsRepeater{ nullptr };
|
||||
Microsoft::UI::Xaml::Controls::ItemsRepeater::ElementPrepared_revoker _elementPreparedRevoker;
|
||||
Windows::UI::Xaml::Controls::ItemsControl _itemsHost{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable>::VectorChanged_revoker _itemsVectorChangedRevoker;
|
||||
};
|
||||
|
||||
// AutomationPeer for SettingsExpander. Reports class name and falls back to
|
||||
@@ -75,7 +76,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
};
|
||||
|
||||
// StyleSelector used by SettingsExpander to choose between a clickable vs.
|
||||
// non-clickable SettingsCard container style for items in its ItemsRepeater.
|
||||
// non-clickable SettingsCard container style for items in its ItemsControl.
|
||||
// Ported from the Windows Community Toolkit's SettingsExpanderItemStyleSelector.
|
||||
struct SettingsExpanderItemStyleSelector : SettingsExpanderItemStyleSelectorT<SettingsExpanderItemStyleSelector>
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
[contentproperty("Content")]
|
||||
[default_interface] runtimeclass SettingsExpander : Windows.UI.Xaml.Controls.Control
|
||||
{
|
||||
SettingsExpander();
|
||||
|
||||
105
src/cascadia/TerminalSettingsEditor/StyleExtensions.cpp
Normal file
105
src/cascadia/TerminalSettingsEditor/StyleExtensions.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "StyleExtensions.h"
|
||||
#include "StyleExtensions.g.cpp"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
DependencyProperty StyleExtensions::_resourcesProperty{ nullptr };
|
||||
|
||||
DependencyProperty StyleExtensions::ResourcesProperty()
|
||||
{
|
||||
_InitializeProperties();
|
||||
return _resourcesProperty;
|
||||
}
|
||||
|
||||
void StyleExtensions::_InitializeProperties()
|
||||
{
|
||||
if (!_resourcesProperty)
|
||||
{
|
||||
_resourcesProperty = DependencyProperty::RegisterAttached(
|
||||
L"Resources",
|
||||
xaml_typename<ResourceDictionary>(),
|
||||
xaml_typename<Editor::StyleExtensions>(),
|
||||
PropertyMetadata{ nullptr, PropertyChangedCallback{ &StyleExtensions::_OnResourcesChanged } });
|
||||
}
|
||||
}
|
||||
|
||||
ResourceDictionary StyleExtensions::GetResources(const DependencyObject& target)
|
||||
{
|
||||
return target.GetValue(ResourcesProperty()).try_as<ResourceDictionary>();
|
||||
}
|
||||
|
||||
void StyleExtensions::SetResources(const DependencyObject& target, const ResourceDictionary& value)
|
||||
{
|
||||
target.SetValue(ResourcesProperty(), value);
|
||||
}
|
||||
|
||||
void StyleExtensions::_OnResourcesChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& e)
|
||||
{
|
||||
const auto frameworkElement{ d.try_as<FrameworkElement>() };
|
||||
if (!frameworkElement)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto elementResources{ frameworkElement.Resources() };
|
||||
if (!elementResources)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto mergedDictionaries{ elementResources.MergedDictionaries() };
|
||||
if (!mergedDictionaries)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the previously-merged dictionary (if any). Resource dictionaries
|
||||
// are reference types so IndexOf walks by identity, which is exactly what
|
||||
// we want: the same Setter.Value is shared across every element that uses
|
||||
// this Style, so the OldValue we see here is the exact instance we
|
||||
// appended last time the property changed.
|
||||
if (const auto oldDict{ e.OldValue().try_as<ResourceDictionary>() })
|
||||
{
|
||||
uint32_t index{ 0 };
|
||||
if (mergedDictionaries.IndexOf(oldDict, index))
|
||||
{
|
||||
mergedDictionaries.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new dictionary directly. We deliberately do NOT clone the way
|
||||
// the WCT C# port does: ResourceDictionary is sealed (so we can't tag a
|
||||
// private subclass like the toolkit), and a deep clone would have to
|
||||
// copy the inline dictionary's Source URI — which XAML may leave as a
|
||||
// relative string like "CommonResources.xaml" and which the runtime
|
||||
// then rejects with "is not a valid absolute URI". Sharing the same
|
||||
// dictionary across elements is fine: each element's MergedDictionaries
|
||||
// only holds a reference, and implicit styles are designed to be shared.
|
||||
if (const auto newDict{ e.NewValue().try_as<ResourceDictionary>() })
|
||||
{
|
||||
mergedDictionaries.Append(newDict);
|
||||
|
||||
if (frameworkElement.IsLoaded())
|
||||
{
|
||||
_ForceControlToReloadThemeResources(frameworkElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StyleExtensions::_ForceControlToReloadThemeResources(const FrameworkElement& element)
|
||||
{
|
||||
// Toggle RequestedTheme to force the framework to re-resolve all
|
||||
// {ThemeResource} bindings under this element. Required when the
|
||||
// style is applied to an already-loaded element. Matches the toolkit.
|
||||
const auto currentTheme{ element.RequestedTheme() };
|
||||
element.RequestedTheme(currentTheme == ElementTheme::Dark ? ElementTheme::Light : ElementTheme::Dark);
|
||||
element.RequestedTheme(currentTheme);
|
||||
}
|
||||
}
|
||||
31
src/cascadia/TerminalSettingsEditor/StyleExtensions.h
Normal file
31
src/cascadia/TerminalSettingsEditor/StyleExtensions.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StyleExtensions.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct StyleExtensions
|
||||
{
|
||||
StyleExtensions() = default;
|
||||
|
||||
static Windows::UI::Xaml::DependencyProperty ResourcesProperty();
|
||||
|
||||
static Windows::UI::Xaml::ResourceDictionary GetResources(const Windows::UI::Xaml::DependencyObject& target);
|
||||
static void SetResources(const Windows::UI::Xaml::DependencyObject& target, const Windows::UI::Xaml::ResourceDictionary& value);
|
||||
|
||||
private:
|
||||
static void _InitializeProperties();
|
||||
static void _OnResourcesChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
|
||||
static void _ForceControlToReloadThemeResources(const Windows::UI::Xaml::FrameworkElement& element);
|
||||
|
||||
static Windows::UI::Xaml::DependencyProperty _resourcesProperty;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(StyleExtensions);
|
||||
}
|
||||
19
src/cascadia/TerminalSettingsEditor/StyleExtensions.idl
Normal file
19
src/cascadia/TerminalSettingsEditor/StyleExtensions.idl
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
// Attached property that lets a Style merge an extra ResourceDictionary into
|
||||
// the target FrameworkElement's Resources.MergedDictionaries. Used by
|
||||
// SettingsCard / SettingsExpander to inject right-aligned ToggleSwitch and
|
||||
// sized Slider / ComboBox / TextBox defaults so children laid out inside a
|
||||
// card look right out of the box. Mirrors the Windows Community Toolkit's
|
||||
// CommunityToolkit.WinUI.Controls.StyleExtensions.
|
||||
static runtimeclass StyleExtensions
|
||||
{
|
||||
static Windows.UI.Xaml.DependencyProperty ResourcesProperty { get; };
|
||||
|
||||
static Windows.UI.Xaml.ResourceDictionary GetResources(Windows.UI.Xaml.DependencyObject target);
|
||||
static void SetResources(Windows.UI.Xaml.DependencyObject target, Windows.UI.Xaml.ResourceDictionary value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user