diff --git a/WinUI3XamlPreview/TestDep/Themes/Generic.xaml b/WinUI3XamlPreview/TestDep/Themes/Generic.xaml
index 8a469a9..adf4dbf 100644
--- a/WinUI3XamlPreview/TestDep/Themes/Generic.xaml
+++ b/WinUI3XamlPreview/TestDep/Themes/Generic.xaml
@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestDep">
-
+
+
diff --git a/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml b/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml
index ee45b7b..c1d653e 100644
--- a/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml
+++ b/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml
@@ -14,11 +14,17 @@
-
+
+
+
+
+
+
@@ -31,6 +37,12 @@
Light
Dark
+
diff --git a/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.cpp b/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.cpp
index 303d7c4..9675611 100644
--- a/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.cpp
+++ b/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.cpp
@@ -21,7 +21,8 @@ double GetScaleComboBoxSelectedScalePercentage(muxc::ComboBox const& comboBox)
namespace winrt::WinUI3XamlPreview::implementation
{
- MainPage::MainPage()
+ MainPage::MainPage() :
+ _customControlNames(winrt::single_threaded_observable_vector())
{
_filePathChangedToken = Preview::InstanceInternal()->FilePathChanged({get_weak(), &MainPage::OnFilePathChanged});
}
@@ -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)
{
@@ -45,6 +46,7 @@ namespace winrt::WinUI3XamlPreview::implementation
if (auto singleElement = std::get_if(&processResult); singleElement != nullptr)
{
elementWrapper().Child(singleElement->element);
+ UpdateCustomControlItems({});
}
else if (auto multipleElement = std::get_if(&processResult); multipleElement != nullptr)
{
@@ -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 (...)
@@ -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)
{
@@ -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 items)
+{
+ _customControlNames.Clear();
+ for (auto&& item : items)
+ {
+ _customControlNames.Append(item.displayName);
+ }
+ _customControlItems = std::move(items);
+ wf::IInspectable newSelected{};
+ auto lastSelectedName = _lastSelectedCustomControlName.try_as();
+ 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)
@@ -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(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::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();
+}
diff --git a/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.h b/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.h
index 7abd313..ad70ffb 100644
--- a/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.h
+++ b/WinUI3XamlPreview/WinUI3XamlPreview/MainPage.xaml.h
@@ -1,6 +1,7 @@
#pragma once
#include "MainPage.g.h"
+#include "XamlProcessor.h"
namespace winrt::WinUI3XamlPreview::implementation
{
@@ -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 _customControlNames;
+ wf::IInspectable _lastSelectedCustomControlName{};
+ std::vector _customControlItems;
friend struct MainPageT;
void LoadXaml(winrt::hstring const& xaml);
winrt::fire_and_forget OpenFileAndRead(winrt::hstring e);
@@ -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 items);
void UpdateScaleByComboBox();
void UpdateResolutionByComboBox();
void UpdateCurrentTheme(winrt::hstring const& theme);
@@ -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);
};
}
diff --git a/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.cpp b/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.cpp
index aa0737e..d6bb516 100644
--- a/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.cpp
+++ b/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.cpp
@@ -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& 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() };
+ while (candidate != nullptr)
+ {
+ if (candidate.LocalName().try_as() == L"Style" && candidate.NamespaceUri().try_as() == defaultNamespace)
+ {
+ parentStyle = candidate;
+ break;
+ }
+ candidate = candidate.ParentNode().try_as();
+ }
+ 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();
+ 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& customControlItems)
{
for (auto&& node : candidate.ChildNodes())
{
@@ -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)
@@ -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 SplitTargetTypeToNamespaceAndLocalName(std::wstring_view targetType)
@@ -148,8 +191,8 @@ winrt::WinUI3XamlPreview::ProcessResult winrt::WinUI3XamlPreview::XamlProcessor:
{
wdxd::XmlDocument doc;
doc.LoadXml(xaml);
- std::vector templateTargetTypes;
- VisitAndTrim(doc, doc, templateTargetTypes);
+ std::vector customControlItems;
+ VisitAndTrim(doc, doc, customControlItems);
auto processedXaml = doc.GetXml();
auto tree = muxm::XamlReader::Load(processedXaml);
if (auto element = tree.try_as(); element != nullptr)
@@ -158,12 +201,12 @@ winrt::WinUI3XamlPreview::ProcessResult winrt::WinUI3XamlPreview::XamlProcessor:
}
if (auto dict = tree.try_as(); dict != nullptr)
{
- if (templateTargetTypes.empty())
+ if (customControlItems.empty())
{
return MultipleElement{};
}
- std::vector elements;
- for (auto&& targetType : templateTargetTypes)
+ std::vector items;
+ for (auto&& control : customControlItems)
{
auto elementRoot = doc.CreateElementNS(box_value(winrt::hstring(defaultNamespace)), L"Border");
auto namespaces{ GetNamespaces(doc) };
@@ -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)
@@ -191,13 +234,14 @@ 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(); 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
{
@@ -205,7 +249,7 @@ winrt::WinUI3XamlPreview::ProcessResult winrt::WinUI3XamlPreview::XamlProcessor:
continue;
}
}
- return MultipleElement{ std::move(elements) };
+ return MultipleElement{ std::move(items) };
}
throw hresult_not_implemented();
}
diff --git a/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.h b/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.h
index de6d605..7fce0e4 100644
--- a/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.h
+++ b/WinUI3XamlPreview/WinUI3XamlPreview/XamlProcessor.h
@@ -11,9 +11,15 @@ namespace winrt::WinUI3XamlPreview
mux::UIElement element;
};
+ struct CustomControlItem
+ {
+ winrt::hstring displayName;
+ mux::UIElement element;
+ };
+
struct MultipleElement
{
- std::vector elements;
+ std::vector elements;
};
using ProcessResult = std::variant;