diff --git a/src/cascadia/TerminalSettingsEditor/CommonResources.xaml b/src/cascadia/TerminalSettingsEditor/CommonResources.xaml index 2b290a3167..fbced179e7 100644 --- a/src/cascadia/TerminalSettingsEditor/CommonResources.xaml +++ b/src/cascadia/TerminalSettingsEditor/CommonResources.xaml @@ -8,14 +8,27 @@ xmlns:mtu="using:Microsoft.Terminal.UI" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> - + - + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.cpp b/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.cpp deleted file mode 100644 index 52ba58d0c5..0000000000 --- a/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "ControlSizeTrigger.h" -#include "ControlSizeTrigger.g.cpp" - -#include - -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::UI::Xaml; - -namespace winrt::Microsoft::Terminal::Settings::Editor::implementation -{ - DependencyProperty ControlSizeTrigger::_CanTriggerProperty{ nullptr }; - DependencyProperty ControlSizeTrigger::_MinWidthProperty{ nullptr }; - DependencyProperty ControlSizeTrigger::_MaxWidthProperty{ nullptr }; - DependencyProperty ControlSizeTrigger::_MinHeightProperty{ nullptr }; - DependencyProperty ControlSizeTrigger::_MaxHeightProperty{ nullptr }; - DependencyProperty ControlSizeTrigger::_TargetElementProperty{ nullptr }; - - ControlSizeTrigger::ControlSizeTrigger() - { - _InitializeProperties(); - } - - void ControlSizeTrigger::_InitializeProperties() - { - // Defaults mirror the toolkit: trigger is always evaluatable, bounds - // are wide open, no target element until one is bound. - if (!_CanTriggerProperty) - { - _CanTriggerProperty = DependencyProperty::Register( - L"CanTrigger", - xaml_typename(), - xaml_typename(), - PropertyMetadata{ box_value(true), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); - } - if (!_MinWidthProperty) - { - _MinWidthProperty = DependencyProperty::Register( - L"MinWidth", - xaml_typename(), - xaml_typename(), - PropertyMetadata{ box_value(0.0), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); - } - if (!_MaxWidthProperty) - { - _MaxWidthProperty = DependencyProperty::Register( - L"MaxWidth", - xaml_typename(), - xaml_typename(), - PropertyMetadata{ box_value(std::numeric_limits::infinity()), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); - } - if (!_MinHeightProperty) - { - _MinHeightProperty = DependencyProperty::Register( - L"MinHeight", - xaml_typename(), - xaml_typename(), - PropertyMetadata{ box_value(0.0), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); - } - if (!_MaxHeightProperty) - { - _MaxHeightProperty = DependencyProperty::Register( - L"MaxHeight", - xaml_typename(), - xaml_typename(), - PropertyMetadata{ box_value(std::numeric_limits::infinity()), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); - } - if (!_TargetElementProperty) - { - _TargetElementProperty = DependencyProperty::Register( - L"TargetElement", - xaml_typename(), - xaml_typename(), - PropertyMetadata{ nullptr, PropertyChangedCallback{ &ControlSizeTrigger::_OnTargetElementChanged } }); - } - } - - void ControlSizeTrigger::_OnTriggerInputChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/) - { - if (const auto obj{ d.try_as() }) - { - get_self(obj)->_UpdateTrigger(); - } - } - - void ControlSizeTrigger::_OnTargetElementChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& e) - { - const auto obj{ d.try_as() }; - if (!obj) - { - return; - } - const auto oldElement = e.OldValue().try_as(); - const auto newElement = e.NewValue().try_as(); - get_self(obj)->_UpdateTargetElement(oldElement, newElement); - } - - void ControlSizeTrigger::_UpdateTargetElement(const FrameworkElement& /*oldValue*/, const FrameworkElement& newValue) - { - // Revoking handles both unhooking the previous element and a null `newValue`. - _sizeChangedRevoker.revoke(); - if (newValue) - { - _sizeChangedRevoker = newValue.SizeChanged(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) { - if (const auto strongThis = weakThis.get()) - { - strongThis->_UpdateTrigger(); - } - }); - } - _UpdateTrigger(); - } - - void ControlSizeTrigger::_UpdateTrigger() - { - const auto target = TargetElement(); - if (!target || !CanTrigger()) - { - _isActive = false; - SetActive(false); - return; - } - - const auto width = target.ActualWidth(); - const auto height = target.ActualHeight(); - - const bool activate = - MinWidth() <= width && - width < MaxWidth() && - MinHeight() <= height && - height < MaxHeight(); - - _isActive = activate; - SetActive(activate); - } -} diff --git a/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.h b/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.h deleted file mode 100644 index 49e6a561f1..0000000000 --- a/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.h +++ /dev/null @@ -1,64 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- ControlSizeTrigger - -Abstract: -- A conditional state trigger that activates based on the size (width and/or - height) of a target FrameworkElement. Lets XAML visual states swap based on - the live size of a templated part. Ported from the Windows Community Toolkit - primitive `CommunityToolkit.WinUI.ControlSizeTrigger`. - - The trigger is "active" when: - MinWidth <= TargetElement.ActualWidth < MaxWidth AND - MinHeight <= TargetElement.ActualHeight < MaxHeight - - Defaults: MinWidth = MinHeight = 0; MaxWidth = MaxHeight = +inf, which makes - the trigger always active unless `CanTrigger` is false or `TargetElement` is - null. - -Author(s): -- Carlos Zamora - May 2026 (port from CommunityToolkit.WinUI.ControlSizeTrigger) - ---*/ - -#pragma once - -#include "ControlSizeTrigger.g.h" -#include "Utils.h" - -namespace winrt::Microsoft::Terminal::Settings::Editor::implementation -{ - struct ControlSizeTrigger : ControlSizeTriggerT - { - public: - ControlSizeTrigger(); - - bool IsActive() const { return _isActive; } - - DEPENDENCY_PROPERTY(bool, CanTrigger); - DEPENDENCY_PROPERTY(double, MinWidth); - DEPENDENCY_PROPERTY(double, MaxWidth); - DEPENDENCY_PROPERTY(double, MinHeight); - DEPENDENCY_PROPERTY(double, MaxHeight); - DEPENDENCY_PROPERTY(Windows::UI::Xaml::FrameworkElement, TargetElement); - - private: - static void _InitializeProperties(); - static void _OnTriggerInputChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); - static void _OnTargetElementChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); - - void _UpdateTargetElement(const Windows::UI::Xaml::FrameworkElement& oldValue, const Windows::UI::Xaml::FrameworkElement& newValue); - void _UpdateTrigger(); - - Windows::UI::Xaml::FrameworkElement::SizeChanged_revoker _sizeChangedRevoker; - bool _isActive{ false }; - }; -} - -namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation -{ - BASIC_FACTORY(ControlSizeTrigger); -} diff --git a/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.idl b/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.idl deleted file mode 100644 index 7bfabbc5ae..0000000000 --- a/src/cascadia/TerminalSettingsEditor/ControlSizeTrigger.idl +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -namespace Microsoft.Terminal.Settings.Editor -{ - [default_interface] runtimeclass ControlSizeTrigger : Windows.UI.Xaml.StateTriggerBase - { - ControlSizeTrigger(); - - Boolean CanTrigger; - static Windows.UI.Xaml.DependencyProperty CanTriggerProperty { get; }; - - Double MinWidth; - static Windows.UI.Xaml.DependencyProperty MinWidthProperty { get; }; - - Double MaxWidth; - static Windows.UI.Xaml.DependencyProperty MaxWidthProperty { get; }; - - Double MinHeight; - static Windows.UI.Xaml.DependencyProperty MinHeightProperty { get; }; - - Double MaxHeight; - static Windows.UI.Xaml.DependencyProperty MaxHeightProperty { get; }; - - Windows.UI.Xaml.FrameworkElement TargetElement; - static Windows.UI.Xaml.DependencyProperty TargetElementProperty { get; }; - - Boolean IsActive { get; }; - } -} diff --git a/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.cpp b/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.cpp deleted file mode 100644 index 0d9b9168b6..0000000000 --- a/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "CornerRadiusFilterConverters.h" -#include "CornerRadiusConverter.g.cpp" -#include "TopCornerRadiusFilterConverter.g.cpp" -#include "BottomCornerRadiusFilterConverter.g.cpp" - -using namespace winrt::Windows::UI::Xaml; - -namespace winrt::Microsoft::Terminal::Settings::Editor::implementation -{ - winrt::Windows::Foundation::IInspectable CornerRadiusConverter::Convert(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) - { - if (!value) - { - return value; - } - const auto cr = unbox_value_or(value, CornerRadius{ 0, 0, 0, 0 }); - return box_value(CornerRadius{ 0, 0, cr.BottomRight, cr.BottomLeft }); - } - - winrt::Windows::Foundation::IInspectable CornerRadiusConverter::ConvertBack(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) - { - return value; - } - - winrt::Windows::Foundation::IInspectable TopCornerRadiusFilterConverter::Convert(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) - { - if (!value) - { - return value; - } - const auto cr = unbox_value_or(value, CornerRadius{ 0, 0, 0, 0 }); - return box_value(CornerRadius{ cr.TopLeft, cr.TopRight, 0, 0 }); - } - - winrt::Windows::Foundation::IInspectable TopCornerRadiusFilterConverter::ConvertBack(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) - { - return value; - } - - winrt::Windows::Foundation::IInspectable BottomCornerRadiusFilterConverter::Convert(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) - { - if (!value) - { - return value; - } - const auto cr = unbox_value_or(value, CornerRadius{ 0, 0, 0, 0 }); - return box_value(CornerRadius{ 0, 0, cr.BottomRight, cr.BottomLeft }); - } - - winrt::Windows::Foundation::IInspectable BottomCornerRadiusFilterConverter::ConvertBack(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) - { - return value; - } -} diff --git a/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.h b/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.h deleted file mode 100644 index bd7b9b01c1..0000000000 --- a/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation -// Licensed under the MIT license. - -#pragma once - -#include "CornerRadiusConverter.g.h" -#include "TopCornerRadiusFilterConverter.g.h" -#include "BottomCornerRadiusFilterConverter.g.h" - -namespace winrt::Microsoft::Terminal::Settings::Editor::implementation -{ - struct CornerRadiusConverter : CornerRadiusConverterT - { - CornerRadiusConverter() = default; - - Windows::Foundation::IInspectable Convert(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); - Windows::Foundation::IInspectable ConvertBack(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); - }; - - struct TopCornerRadiusFilterConverter : TopCornerRadiusFilterConverterT - { - TopCornerRadiusFilterConverter() = default; - - Windows::Foundation::IInspectable Convert(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); - Windows::Foundation::IInspectable ConvertBack(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); - }; - - struct BottomCornerRadiusFilterConverter : BottomCornerRadiusFilterConverterT - { - BottomCornerRadiusFilterConverter() = default; - - Windows::Foundation::IInspectable Convert(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); - Windows::Foundation::IInspectable ConvertBack(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); - }; -} - -namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation -{ - BASIC_FACTORY(CornerRadiusConverter); - BASIC_FACTORY(TopCornerRadiusFilterConverter); - BASIC_FACTORY(BottomCornerRadiusFilterConverter); -} diff --git a/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.idl b/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.idl deleted file mode 100644 index 83b5a87a4c..0000000000 --- a/src/cascadia/TerminalSettingsEditor/CornerRadiusFilterConverters.idl +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -namespace Microsoft.Terminal.Settings.Editor -{ - [default_interface] runtimeclass CornerRadiusConverter : Windows.UI.Xaml.Data.IValueConverter - { - CornerRadiusConverter(); - } - - [default_interface] runtimeclass TopCornerRadiusFilterConverter : Windows.UI.Xaml.Data.IValueConverter - { - TopCornerRadiusFilterConverter(); - } - - [default_interface] runtimeclass BottomCornerRadiusFilterConverter : Windows.UI.Xaml.Data.IValueConverter - { - BottomCornerRadiusFilterConverter(); - } -} diff --git a/src/cascadia/TerminalSettingsEditor/Extensions.xaml b/src/cascadia/TerminalSettingsEditor/Extensions.xaml index 22d03d8832..c10a8fb145 100644 --- a/src/cascadia/TerminalSettingsEditor/Extensions.xaml +++ b/src/cascadia/TerminalSettingsEditor/Extensions.xaml @@ -201,7 +201,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - 16 - - - - - - - - - - - diff --git a/src/cascadia/TerminalSettingsEditor/SettingsCard.cpp b/src/cascadia/TerminalSettingsEditor/SettingsCard.cpp index 333a31ff8c..43f90c05e8 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingsCard.cpp +++ b/src/cascadia/TerminalSettingsEditor/SettingsCard.cpp @@ -5,7 +5,7 @@ #include "SettingsCard.h" #include "SettingsCard.g.cpp" #include "SettingsCardAutomationPeer.g.cpp" -#include "StyleExtensions.h" +#include "SettingsControlsHelpers.h" using namespace winrt::Windows::Foundation; using namespace winrt::Windows::System; diff --git a/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.cpp b/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.cpp new file mode 100644 index 0000000000..a0f40e0b46 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.cpp @@ -0,0 +1,276 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SettingsControlsHelpers.h" +#include "ControlSizeTrigger.g.cpp" +#include "TopCornerRadiusFilterConverter.g.cpp" +#include "BottomCornerRadiusFilterConverter.g.cpp" +#include "StringDefaultTemplateSelector.g.cpp" +#include "StyleExtensions.g.cpp" + +#include + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::UI::Xaml; + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ +#pragma region ControlSizeTrigger + + DependencyProperty ControlSizeTrigger::_CanTriggerProperty{ nullptr }; + DependencyProperty ControlSizeTrigger::_MinWidthProperty{ nullptr }; + DependencyProperty ControlSizeTrigger::_MaxWidthProperty{ nullptr }; + DependencyProperty ControlSizeTrigger::_MinHeightProperty{ nullptr }; + DependencyProperty ControlSizeTrigger::_MaxHeightProperty{ nullptr }; + DependencyProperty ControlSizeTrigger::_TargetElementProperty{ nullptr }; + + ControlSizeTrigger::ControlSizeTrigger() + { + _InitializeProperties(); + } + + void ControlSizeTrigger::_InitializeProperties() + { + // Defaults mirror the toolkit: trigger is always evaluatable, bounds + // are wide open, no target element until one is bound. + if (!_CanTriggerProperty) + { + _CanTriggerProperty = DependencyProperty::Register( + L"CanTrigger", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ box_value(true), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); + } + if (!_MinWidthProperty) + { + _MinWidthProperty = DependencyProperty::Register( + L"MinWidth", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ box_value(0.0), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); + } + if (!_MaxWidthProperty) + { + _MaxWidthProperty = DependencyProperty::Register( + L"MaxWidth", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ box_value(std::numeric_limits::infinity()), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); + } + if (!_MinHeightProperty) + { + _MinHeightProperty = DependencyProperty::Register( + L"MinHeight", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ box_value(0.0), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); + } + if (!_MaxHeightProperty) + { + _MaxHeightProperty = DependencyProperty::Register( + L"MaxHeight", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ box_value(std::numeric_limits::infinity()), PropertyChangedCallback{ &ControlSizeTrigger::_OnTriggerInputChanged } }); + } + if (!_TargetElementProperty) + { + _TargetElementProperty = DependencyProperty::Register( + L"TargetElement", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ nullptr, PropertyChangedCallback{ &ControlSizeTrigger::_OnTargetElementChanged } }); + } + } + + void ControlSizeTrigger::_OnTriggerInputChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/) + { + if (const auto obj{ d.try_as() }) + { + get_self(obj)->_UpdateTrigger(); + } + } + + void ControlSizeTrigger::_OnTargetElementChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& e) + { + const auto obj{ d.try_as() }; + if (!obj) + { + return; + } + const auto oldElement = e.OldValue().try_as(); + const auto newElement = e.NewValue().try_as(); + get_self(obj)->_UpdateTargetElement(oldElement, newElement); + } + + void ControlSizeTrigger::_UpdateTargetElement(const FrameworkElement& /*oldValue*/, const FrameworkElement& newValue) + { + // Revoking handles both unhooking the previous element and a null `newValue`. + _sizeChangedRevoker.revoke(); + if (newValue) + { + _sizeChangedRevoker = newValue.SizeChanged(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) { + if (const auto strongThis = weakThis.get()) + { + strongThis->_UpdateTrigger(); + } + }); + } + _UpdateTrigger(); + } + + void ControlSizeTrigger::_UpdateTrigger() + { + const auto target = TargetElement(); + if (!target || !CanTrigger()) + { + _isActive = false; + SetActive(false); + return; + } + + const auto width = target.ActualWidth(); + const auto height = target.ActualHeight(); + + const bool activate = + MinWidth() <= width && + width < MaxWidth() && + MinHeight() <= height && + height < MaxHeight(); + + _isActive = activate; + SetActive(activate); + } + +#pragma endregion +#pragma region TopBottomCornerRadiusFilters + + winrt::Windows::Foundation::IInspectable TopCornerRadiusFilterConverter::Convert(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) + { + if (!value) + { + return value; + } + const auto cr = unbox_value_or(value, CornerRadius{ 0, 0, 0, 0 }); + return box_value(CornerRadius{ cr.TopLeft, cr.TopRight, 0, 0 }); + } + + winrt::Windows::Foundation::IInspectable TopCornerRadiusFilterConverter::ConvertBack(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) + { + return value; + } + + winrt::Windows::Foundation::IInspectable BottomCornerRadiusFilterConverter::Convert(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) + { + if (!value) + { + return value; + } + const auto cr = unbox_value_or(value, CornerRadius{ 0, 0, 0, 0 }); + return box_value(CornerRadius{ 0, 0, cr.BottomRight, cr.BottomLeft }); + } + + winrt::Windows::Foundation::IInspectable BottomCornerRadiusFilterConverter::ConvertBack(const winrt::Windows::Foundation::IInspectable& value, const Interop::TypeName& /*targetType*/, const winrt::Windows::Foundation::IInspectable& /*parameter*/, const hstring& /*language*/) + { + return value; + } + +#pragma endregion +#pragma region StringDefaultTemplateSelector + + DataTemplate StringDefaultTemplateSelector::SelectTemplateCore(const IInspectable& item, const DependencyObject& /*container*/) + { + return SelectTemplateCore(item); + } + + DataTemplate StringDefaultTemplateSelector::SelectTemplateCore(const IInspectable& item) + { + if (const auto pv{ item.try_as() }; pv && pv.Type() == PropertyType::String) + { + return _StringTemplate; + } + return nullptr; + } + +#pragma endregion +#pragma region StyleExtensions + + ResourceDictionary StyleExtensions::_sharedImplicitStylesDictionary{ nullptr }; + + // Lazy singleton: loads SettingsControlsImplicitStyles.xaml exactly once + // for the process lifetime. We do NOT append this dictionary itself to any + // element's MergedDictionaries — that triggers "Element is already the + // child of another element" once a second element tries to merge it. + // Instead, EnsureImplicitStylesMergedInto copies the dictionary's entries + // (Style references) into the target's own Resources. Style instances are + // reference types but not UIElements, so sharing them across multiple + // elements' Resources collections is safe. + ResourceDictionary StyleExtensions::_SharedImplicitStylesDictionary() + { + if (!_sharedImplicitStylesDictionary) + { + try + { + auto dict{ ResourceDictionary{} }; + dict.Source(winrt::Windows::Foundation::Uri{ L"ms-appx:///Microsoft.Terminal.Settings.Editor/SettingsControlsImplicitStyles.xaml" }); + _sharedImplicitStylesDictionary = dict; + } + CATCH_LOG(); + } + return _sharedImplicitStylesDictionary; + } + + void StyleExtensions::EnsureImplicitStylesMergedInto(const FrameworkElement& target) + { + if (!target) + { + return; + } + + try + { + const auto resources{ target.Resources() }; + if (!resources) + { + return; + } + + // Idempotency marker: if we've already populated this element's + // Resources with the implicit styles, skip. Cheap to check + // (one hash lookup), independent of MergedDictionaries. + const auto markerKey{ box_value(hstring{ L"__SettingsControls_ImplicitStyles" }) }; + if (resources.HasKey(markerKey)) + { + return; + } + + const auto sharedDict{ _SharedImplicitStylesDictionary() }; + if (!sharedDict) + { + return; + } + + // Copy each entry (Style or other resource) from the shared loaded + // dictionary into the target's Resources. Style instances are + // reference types that can safely be shared across multiple + // element Resources collections (unlike UIElements, which have a + // single-parent constraint). We deliberately do NOT + // MergedDictionaries.Append(sharedDict) — that path throws + // "Element is already the child of another element" once a second + // element tries to merge the same shared dict. + for (const auto& kv : sharedDict) + { + if (!resources.HasKey(kv.Key())) + { + resources.Insert(kv.Key(), kv.Value()); + } + } + resources.Insert(markerKey, box_value(true)); + } + CATCH_LOG(); + } + +#pragma endregion +} diff --git a/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.h b/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.h new file mode 100644 index 0000000000..2990f6f1c4 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.h @@ -0,0 +1,100 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- SettingsControlsHelpers + +Abstract: +- Helper types backing the Windows Community Toolkit port of SettingsCard/SettingsExpander. + Grouped into a single file so the port boundary is obvious. + +Author(s): +- Carlos Zamora - June 2026 + +--*/ + +#pragma once + +#include "ControlSizeTrigger.g.h" +#include "TopCornerRadiusFilterConverter.g.h" +#include "BottomCornerRadiusFilterConverter.g.h" +#include "StringDefaultTemplateSelector.g.h" +#include "StyleExtensions.g.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct ControlSizeTrigger : ControlSizeTriggerT + { + public: + ControlSizeTrigger(); + + bool IsActive() const { return _isActive; } + + DEPENDENCY_PROPERTY(bool, CanTrigger); + DEPENDENCY_PROPERTY(double, MinWidth); + DEPENDENCY_PROPERTY(double, MaxWidth); + DEPENDENCY_PROPERTY(double, MinHeight); + DEPENDENCY_PROPERTY(double, MaxHeight); + DEPENDENCY_PROPERTY(Windows::UI::Xaml::FrameworkElement, TargetElement); + + private: + static void _InitializeProperties(); + static void _OnTriggerInputChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); + static void _OnTargetElementChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); + + void _UpdateTargetElement(const Windows::UI::Xaml::FrameworkElement& oldValue, const Windows::UI::Xaml::FrameworkElement& newValue); + void _UpdateTrigger(); + + Windows::UI::Xaml::FrameworkElement::SizeChanged_revoker _sizeChangedRevoker; + bool _isActive{ false }; + }; + + struct TopCornerRadiusFilterConverter : TopCornerRadiusFilterConverterT + { + TopCornerRadiusFilterConverter() = default; + + Windows::Foundation::IInspectable Convert(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); + Windows::Foundation::IInspectable ConvertBack(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); + }; + + struct BottomCornerRadiusFilterConverter : BottomCornerRadiusFilterConverterT + { + BottomCornerRadiusFilterConverter() = default; + + Windows::Foundation::IInspectable Convert(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); + Windows::Foundation::IInspectable ConvertBack(const Windows::Foundation::IInspectable& value, const Windows::UI::Xaml::Interop::TypeName& targetType, const Windows::Foundation::IInspectable& parameter, const hstring& language); + }; + + struct StringDefaultTemplateSelector : StringDefaultTemplateSelectorT + { + StringDefaultTemplateSelector() = default; + + Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item, const winrt::Windows::UI::Xaml::DependencyObject& container); + Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item); + + WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, StringTemplate, nullptr); + }; + + struct StyleExtensions + { + StyleExtensions() = default; + + static void EnsureImplicitStylesMergedInto(const Windows::UI::Xaml::FrameworkElement& target); + + private: + static Windows::UI::Xaml::ResourceDictionary _SharedImplicitStylesDictionary(); + + static Windows::UI::Xaml::ResourceDictionary _sharedImplicitStylesDictionary; + }; +} + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(ControlSizeTrigger); + BASIC_FACTORY(TopCornerRadiusFilterConverter); + BASIC_FACTORY(BottomCornerRadiusFilterConverter); + BASIC_FACTORY(StringDefaultTemplateSelector); + BASIC_FACTORY(StyleExtensions); +} diff --git a/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.idl b/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.idl new file mode 100644 index 0000000000..5aa0306781 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/SettingsControlsHelpers.idl @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Helper types backing the Windows Community Toolkit port of SettingsCard/SettingsExpander. +// These are grouped together so the port boundary is obvious. +namespace Microsoft.Terminal.Settings.Editor +{ + // A conditional state trigger that activates based on the size of a target + // FrameworkElement. Ported from CommunityToolkit.WinUI.ControlSizeTrigger. + [default_interface] runtimeclass ControlSizeTrigger : Windows.UI.Xaml.StateTriggerBase + { + ControlSizeTrigger(); + + Boolean CanTrigger; + static Windows.UI.Xaml.DependencyProperty CanTriggerProperty { get; }; + + Double MinWidth; + static Windows.UI.Xaml.DependencyProperty MinWidthProperty { get; }; + + Double MaxWidth; + static Windows.UI.Xaml.DependencyProperty MaxWidthProperty { get; }; + + Double MinHeight; + static Windows.UI.Xaml.DependencyProperty MinHeightProperty { get; }; + + Double MaxHeight; + static Windows.UI.Xaml.DependencyProperty MaxHeightProperty { get; }; + + Windows.UI.Xaml.FrameworkElement TargetElement; + static Windows.UI.Xaml.DependencyProperty TargetElementProperty { get; }; + + Boolean IsActive { get; }; + } + + // Filter a CornerRadius down to only its top/bottom corners. Used + // by the SettingsExpander template so the header and items panel visually + // stitch together with rounded outer corners only. + [default_interface] runtimeclass TopCornerRadiusFilterConverter : Windows.UI.Xaml.Data.IValueConverter + { + TopCornerRadiusFilterConverter(); + } + + [default_interface] runtimeclass BottomCornerRadiusFilterConverter : Windows.UI.Xaml.Data.IValueConverter + { + BottomCornerRadiusFilterConverter(); + } + + // Selects a template only when the bound item is a string. Used by + // SettingsCard/SettingsExpander to render string Header/Description content. + [default_interface] runtimeclass StringDefaultTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector + { + StringDefaultTemplateSelector(); + + Windows.UI.Xaml.DataTemplate StringTemplate; + } + + // Injects the project's SettingsControlsImplicitStyles.xaml dictionary into a + // SettingsCard/SettingsExpander's Resources so children (e.g. ToggleSwitch) + // pick up card-appropriate defaults. Loaded from C++ rather than the WCT + // attached-property Setter pattern, which crashes WinUI 2 in this codebase. + static runtimeclass StyleExtensions + { + static void EnsureImplicitStylesMergedInto(Windows.UI.Xaml.FrameworkElement target); + } +} diff --git a/src/cascadia/TerminalSettingsEditor/SettingsControlsStyle.xaml b/src/cascadia/TerminalSettingsEditor/SettingsControlsStyle.xaml index a57be63b3b..e2aedc41d7 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingsControlsStyle.xaml +++ b/src/cascadia/TerminalSettingsEditor/SettingsControlsStyle.xaml @@ -161,7 +161,6 @@ --> Show all settings - diff --git a/src/cascadia/TerminalSettingsEditor/SettingsExpander.cpp b/src/cascadia/TerminalSettingsEditor/SettingsExpander.cpp index 1048a66b35..b9b16151d6 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingsExpander.cpp +++ b/src/cascadia/TerminalSettingsEditor/SettingsExpander.cpp @@ -6,7 +6,7 @@ #include "SettingsExpander.g.cpp" #include "SettingsExpanderAutomationPeer.g.cpp" #include "SettingsExpanderItemStyleSelector.g.cpp" -#include "StyleExtensions.h" +#include "SettingsControlsHelpers.h" using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; diff --git a/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.cpp b/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.cpp deleted file mode 100644 index 4c17b18f2f..0000000000 --- a/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "StringDefaultTemplateSelector.h" -#include "StringDefaultTemplateSelector.g.cpp" - -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::UI::Xaml; - -namespace winrt::Microsoft::Terminal::Settings::Editor::implementation -{ - DataTemplate StringDefaultTemplateSelector::SelectTemplateCore(const IInspectable& item, const DependencyObject& /*container*/) - { - return SelectTemplateCore(item); - } - - DataTemplate StringDefaultTemplateSelector::SelectTemplateCore(const IInspectable& item) - { - if (const auto pv{ item.try_as() }; pv && pv.Type() == PropertyType::String) - { - return _StringTemplate; - } - return nullptr; - } -} diff --git a/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.h b/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.h deleted file mode 100644 index 56b7f4a355..0000000000 --- a/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation -// Licensed under the MIT license. - -#pragma once - -#include "StringDefaultTemplateSelector.g.h" - -namespace winrt::Microsoft::Terminal::Settings::Editor::implementation -{ - struct StringDefaultTemplateSelector : StringDefaultTemplateSelectorT - { - StringDefaultTemplateSelector() = default; - - Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item, const winrt::Windows::UI::Xaml::DependencyObject& container); - Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item); - - WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, StringTemplate, nullptr); - }; -} - -namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation -{ - BASIC_FACTORY(StringDefaultTemplateSelector); -} diff --git a/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.idl b/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.idl deleted file mode 100644 index db8562d8ac..0000000000 --- a/src/cascadia/TerminalSettingsEditor/StringDefaultTemplateSelector.idl +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -namespace Microsoft.Terminal.Settings.Editor -{ - [default_interface] runtimeclass StringDefaultTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector - { - StringDefaultTemplateSelector(); - - Windows.UI.Xaml.DataTemplate StringTemplate; - } -} diff --git a/src/cascadia/TerminalSettingsEditor/StyleExtensions.cpp b/src/cascadia/TerminalSettingsEditor/StyleExtensions.cpp deleted file mode 100644 index 4a9bb2b876..0000000000 --- a/src/cascadia/TerminalSettingsEditor/StyleExtensions.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// 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 }; - ResourceDictionary StyleExtensions::_sharedImplicitStylesDictionary{ nullptr }; - - DependencyProperty StyleExtensions::ResourcesProperty() - { - _InitializeProperties(); - return _resourcesProperty; - } - - void StyleExtensions::_InitializeProperties() - { - if (!_resourcesProperty) - { - _resourcesProperty = DependencyProperty::RegisterAttached( - L"Resources", - xaml_typename(), - xaml_typename(), - PropertyMetadata{ nullptr, PropertyChangedCallback{ &StyleExtensions::_OnResourcesChanged } }); - } - } - - ResourceDictionary StyleExtensions::GetResources(const DependencyObject& target) - { - return target.GetValue(ResourcesProperty()).try_as(); - } - - 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() }; - 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() }) - { - 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() }) - { - 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); - } - - // Lazy singleton: loads SettingsControlsImplicitStyles.xaml exactly once - // for the process lifetime. We do NOT append this dictionary itself to any - // element's MergedDictionaries — that triggers "Element is already the - // child of another element" once a second element tries to merge it. - // Instead, EnsureImplicitStylesMergedInto copies the dictionary's entries - // (Style references) into the target's own Resources. Style instances are - // reference types but not UIElements, so sharing them across multiple - // elements' Resources collections is safe. - ResourceDictionary StyleExtensions::_SharedImplicitStylesDictionary() - { - if (!_sharedImplicitStylesDictionary) - { - try - { - auto dict{ ResourceDictionary{} }; - dict.Source(winrt::Windows::Foundation::Uri{ L"ms-appx:///Microsoft.Terminal.Settings.Editor/SettingsControlsImplicitStyles.xaml" }); - _sharedImplicitStylesDictionary = dict; - } - CATCH_LOG(); - } - return _sharedImplicitStylesDictionary; - } - - void StyleExtensions::EnsureImplicitStylesMergedInto(const FrameworkElement& target) - { - if (!target) - { - return; - } - - try - { - const auto resources{ target.Resources() }; - if (!resources) - { - return; - } - - // Idempotency marker: if we've already populated this element's - // Resources with the implicit styles, skip. Cheap to check - // (one hash lookup), independent of MergedDictionaries. - const auto markerKey{ box_value(hstring{ L"__SettingsControls_ImplicitStyles" }) }; - if (resources.HasKey(markerKey)) - { - return; - } - - const auto sharedDict{ _SharedImplicitStylesDictionary() }; - if (!sharedDict) - { - return; - } - - // Copy each entry (Style or other resource) from the shared loaded - // dictionary into the target's Resources. Style instances are - // reference types that can safely be shared across multiple - // element Resources collections (unlike UIElements, which have a - // single-parent constraint). We deliberately do NOT - // MergedDictionaries.Append(sharedDict) — that path throws - // "Element is already the child of another element" once a second - // element tries to merge the same shared dict. - for (const auto& kv : sharedDict) - { - if (!resources.HasKey(kv.Key())) - { - resources.Insert(kv.Key(), kv.Value()); - } - } - resources.Insert(markerKey, box_value(true)); - } - CATCH_LOG(); - } -} diff --git a/src/cascadia/TerminalSettingsEditor/StyleExtensions.h b/src/cascadia/TerminalSettingsEditor/StyleExtensions.h deleted file mode 100644 index 764d93d7ce..0000000000 --- a/src/cascadia/TerminalSettingsEditor/StyleExtensions.h +++ /dev/null @@ -1,35 +0,0 @@ -// 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); - - static void EnsureImplicitStylesMergedInto(const Windows::UI::Xaml::FrameworkElement& target); - - 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::ResourceDictionary _SharedImplicitStylesDictionary(); - - static Windows::UI::Xaml::DependencyProperty _resourcesProperty; - static Windows::UI::Xaml::ResourceDictionary _sharedImplicitStylesDictionary; - }; -} - -namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation -{ - BASIC_FACTORY(StyleExtensions); -} diff --git a/src/cascadia/TerminalSettingsEditor/StyleExtensions.idl b/src/cascadia/TerminalSettingsEditor/StyleExtensions.idl deleted file mode 100644 index 716151816e..0000000000 --- a/src/cascadia/TerminalSettingsEditor/StyleExtensions.idl +++ /dev/null @@ -1,28 +0,0 @@ -// 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); - - // Idempotently merges the project's SettingsControlsImplicitStyles.xaml - // dictionary into the target's Resources.MergedDictionaries. Called from - // SettingsCard / SettingsExpander OnApplyTemplate to give children - // (ToggleSwitch / Slider / ComboBox / TextBox) sensible Windows 11 - // defaults without each call site setting Style explicitly. Loaded from - // C++ rather than via the attached DP because the WCT Setter.Value - // pattern crashes WinUI 2 in this codebase. - static void EnsureImplicitStylesMergedInto(Windows.UI.Xaml.FrameworkElement target); - } -} diff --git a/tools/GenerateSettingsIndex.ps1 b/tools/GenerateSettingsIndex.ps1 index 136f8e95d8..8e43134f36 100644 --- a/tools/GenerateSettingsIndex.ps1 +++ b/tools/GenerateSettingsIndex.ps1 @@ -210,7 +210,7 @@ foreach ($xamlFile in Get-ChildItem -Path $SourceDir -Filter *.xaml) } # Extract Name via GetAttribute to avoid PowerShell's XML integration - # returning the element name (e.g. "local:SettingContainer") when x:Name is absent. + # returning the element name (e.g. "local:SettingsCard") when x:Name is absent. $name = $settingContainer.GetAttribute("x:Name") if ([string]::IsNullOrEmpty($name)) {