Skip to content

Commit

Permalink
preview: Allow selecting custom control stlyes #12
Browse files Browse the repository at this point in the history
  • Loading branch information
roxk committed Apr 18, 2024
1 parent c059628 commit c22c47f
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 35 deletions.
17 changes: 16 additions & 1 deletion WinUI3XamlPreview/TestDep/Themes/Generic.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestDep">

<Style TargetType="local:DepCustomControl">
<Style x:Key="DefaultDepCustomControlStyle" TargetType="local:DepCustomControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:DepCustomControl">
Expand All @@ -17,4 +17,19 @@
</Setter.Value>
</Setter>
</Style>
<Style x:Key="AlternateDepCustomControlStyle" TargetType="local:DepCustomControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:DepCustomControl">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBlock Text="Hello from dep custom control (alternate)" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource DefaultDepCustomControlStyle}" TargetType="local:DepCustomControl" />
</ResourceDictionary>
14 changes: 13 additions & 1 deletion WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<ScrollView>
<ScrollView x:Name="titleBar" SizeChanged="titleBar_SizeChanged">
<Grid Padding="16,0,16,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox
x:Name="themeComboBox"
MinWidth="96"
HorizontalAlignment="Left"
VerticalAlignment="Center"
SelectedIndex="0"
SelectionChanged="themeComboBox_SelectionChanged">
Expand All @@ -31,6 +37,12 @@
<x:String>Light</x:String>
<x:String>Dark</x:String>
</ComboBox>
<ComboBox
x:Name="customControlComboBox"
Grid.Column="2"
VerticalAlignment="Center"
SelectionChanged="customControlComboBox_SelectionChanged"
SizeChanged="customControlComboBox_SizeChanged" />
</Grid>
</ScrollView>
<Grid Grid.Row="1">
Expand Down
97 changes: 79 additions & 18 deletions WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ double GetScaleComboBoxSelectedScalePercentage(muxc::ComboBox const& comboBox)

namespace winrt::WinUI3XamlPreview::implementation
{
MainPage::MainPage()
MainPage::MainPage() :
_customControlNames(winrt::single_threaded_observable_vector<winrt::hstring>())
{
_filePathChangedToken = Preview::InstanceInternal()->FilePathChanged({get_weak(), &MainPage::OnFilePathChanged});
}
Expand All @@ -31,7 +32,7 @@ namespace winrt::WinUI3XamlPreview::implementation
}
void MainPage::Window(mux::Window const& window)
{
_window = window;
_appWindow = window.AppWindow();
}
void MainPage::OnFilePathChanged(IInspectable const& sender, winrt::hstring const& e)
{
Expand All @@ -45,6 +46,7 @@ namespace winrt::WinUI3XamlPreview::implementation
if (auto singleElement = std::get_if<SingleElement>(&processResult); singleElement != nullptr)
{
elementWrapper().Child(singleElement->element);
UpdateCustomControlItems({});
}
else if (auto multipleElement = std::get_if<MultipleElement>(&processResult); multipleElement != nullptr)
{
Expand All @@ -55,7 +57,7 @@ namespace winrt::WinUI3XamlPreview::implementation
L"ResourceDictionary doesn't contain any control template");
return;
}
elementWrapper().Child(elements.front());
UpdateCustomControlItems(std::move(elements));
}
}
catch (...)
Expand Down Expand Up @@ -119,28 +121,33 @@ namespace winrt::WinUI3XamlPreview::implementation
}
void MainPage::SetRegionsForCustomTitleBar()
{
auto& window = _window;
if (window == nullptr)
auto& appWindow = _appWindow;
if (appWindow == nullptr)
{
return;
}
auto scale = Content().XamlRoot().RasterizationScale();
auto themeCb = themeComboBox();
auto transform = themeCb.TransformToVisual(nullptr);
auto scale = XamlRoot().RasterizationScale();
auto leftInset = _appWindow.TitleBar().LeftInset() / scale;
auto rightInset = _appWindow.TitleBar().RightInset() / scale;
titleBar().Margin({ leftInset, 0, rightInset, 0 });
wg::RectInt32 rectArray[] = { GetControlClientRect(themeComboBox()), GetControlClientRect(customControlComboBox()) };
auto nonClientInputSrc =
mui::InputNonClientPointerSource::GetForWindowId(_appWindow.Id());
nonClientInputSrc.SetRegionRects(mui::NonClientRegionKind::Passthrough, rectArray);
}
wg::RectInt32 MainPage::GetControlClientRect(mux::FrameworkElement const& element)
{
auto scale = element.XamlRoot().RasterizationScale();
auto transform = element.TransformToVisual(nullptr);
auto bounds = transform.TransformBounds(wf::Rect{ 0, 0,
float(themeCb.ActualWidth()),
float(themeCb.ActualHeight())
});
auto themeCbRect = wg::RectInt32{ int32_t(bounds.X * scale),
float(element.ActualWidth()),
float(element.ActualHeight())
});
return wg::RectInt32{ int32_t(bounds.X * scale),
int32_t(bounds.Y * scale),
int32_t(bounds.Width * scale),
int32_t(bounds.Height * scale)
};

wg::RectInt32 rectArray[] = { themeCbRect };
auto nonClientInputSrc =
mui::InputNonClientPointerSource::GetForWindowId(window.AppWindow().Id());
nonClientInputSrc.SetRegionRects(mui::NonClientRegionKind::Passthrough, rectArray);
}
winrt::hstring MainPage::ResolutionDisplay(wf::Numerics::float2 resolution)
{
Expand Down Expand Up @@ -285,6 +292,36 @@ void winrt::WinUI3XamlPreview::implementation::MainPage::Page_Loaded(winrt::Wind
slider.Minimum(minScale);
slider.Maximum(maxScale);
slider.Value(GetScaleComboBoxSelectedScalePercentage(comboBox));

customControlComboBox().ItemsSource(_customControlNames);
}

void winrt::WinUI3XamlPreview::implementation::MainPage::UpdateCustomControlItems(std::vector<CustomControlItem> items)
{
_customControlNames.Clear();
for (auto&& item : items)
{
_customControlNames.Append(item.displayName);
}
_customControlItems = std::move(items);
wf::IInspectable newSelected{};
auto lastSelectedName = _lastSelectedCustomControlName.try_as<winrt::hstring>();
for (auto&& candidate : _customControlNames)
{
if (candidate == lastSelectedName)
{
newSelected = box_value(candidate);
break;
}
}
// If can't select any previous selection, select the first one.
if (newSelected == nullptr && _customControlNames.Size() > 0)
{
newSelected = box_value(_customControlNames.GetAt(0));
}
_lastSelectedCustomControlName = newSelected;
customControlComboBox().SelectedItem(newSelected);
customControlComboBox().Visibility(_customControlNames.Size() > 0 ? mux::Visibility::Visible : mux::Visibility::Collapsed);
}

void winrt::WinUI3XamlPreview::implementation::MainPage::scaleSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e)
Expand Down Expand Up @@ -321,8 +358,32 @@ void winrt::WinUI3XamlPreview::implementation::MainPage::fitPageButton_Click(win
FitToPage();
}


void winrt::WinUI3XamlPreview::implementation::MainPage::themeComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e)
{
UpdateCurrentTheme(unbox_value<winrt::hstring>(themeComboBox().SelectedItem()));
}

void winrt::WinUI3XamlPreview::implementation::MainPage::customControlComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e)
{
customControlComboBox().Width(std::numeric_limits<double>::signaling_NaN());
auto index = customControlComboBox().SelectedIndex();
if (index < 0 || index >= _customControlItems.size())
{
return;
}
auto& element = _customControlItems[index];
elementWrapper().Child(element.element);
}

void winrt::WinUI3XamlPreview::implementation::MainPage::titleBar_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::SizeChangedEventArgs const& e)
{
SetRegionsForCustomTitleBar();
}

void winrt::WinUI3XamlPreview::implementation::MainPage::customControlComboBox_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::SizeChangedEventArgs const& e)
{
// ComboBox for some reason doesn't have intrinsic width when flyout is shown if width isn't set.
// Workaround: set measured actual width as width so even when flyout is shown the width stay constant.
customControlComboBox().Width(e.NewSize().Width);
SetRegionsForCustomTitleBar();
}
11 changes: 10 additions & 1 deletion WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "MainPage.g.h"
#include "XamlProcessor.h"

namespace winrt::WinUI3XamlPreview::implementation
{
Expand All @@ -16,11 +17,15 @@ namespace winrt::WinUI3XamlPreview::implementation
static winrt::hstring ResolutionDisplay(wf::IInspectable const& resolutionFloat2);
private:
void SetRegionsForCustomTitleBar();
static wg::RectInt32 GetControlClientRect(mux::FrameworkElement const& element);
static winrt::hstring ResolutionDisplay(wf::Numerics::float2 resolution);
mux::Window _window{};
muw::AppWindow _appWindow{nullptr};
winrt::hstring _currentTheme{};
double _currentScale{};
wf::Numerics::float2 _currentResolution{};
wfc::IVector<winrt::hstring> _customControlNames;
wf::IInspectable _lastSelectedCustomControlName{};
std::vector<CustomControlItem> _customControlItems;
friend struct MainPageT<MainPage>;
void LoadXaml(winrt::hstring const& xaml);
winrt::fire_and_forget OpenFileAndRead(winrt::hstring e);
Expand All @@ -29,6 +34,7 @@ namespace winrt::WinUI3XamlPreview::implementation
void heightInput_ValueChanged(winrt::Microsoft::UI::Xaml::Controls::NumberBox const& sender, winrt::Microsoft::UI::Xaml::Controls::NumberBoxValueChangedEventArgs const& args);
void scaleComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
void Page_Loaded(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void UpdateCustomControlItems(std::vector<CustomControlItem> items);
void UpdateScaleByComboBox();
void UpdateResolutionByComboBox();
void UpdateCurrentTheme(winrt::hstring const& theme);
Expand All @@ -43,6 +49,9 @@ namespace winrt::WinUI3XamlPreview::implementation
void CombobBoxSelectedItem(muxc::ComboBox const& comboBox, T&& value, D display);
void fitPageButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void themeComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
void customControlComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
void titleBar_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::SizeChangedEventArgs const& e);
void customControlComboBox_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::SizeChangedEventArgs const& e);
};
}

Expand Down
70 changes: 57 additions & 13 deletions WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,48 @@ bool IsAttrValid(wdxd::XmlDocument const& doc, wdxd::XmlElement const& element,
return false;
}
}
void VisitAndTrim(wdxd::XmlDocument const& doc, wdxd::IXmlNode const& candidate, std::vector<winrt::hstring>& templateTargetTypes)
struct CustomControlItemInfo
{
winrt::hstring styleKey;
winrt::hstring targetType;
};
winrt::hstring FindControlTemplateStyleKey(wdxd::XmlElement const& templateElement)
{
wdxd::XmlElement parentStyle{ nullptr };
wdxd::XmlElement candidate{ templateElement.ParentNode().try_as<wdxd::XmlElement>() };
while (candidate != nullptr)
{
if (candidate.LocalName().try_as<winrt::hstring>() == L"Style" && candidate.NamespaceUri().try_as<winrt::hstring>() == defaultNamespace)
{
parentStyle = candidate;
break;
}
candidate = candidate.ParentNode().try_as<wdxd::XmlElement>();
}
if (parentStyle == nullptr)
{
return L"";
}
auto attributes = parentStyle.Attributes();
for (auto&& attrNode : attributes)
{
if (attrNode.NodeType() != wdxd::NodeType::AttributeNode)
{
continue;
}
auto attr = attrNode.try_as<wdxd::XmlAttribute>();
if (attr == nullptr)
{
continue;
}
if (attr.NodeName() == L"x:Key")
{
return attr.Value();
}
}
return L"";
}
void VisitAndTrim(wdxd::XmlDocument const& doc, wdxd::IXmlNode const& candidate, std::vector<CustomControlItemInfo>& customControlItems)
{
for (auto&& node : candidate.ChildNodes())
{
Expand Down Expand Up @@ -103,7 +144,7 @@ void VisitAndTrim(wdxd::XmlDocument const& doc, wdxd::IXmlNode const& candidate,
auto isSetter = elemetName == L"Setter" && elementNamespace == defaultNamespace;
if (isSetter)
{
VisitAndTrim(doc, element, templateTargetTypes);
VisitAndTrim(doc, element, customControlItems);
continue;
}
for (uint32_t i = 0; i < attributes.Size(); ++i)
Expand All @@ -128,11 +169,13 @@ void VisitAndTrim(wdxd::XmlDocument const& doc, wdxd::IXmlNode const& candidate,
auto name = attr.NodeName();
if (isControlTemplate && attr.Name() == L"TargetType")
{
templateTargetTypes.emplace_back(attr.Value());
auto parentStyleKey{ FindControlTemplateStyleKey(element) };
// TODO: Distinct by key + target type
customControlItems.emplace_back(CustomControlItemInfo{ parentStyleKey, attr.Value() });
}
}
}
VisitAndTrim(doc, element, templateTargetTypes);
VisitAndTrim(doc, element, customControlItems);
}
}
std::pair<std::wstring_view, std::wstring_view> SplitTargetTypeToNamespaceAndLocalName(std::wstring_view targetType)
Expand All @@ -148,8 +191,8 @@ winrt::WinUI3XamlPreview::ProcessResult winrt::WinUI3XamlPreview::XamlProcessor:
{
wdxd::XmlDocument doc;
doc.LoadXml(xaml);
std::vector<winrt::hstring> templateTargetTypes;
VisitAndTrim(doc, doc, templateTargetTypes);
std::vector<CustomControlItemInfo> customControlItems;
VisitAndTrim(doc, doc, customControlItems);
auto processedXaml = doc.GetXml();
auto tree = muxm::XamlReader::Load(processedXaml);
if (auto element = tree.try_as<mux::UIElement>(); element != nullptr)
Expand All @@ -158,12 +201,12 @@ winrt::WinUI3XamlPreview::ProcessResult winrt::WinUI3XamlPreview::XamlProcessor:
}
if (auto dict = tree.try_as<mux::ResourceDictionary>(); dict != nullptr)
{
if (templateTargetTypes.empty())
if (customControlItems.empty())
{
return MultipleElement{};
}
std::vector<mux::UIElement> elements;
for (auto&& targetType : templateTargetTypes)
std::vector<CustomControlItem> items;
for (auto&& control : customControlItems)
{
auto elementRoot = doc.CreateElementNS(box_value(winrt::hstring(defaultNamespace)), L"Border");
auto namespaces{ GetNamespaces(doc) };
Expand All @@ -174,7 +217,7 @@ winrt::WinUI3XamlPreview::ProcessResult winrt::WinUI3XamlPreview::XamlProcessor:
auto elementResource = doc.CreateElementNS(box_value(winrt::hstring(defaultNamespace)), L"Border.Resources");
elementResource.AppendChild(doc.DocumentElement().CloneNode(true));
elementRoot.AppendChild(elementResource);
auto targetTypeParts{ SplitTargetTypeToNamespaceAndLocalName(targetType) };
auto targetTypeParts{ SplitTargetTypeToNamespaceAndLocalName(control.targetType) };
auto& targetTypePrefix{ targetTypeParts.first };
winrt::hstring targetTypeNamespaceUri;
for (auto&& aNamespace : namespaces)
Expand All @@ -191,21 +234,22 @@ winrt::WinUI3XamlPreview::ProcessResult winrt::WinUI3XamlPreview::XamlProcessor:
continue;
}
auto targetTypeElement = doc.CreateElementNS(box_value(targetTypeNamespaceUri), targetTypeParts.second);
targetTypeElement.SetAttribute(L"Style", (std::wstring(L"{StaticResource ") + control.styleKey + L"}").c_str());
elementRoot.AppendChild(targetTypeElement);
auto elementRootXaml = elementRoot.GetXml();
// TODO: Assign control template's style name
auto targetTypeTree = muxm::XamlReader::Load(elementRootXaml);
if (auto element = targetTypeTree.try_as<mux::UIElement>(); element != nullptr)
{
elements.emplace_back(std::move(element));
auto displayName = control.styleKey == L"" ? control.targetType : control.targetType + L" (" + control.styleKey + L")";
items.emplace_back(CustomControlItem{ std::move(displayName), std::move(element)});
}
else
{
// TODO: Log?
continue;
}
}
return MultipleElement{ std::move(elements) };
return MultipleElement{ std::move(items) };
}
throw hresult_not_implemented();
}
Loading

0 comments on commit c22c47f

Please sign in to comment.