aboutsummaryrefslogtreecommitdiff
path: root/src/ThemeBuilder/components/properties
diff options
context:
space:
mode:
Diffstat (limited to 'src/ThemeBuilder/components/properties')
-rw-r--r--src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.cpp19
-rw-r--r--src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.h29
-rw-r--r--src/ThemeBuilder/components/properties/ColorPropertyEditor.cpp48
-rw-r--r--src/ThemeBuilder/components/properties/ColorPropertyEditor.h36
-rw-r--r--src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.cpp42
-rw-r--r--src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.h28
-rw-r--r--src/ThemeBuilder/components/properties/FontPropertyEditor.cpp60
-rw-r--r--src/ThemeBuilder/components/properties/FontPropertyEditor.h38
-rw-r--r--src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.cpp37
-rw-r--r--src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.h35
-rw-r--r--src/ThemeBuilder/components/properties/OptionalPropertyEditor.h65
-rw-r--r--src/ThemeBuilder/components/properties/PointPropertyEditor.cpp38
-rw-r--r--src/ThemeBuilder/components/properties/PointPropertyEditor.h35
-rw-r--r--src/ThemeBuilder/components/properties/SelectPropertyEditor.cpp15
-rw-r--r--src/ThemeBuilder/components/properties/SelectPropertyEditor.h42
-rw-r--r--src/ThemeBuilder/components/properties/TextPropertyEditor.cpp22
-rw-r--r--src/ThemeBuilder/components/properties/TextPropertyEditor.h31
-rw-r--r--src/ThemeBuilder/components/properties/ThicknessPropertyEditor.cpp34
-rw-r--r--src/ThemeBuilder/components/properties/ThicknessPropertyEditor.h31
19 files changed, 685 insertions, 0 deletions
diff --git a/src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.cpp b/src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.cpp
new file mode 100644
index 00000000..fb6f4705
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.cpp
@@ -0,0 +1,19 @@
+#include "CheckBoxPropertyEditor.h"
+
+namespace cru::theme_builder::components::properties {
+CheckBoxPropertyEditor::CheckBoxPropertyEditor() {
+ container_.SetFlexDirection(ui::controls::FlexDirection::Horizontal);
+ container_.AddChild(&label_);
+ container_.AddChild(&check_box_);
+
+ check_box_.CheckedChangeEvent()->AddSpyOnlyHandler(
+ [this] { RaiseChangeEvent(); });
+}
+
+CheckBoxPropertyEditor::~CheckBoxPropertyEditor() {}
+
+void CheckBoxPropertyEditor::SetValue(bool value, bool trigger_change) {
+ if (!trigger_change) SuppressNextChangeEvent();
+ check_box_.SetChecked(value);
+}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.h b/src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.h
new file mode 100644
index 00000000..f78ed6c9
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/CheckBoxPropertyEditor.h
@@ -0,0 +1,29 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/ui/controls/CheckBox.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+
+namespace cru::theme_builder::components::properties {
+class CheckBoxPropertyEditor : public Editor {
+ public:
+ using PropertyType = bool;
+
+ CheckBoxPropertyEditor();
+ ~CheckBoxPropertyEditor() override;
+
+ public:
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ bool GetValue() const { return check_box_.IsChecked(); }
+ void SetValue(bool value, bool trigger_change = true);
+
+ private:
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::controls::CheckBox check_box_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/ColorPropertyEditor.cpp b/src/ThemeBuilder/components/properties/ColorPropertyEditor.cpp
new file mode 100644
index 00000000..e9e486ac
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/ColorPropertyEditor.cpp
@@ -0,0 +1,48 @@
+#include "ColorPropertyEditor.h"
+#include "cru/platform/graphics/Factory.h"
+#include "cru/ui/Base.h"
+#include "cru/ui/ThemeManager.h"
+
+namespace cru::theme_builder::components::properties {
+ColorPropertyEditor::ColorPropertyEditor() {
+ container_.AddChild(&label_);
+ container_.AddChild(&color_cube_);
+ container_.AddChild(&color_text_);
+
+ color_cube_.SetBorderEnabled(true);
+ color_cube_.GetStyleRuleSet()->SetParent(
+ ui::ThemeManager::GetInstance()->GetResourceStyleRuleSet(
+ u"cru.theme_builder.color_cube.style"));
+
+ color_cube_brush_ = platform::gui::IUiApplication::GetInstance()
+ ->GetGraphicsFactory()
+ ->CreateSolidColorBrush(color_);
+
+ color_cube_.SetForegroundBrush(color_cube_brush_);
+
+ color_text_.SetText(color_.ToString());
+ color_text_.SetMargin(ui::Thickness(10, 0, 0, 0));
+
+ color_text_.TextChangeEvent()->AddHandler([this](std::nullptr_t) {
+ auto text = color_text_.GetTextView();
+ auto color = ui::Color::Parse(text);
+ if (color) {
+ color_ = *color;
+ color_cube_brush_->SetColor(*color);
+ is_color_text_valid_ = true;
+ RaiseChangeEvent();
+ } else {
+ is_color_text_valid_ = false;
+ // TODO: Show error!
+ }
+ });
+}
+
+ColorPropertyEditor::~ColorPropertyEditor() {}
+
+void ColorPropertyEditor::SetValue(const ui::Color &color,
+ bool trigger_change) {
+ if (!trigger_change) SuppressNextChangeEvent();
+ color_text_.SetText(color.ToString());
+}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/ColorPropertyEditor.h b/src/ThemeBuilder/components/properties/ColorPropertyEditor.h
new file mode 100644
index 00000000..aa6cfcfa
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/ColorPropertyEditor.h
@@ -0,0 +1,36 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/platform/graphics/Base.h"
+#include "cru/ui/controls/Container.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+#include "cru/ui/controls/TextBox.h"
+
+namespace cru::theme_builder::components::properties {
+class ColorPropertyEditor : public Editor {
+ public:
+ using PropertyType = ui::Color;
+
+ ColorPropertyEditor();
+ ~ColorPropertyEditor() override;
+
+ public:
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ ui::Color GetValue() const { return color_; }
+ void SetValue(const ui::Color& color, bool trigger_change = true);
+
+ private:
+ ui::Color color_ = ui::colors::transparent;
+
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::controls::Container color_cube_;
+ std::shared_ptr<platform::graphics::ISolidColorBrush> color_cube_brush_;
+ ui::controls::TextBox color_text_;
+ bool is_color_text_valid_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.cpp b/src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.cpp
new file mode 100644
index 00000000..fc86b0ed
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.cpp
@@ -0,0 +1,42 @@
+#include "CornerRadiusPropertyEditor.h"
+#include "cru/ui/Base.h"
+#include "cru/ui/controls/FlexLayout.h"
+
+namespace cru::theme_builder::components::properties {
+CornerRadiusPropertyEditor::CornerRadiusPropertyEditor() {
+ container_.SetItemCrossAlign(ui::controls::FlexCrossAlignment::Start);
+
+ left_top_editor_.SetLabel(u"⌜");
+ right_top_editor_.SetLabel(u"⌝");
+ left_bottom_editor_.SetLabel(u"⌞");
+ right_bottom_editor_.SetLabel(u"⌟");
+
+ container_.SetFlexDirection(ui::controls::FlexDirection::Vertical);
+ container_.AddChild(left_top_editor_.GetRootControl());
+ container_.AddChild(right_top_editor_.GetRootControl());
+ container_.AddChild(left_bottom_editor_.GetRootControl());
+ container_.AddChild(right_bottom_editor_.GetRootControl());
+
+ ConnectChangeEvent(left_top_editor_);
+ ConnectChangeEvent(right_top_editor_);
+ ConnectChangeEvent(left_bottom_editor_);
+ ConnectChangeEvent(right_bottom_editor_);
+}
+
+CornerRadiusPropertyEditor::~CornerRadiusPropertyEditor() {}
+
+ui::CornerRadius CornerRadiusPropertyEditor::GetValue() const {
+ return ui::CornerRadius(
+ left_top_editor_.GetValue(), right_top_editor_.GetValue(),
+ left_bottom_editor_.GetValue(), right_bottom_editor_.GetValue());
+}
+
+void CornerRadiusPropertyEditor::SetValue(const ui::CornerRadius& corner_radius,
+ bool trigger_change) {
+ left_top_editor_.SetValue(corner_radius.left_top, false);
+ right_top_editor_.SetValue(corner_radius.right_top, false);
+ left_bottom_editor_.SetValue(corner_radius.left_bottom, false);
+ right_bottom_editor_.SetValue(corner_radius.right_bottom, false);
+ if (trigger_change) RaiseChangeEvent();
+}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.h b/src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.h
new file mode 100644
index 00000000..6b6833d1
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/CornerRadiusPropertyEditor.h
@@ -0,0 +1,28 @@
+#pragma once
+#include "../Editor.h"
+#include "PointPropertyEditor.h"
+#include "cru/ui/Base.h"
+#include "cru/ui/controls/FlexLayout.h"
+
+namespace cru::theme_builder::components::properties {
+class CornerRadiusPropertyEditor : public Editor {
+ public:
+ using PropertyType = ui::CornerRadius;
+
+ CornerRadiusPropertyEditor();
+ ~CornerRadiusPropertyEditor() override;
+
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ ui::CornerRadius GetValue() const;
+ void SetValue(const ui::CornerRadius& corner_radius,
+ bool trigger_change = true);
+
+ private:
+ ui::controls::FlexLayout container_;
+ PointPropertyEditor left_top_editor_;
+ PointPropertyEditor right_top_editor_;
+ PointPropertyEditor left_bottom_editor_;
+ PointPropertyEditor right_bottom_editor_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/FontPropertyEditor.cpp b/src/ThemeBuilder/components/properties/FontPropertyEditor.cpp
new file mode 100644
index 00000000..927ada7d
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/FontPropertyEditor.cpp
@@ -0,0 +1,60 @@
+#include "FontPropertyEditor.h"
+#include "cru/platform/graphics/Factory.h"
+#include "cru/platform/graphics/Font.h"
+#include "cru/platform/gui/UiApplication.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/render/FlexLayoutRenderObject.h"
+
+namespace cru::theme_builder::components::properties {
+using namespace cru::ui::controls;
+
+FontPropertyEditor::FontPropertyEditor() {
+ main_container_.SetFlexDirection(FlexDirection::Horizontal);
+ main_container_.AddChild(&label_);
+ main_container_.AddChild(&right_container_);
+
+ right_container_.SetFlexDirection(FlexDirection::Vertical);
+ right_container_.AddChild(&font_family_container_);
+ right_container_.AddChild(&font_size_container_);
+
+ font_family_container_.SetFlexDirection(FlexDirection::Horizontal);
+ font_family_container_.AddChild(&font_family_label_);
+ font_family_container_.AddChild(&font_family_text_);
+ font_family_label_.SetText(u"Font Family");
+
+ font_size_container_.SetFlexDirection(FlexDirection::Horizontal);
+ font_size_container_.AddChild(&font_size_label_);
+ font_size_container_.AddChild(font_size_input_.GetRootControl());
+ font_size_label_.SetText(u"Font Size");
+ font_size_input_.SetMin(0.0f);
+
+ font_family_text_.TextChangeEvent()->AddSpyOnlyHandler(
+ [this] { RaiseChangeEvent(); });
+
+ font_size_input_.ChangeEvent()->AddSpyOnlyHandler(
+ [this] { RaiseChangeEvent(); });
+}
+
+FontPropertyEditor::~FontPropertyEditor() {}
+
+Control* FontPropertyEditor::GetRootControl() { return &main_container_; }
+
+std::shared_ptr<platform::graphics::IFont> FontPropertyEditor::GetValue()
+ const {
+ return platform::gui::IUiApplication::GetInstance()
+ ->GetGraphicsFactory()
+ ->CreateFont(font_family_text_.GetText(), font_size_input_.GetValue());
+}
+
+void FontPropertyEditor::SetValue(
+ std::shared_ptr<platform::graphics::IFont> value, bool trigger_change) {
+ SuppressNextChangeEvent();
+ font_family_text_.SetText(value->GetFontName());
+ SuppressNextChangeEvent();
+ font_size_input_.SetValue(value->GetFontSize());
+
+ if (trigger_change) {
+ RaiseChangeEvent();
+ }
+}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/FontPropertyEditor.h b/src/ThemeBuilder/components/properties/FontPropertyEditor.h
new file mode 100644
index 00000000..d349f1f2
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/FontPropertyEditor.h
@@ -0,0 +1,38 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/platform/graphics/Font.h"
+#include "cru/ui/components/Input.h"
+#include "cru/ui/controls/Control.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+#include "cru/ui/controls/TextBox.h"
+
+namespace cru::theme_builder::components::properties {
+class FontPropertyEditor : public Editor {
+ public:
+ using PropertyType = std::shared_ptr<platform::graphics::IFont>;
+
+ FontPropertyEditor();
+ ~FontPropertyEditor() override;
+
+ ui::controls::Control* GetRootControl() override;
+
+ String GetLabelText() const { return label_.GetText(); }
+ void SetLabelText(String label) { label_.SetText(std::move(label)); }
+
+ std::shared_ptr<platform::graphics::IFont> GetValue() const;
+ void SetValue(std::shared_ptr<platform::graphics::IFont> value,
+ bool trigger_change = true);
+
+ private:
+ ui::controls::FlexLayout main_container_;
+ ui::controls::TextBlock label_;
+ ui::controls::FlexLayout right_container_;
+ ui::controls::FlexLayout font_family_container_;
+ ui::controls::TextBlock font_family_label_;
+ ui::controls::TextBox font_family_text_;
+ ui::controls::FlexLayout font_size_container_;
+ ui::controls::TextBlock font_size_label_;
+ ui::components::FloatInput font_size_input_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.cpp b/src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.cpp
new file mode 100644
index 00000000..d1f4afce
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.cpp
@@ -0,0 +1,37 @@
+#include "MeasureLengthPropertyEditor.h"
+#include "cru/common/Format.h"
+#include "cru/ui/mapper/MapperRegistry.h"
+#include "cru/ui/render/MeasureRequirement.h"
+
+namespace cru::theme_builder::components::properties {
+MeasureLengthPropertyEditor::MeasureLengthPropertyEditor() {
+ container_.AddChild(&label_);
+ container_.AddChild(&text_);
+
+ text_.TextChangeEvent()->AddHandler([this](std::nullptr_t) {
+ auto text = text_.GetTextView();
+ auto measure_length_mapper = ui::mapper::MapperRegistry::GetInstance()
+ ->GetMapper<ui::render::MeasureLength>();
+ try {
+ auto measure_length =
+ measure_length_mapper->MapFromString(text.ToString());
+ measure_length_ = measure_length;
+ is_text_valid_ = true;
+ RaiseChangeEvent();
+ } catch (const Exception&) {
+ is_text_valid_ = false;
+ // TODO: Show error!
+ }
+ });
+}
+
+MeasureLengthPropertyEditor::~MeasureLengthPropertyEditor() {}
+
+void MeasureLengthPropertyEditor::SetValue(
+ const ui::render::MeasureLength& value, bool trigger_change) {
+ if (!trigger_change) SuppressNextChangeEvent();
+ text_.SetText(measure_length_.IsNotSpecified()
+ ? u"unspecified"
+ : ToString(measure_length_.GetLengthOrUndefined()));
+}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.h b/src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.h
new file mode 100644
index 00000000..43e783c5
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/MeasureLengthPropertyEditor.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/platform/graphics/Base.h"
+#include "cru/ui/controls/Container.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+#include "cru/ui/controls/TextBox.h"
+#include "cru/ui/render/MeasureRequirement.h"
+
+namespace cru::theme_builder::components::properties {
+class MeasureLengthPropertyEditor : public Editor {
+ public:
+ using PropertyType = ui::render::MeasureLength;
+
+ MeasureLengthPropertyEditor();
+ ~MeasureLengthPropertyEditor() override;
+
+ public:
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ PropertyType GetValue() const { return measure_length_; }
+ void SetValue(const PropertyType& value, bool trigger_change = true);
+
+ private:
+ PropertyType measure_length_;
+
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::controls::TextBox text_;
+ bool is_text_valid_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/OptionalPropertyEditor.h b/src/ThemeBuilder/components/properties/OptionalPropertyEditor.h
new file mode 100644
index 00000000..0f22616a
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/OptionalPropertyEditor.h
@@ -0,0 +1,65 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/ui/controls/CheckBox.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+
+#include <optional>
+
+namespace cru::theme_builder::components::properties {
+template <typename TEditor>
+class OptionalPropertyEditor : public Editor {
+ public:
+ using PropertyType = typename TEditor::PropertyType;
+
+ OptionalPropertyEditor() {
+ container_.AddChild(&label_);
+ container_.AddChild(&check_box_);
+ check_box_.SetMargin({0, 0, 10, 0});
+ container_.AddChild(editor_.GetRootControl());
+
+ editor_.ChangeEvent()->AddHandler([this](std::nullptr_t) {
+ if (IsEnabled()) {
+ RaiseChangeEvent();
+ }
+ });
+ }
+ ~OptionalPropertyEditor() override {}
+
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ bool IsEnabled() const { return check_box_.IsChecked(); }
+ void SetEnabled(bool enabled, bool trigger_change = true) {
+ check_box_.SetChecked(enabled);
+ if (trigger_change) {
+ RaiseChangeEvent();
+ }
+ }
+
+ std::optional<PropertyType> GetValue() const {
+ return IsEnabled() ? std::optional<PropertyType>(editor_.GetValue())
+ : std::nullopt;
+ }
+
+ void SetValue(std::optional<PropertyType> value, bool trigger_change = true) {
+ if (value) {
+ SetEnabled(true, false);
+ editor_.SetValue(*value, false);
+ if (trigger_change) RaiseChangeEvent();
+ } else {
+ SetEnabled(false, trigger_change);
+ }
+ }
+
+ TEditor* GetEditor() { return &editor_; }
+
+ private:
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::controls::CheckBox check_box_;
+ TEditor editor_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/PointPropertyEditor.cpp b/src/ThemeBuilder/components/properties/PointPropertyEditor.cpp
new file mode 100644
index 00000000..6d4277aa
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/PointPropertyEditor.cpp
@@ -0,0 +1,38 @@
+#include "PointPropertyEditor.h"
+#include "cru/common/Format.h"
+#include "cru/ui/mapper/MapperRegistry.h"
+#include "cru/ui/mapper/PointMapper.h"
+
+namespace cru::theme_builder::components::properties {
+PointPropertyEditor::PointPropertyEditor() {
+ container_.AddChild(&label_);
+ container_.AddChild(&text_);
+
+ text_.TextChangeEvent()->AddHandler([this](std::nullptr_t) {
+ auto text = text_.GetTextView();
+ auto point_mapper =
+ ui::mapper::MapperRegistry::GetInstance()->GetMapper<ui::Point>();
+ try {
+ auto point = point_mapper->MapFromString(text.ToString());
+ point_ = point;
+ is_text_valid_ = true;
+ RaiseChangeEvent();
+ } catch (const Exception&) {
+ is_text_valid_ = false;
+ // TODO: Show error!
+ }
+ });
+}
+
+PointPropertyEditor::~PointPropertyEditor() {}
+
+void PointPropertyEditor::SetValue(const ui::Point& point,
+ bool trigger_change) {
+ if (!trigger_change) SuppressNextChangeEvent();
+ text_.SetText(ConvertPointToString(point));
+}
+
+String PointPropertyEditor::ConvertPointToString(const ui::Point& point) {
+ return Format(u"{} {}", point.x, point.y);
+}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/PointPropertyEditor.h b/src/ThemeBuilder/components/properties/PointPropertyEditor.h
new file mode 100644
index 00000000..bd852e3a
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/PointPropertyEditor.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+#include "cru/ui/controls/TextBox.h"
+
+namespace cru::theme_builder::components::properties {
+class PointPropertyEditor : public Editor {
+ public:
+ using PropertyType = ui::Point;
+
+ PointPropertyEditor();
+ ~PointPropertyEditor() override;
+
+ public:
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ ui::Point GetValue() const { return point_; }
+ void SetValue(const ui::Point& point, bool trigger_change = true);
+
+ private:
+ static String ConvertPointToString(const ui::Point& point);
+
+ private:
+ ui::Point point_;
+
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::controls::TextBox text_;
+ bool is_text_valid_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/SelectPropertyEditor.cpp b/src/ThemeBuilder/components/properties/SelectPropertyEditor.cpp
new file mode 100644
index 00000000..835b2d12
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/SelectPropertyEditor.cpp
@@ -0,0 +1,15 @@
+#include "SelectPropertyEditor.h"
+#include "cru/ui/controls/FlexLayout.h"
+
+namespace cru::theme_builder::components::properties {
+SelectPropertyEditor::SelectPropertyEditor() {
+ container_.SetFlexDirection(ui::controls::FlexDirection::Horizontal);
+ container_.AddChild(&label_);
+ container_.AddChild(select_.GetRootControl());
+
+ select_.ItemSelectedEvent()->AddHandler(
+ [this](Index index) { RaiseChangeEvent(); });
+}
+
+SelectPropertyEditor::~SelectPropertyEditor() {}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/SelectPropertyEditor.h b/src/ThemeBuilder/components/properties/SelectPropertyEditor.h
new file mode 100644
index 00000000..475d2d0a
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/SelectPropertyEditor.h
@@ -0,0 +1,42 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/ui/components/Select.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+
+namespace cru::theme_builder::components::properties {
+class SelectPropertyEditor : public Editor {
+ public:
+ using PropertyType = Index;
+
+ SelectPropertyEditor();
+ ~SelectPropertyEditor() override;
+
+ public:
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ Index GetSelectedIndex() const { return select_.GetSelectedIndex(); }
+ void SetSelectedIndex(Index index, bool trigger_change = true) {
+ if (trigger_change == false) SuppressNextChangeEvent();
+ select_.SetSelectedIndex(index);
+ }
+
+ std::vector<String> GetItems() const { return select_.GetItems(); }
+ void SetItems(std::vector<String> items) {
+ select_.SetItems(std::move(items));
+ }
+
+ Index GetValue() const { return GetSelectedIndex(); }
+ void SetValue(Index value, bool trigger_change = true) {
+ SetSelectedIndex(value, trigger_change);
+ }
+
+ private:
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::components::Select select_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/TextPropertyEditor.cpp b/src/ThemeBuilder/components/properties/TextPropertyEditor.cpp
new file mode 100644
index 00000000..9854019c
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/TextPropertyEditor.cpp
@@ -0,0 +1,22 @@
+#include "TextPropertyEditor.h"
+
+namespace cru::theme_builder::components::properties {
+TextPropertyEditor::TextPropertyEditor() {
+ editor_.TextChangeEvent()->AddHandler([this](std::nullptr_t) {
+ auto text_view = editor_.GetTextView();
+ String error_message;
+ auto validation_result = Validate(text_view, &error_message);
+ if (validation_result) {
+ OnTextChanged(text_view);
+ }
+ });
+}
+
+TextPropertyEditor::~TextPropertyEditor() {}
+
+bool TextPropertyEditor::Validate(StringView text, String* error_message) {
+ return true;
+}
+
+void TextPropertyEditor::OnTextChanged(StringView text) {}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/TextPropertyEditor.h b/src/ThemeBuilder/components/properties/TextPropertyEditor.h
new file mode 100644
index 00000000..c4944228
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/TextPropertyEditor.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "cru/ui/components/Component.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+#include "cru/ui/controls/TextBox.h"
+
+namespace cru::theme_builder::components::properties {
+class TextPropertyEditor : public ui::components::Component {
+ public:
+ TextPropertyEditor();
+ ~TextPropertyEditor() override;
+
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ String GetText() const { return editor_.GetText(); }
+ StringView GetTextView() const { return editor_.GetTextView(); }
+ void SetText(String text) { editor_.SetText(std::move(text)); }
+
+ protected:
+ virtual bool Validate(StringView text, String* error_message);
+ virtual void OnTextChanged(StringView text);
+
+ private:
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::controls::TextBox editor_;
+};
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/ThicknessPropertyEditor.cpp b/src/ThemeBuilder/components/properties/ThicknessPropertyEditor.cpp
new file mode 100644
index 00000000..3e022bb1
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/ThicknessPropertyEditor.cpp
@@ -0,0 +1,34 @@
+#include "ThicknessPropertyEditor.h"
+#include "cru/ui/mapper/MapperRegistry.h"
+#include "cru/ui/mapper/ThicknessMapper.h"
+
+namespace cru::theme_builder::components::properties {
+ThicknessPropertyEditor::ThicknessPropertyEditor() {
+ container_.AddChild(&label_);
+ container_.AddChild(&text_);
+
+ text_.TextChangeEvent()->AddHandler([this](std::nullptr_t) {
+ auto text = text_.GetText();
+ auto thickness_mapper =
+ ui::mapper::MapperRegistry::GetInstance()->GetMapper<ui::Thickness>();
+ try {
+ auto thickness = thickness_mapper->MapFromString(text);
+ thickness_ = thickness;
+ is_text_valid_ = true;
+ RaiseChangeEvent();
+ } catch (const Exception &) {
+ is_text_valid_ = false;
+ // TODO: Show error!
+ }
+ });
+}
+
+ThicknessPropertyEditor::~ThicknessPropertyEditor() {}
+
+void ThicknessPropertyEditor::SetValue(const ui::Thickness &thickness,
+ bool trigger_change) {
+ if (!trigger_change) SuppressNextChangeEvent();
+ text_.SetText(Format(u"{} {} {} {}", thickness.left, thickness.top,
+ thickness.right, thickness.bottom));
+}
+} // namespace cru::theme_builder::components::properties
diff --git a/src/ThemeBuilder/components/properties/ThicknessPropertyEditor.h b/src/ThemeBuilder/components/properties/ThicknessPropertyEditor.h
new file mode 100644
index 00000000..cea9ae9d
--- /dev/null
+++ b/src/ThemeBuilder/components/properties/ThicknessPropertyEditor.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "../Editor.h"
+#include "cru/ui/controls/FlexLayout.h"
+#include "cru/ui/controls/TextBlock.h"
+#include "cru/ui/controls/TextBox.h"
+
+namespace cru::theme_builder::components::properties {
+class ThicknessPropertyEditor : public Editor {
+ public:
+ using PropertyType = ui::Thickness;
+
+ ThicknessPropertyEditor();
+ ~ThicknessPropertyEditor() override;
+
+ ui::controls::Control* GetRootControl() override { return &container_; }
+
+ String GetLabel() const { return label_.GetText(); }
+ void SetLabel(String label) { label_.SetText(std::move(label)); }
+
+ ui::Thickness GetValue() const { return thickness_; }
+ void SetValue(const ui::Thickness& thickness, bool trigger_change = true);
+
+ private:
+ ui::Thickness thickness_;
+
+ ui::controls::FlexLayout container_;
+ ui::controls::TextBlock label_;
+ ui::controls::TextBox text_;
+ bool is_text_valid_;
+};
+} // namespace cru::theme_builder::components::properties