diff options
Diffstat (limited to 'src/ThemeBuilder')
63 files changed, 2426 insertions, 0 deletions
diff --git a/src/ThemeBuilder/CMakeLists.txt b/src/ThemeBuilder/CMakeLists.txt new file mode 100644 index 00000000..93b24e85 --- /dev/null +++ b/src/ThemeBuilder/CMakeLists.txt @@ -0,0 +1,45 @@ +add_executable(CruThemeBuilder + main.cpp + components/Common.cpp + components/Editor.cpp + components/HeadBodyEditor.cpp + components/MainWindow.cpp + components/StyleRuleEditor.cpp + components/StyleRuleSetEditor.cpp + components/conditions/CheckedConditionEditor.cpp + components/conditions/ClickStateConditionEditor.cpp + components/conditions/CompoundConditionEditor.cpp + components/conditions/ConditionEditor.cpp + components/conditions/FocusConditionEditor.cpp + components/conditions/NoConditionEditor.cpp + components/properties/CheckBoxPropertyEditor.cpp + components/properties/ColorPropertyEditor.cpp + components/properties/CornerRadiusPropertyEditor.cpp + components/properties/FontPropertyEditor.cpp + components/properties/MeasureLengthPropertyEditor.cpp + components/properties/PointPropertyEditor.cpp + components/properties/SelectPropertyEditor.cpp + components/properties/TextPropertyEditor.cpp + components/properties/ThicknessPropertyEditor.cpp + components/stylers/BorderStylerEditor.cpp + components/stylers/CompoundStylerEditor.cpp + components/stylers/ContentBrushStylerEditor.cpp + components/stylers/CursorStylerEditor.cpp + components/stylers/FontStylerEditor.cpp + components/stylers/MarginStylerEditor.cpp + components/stylers/PaddingStylerEditor.cpp + components/stylers/PreferredSizeStylerEditor.cpp + components/stylers/StylerEditor.cpp +) + +if(APPLE) + set_target_properties(CruThemeBuilder PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_BUNDLE_NAME cru-theme-builder + MACOSX_BUNDLE_GUI_IDENTIFIER life.crupest.cru.theme-builder + ) +endif() + +target_add_resources(CruThemeBuilder cru/ui) +target_add_resources(CruThemeBuilder cru/theme_builder) +target_link_libraries(CruThemeBuilder PRIVATE CruPlatformBootstrap CruUi) diff --git a/src/ThemeBuilder/components/Common.cpp b/src/ThemeBuilder/components/Common.cpp new file mode 100644 index 00000000..75d5deb0 --- /dev/null +++ b/src/ThemeBuilder/components/Common.cpp @@ -0,0 +1,22 @@ +#include "Common.h" +#include "cru/platform/Color.h" +#include "cru/platform/graphics/Factory.h" +#include "cru/platform/gui/UiApplication.h" + +#include <random> + +namespace cru::theme_builder::components { +std::unique_ptr<platform::graphics::ISolidColorBrush> +CreateRandomEditorBackgroundBrush() { + static float current_hue = 0.0f; + current_hue += 23.f; + if (current_hue > 360.f) { + current_hue -= 360.f; + } + + return platform::gui::IUiApplication::GetInstance() + ->GetGraphicsFactory() + ->CreateSolidColorBrush(platform::HslColor(current_hue, 0.5f, 0.8f)); +} + +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/Common.h b/src/ThemeBuilder/components/Common.h new file mode 100644 index 00000000..b91fcf88 --- /dev/null +++ b/src/ThemeBuilder/components/Common.h @@ -0,0 +1,7 @@ +#pragma once +#include "cru/platform/graphics/Brush.h" + +namespace cru::theme_builder::components { +std::unique_ptr<platform::graphics::ISolidColorBrush> +CreateRandomEditorBackgroundBrush(); +} diff --git a/src/ThemeBuilder/components/Editor.cpp b/src/ThemeBuilder/components/Editor.cpp new file mode 100644 index 00000000..89e27e83 --- /dev/null +++ b/src/ThemeBuilder/components/Editor.cpp @@ -0,0 +1,23 @@ +#include "Editor.h" + +namespace cru::theme_builder::components { + +void Editor::RaiseChangeEvent() { + if (suppress_next_change_event_) { + suppress_next_change_event_ = false; + } else { + change_event_.Raise(nullptr); + } +} + +void Editor::SuppressNextChangeEvent() { suppress_next_change_event_ = true; } + +void Editor::ConnectChangeEvent(IEvent<std::nullptr_t>* event) { + event->AddHandler([this](std::nullptr_t) { RaiseChangeEvent(); }); +} + +void Editor::ConnectChangeEvent(Editor* editor) { + ConnectChangeEvent(editor->ChangeEvent()); +} + +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/Editor.h b/src/ThemeBuilder/components/Editor.h new file mode 100644 index 00000000..29809c82 --- /dev/null +++ b/src/ThemeBuilder/components/Editor.h @@ -0,0 +1,25 @@ +#pragma once +#include "cru/common/Event.h" +#include "cru/ui/components/Component.h" + +namespace cru::theme_builder::components { +class Editor : public ui::components::Component { + public: + Editor() = default; + ~Editor() override = default; + + public: + IEvent<std::nullptr_t>* ChangeEvent() { return &change_event_; } + + protected: + void RaiseChangeEvent(); + void SuppressNextChangeEvent(); + void ConnectChangeEvent(IEvent<std::nullptr_t>* event); + void ConnectChangeEvent(Editor* editor); + void ConnectChangeEvent(Editor& editor) { ConnectChangeEvent(&editor); } + + private: + bool suppress_next_change_event_ = false; + Event<std::nullptr_t> change_event_; +}; +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/HeadBodyEditor.cpp b/src/ThemeBuilder/components/HeadBodyEditor.cpp new file mode 100644 index 00000000..6d7ace66 --- /dev/null +++ b/src/ThemeBuilder/components/HeadBodyEditor.cpp @@ -0,0 +1,30 @@ +#include "HeadBodyEditor.h" +#include "Common.h" +#include "cru/ui/ThemeManager.h" +#include "cru/ui/controls/FlexLayout.h" + +namespace cru::theme_builder::components { +HeadBodyEditor::HeadBodyEditor() { + border_.SetChild(&container_); + border_.SetBackgroundBrush(CreateRandomEditorBackgroundBrush()); + + container_.SetFlexDirection(ui::controls::FlexDirection::Vertical); + container_.AddChild(&head_container_); + container_.SetItemCrossAlign(ui::controls::FlexCrossAlignment::Stretch); + head_container_.SetFlexDirection(ui::controls::FlexDirection::Horizontal); + head_container_.AddChild(&label_); + + remove_button_.GetStyleRuleSet()->SetParent( + ui::ThemeManager::GetInstance()->GetResourceStyleRuleSet( + u"cru.theme_builder.icon-button.style")); + remove_button_.SetIconWithSvgPathDataStringResourceKey(u"icon.close", + {0, 0, 16, 16}); + remove_button_.SetIconFillColor(ui::colors::red); + head_container_.AddChild(&remove_button_); + + remove_button_.ClickEvent()->AddSpyOnlyHandler( + [this] { remove_event_.Raise(nullptr); }); +} + +HeadBodyEditor::~HeadBodyEditor() {} +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/HeadBodyEditor.h b/src/ThemeBuilder/components/HeadBodyEditor.h new file mode 100644 index 00000000..8119724f --- /dev/null +++ b/src/ThemeBuilder/components/HeadBodyEditor.h @@ -0,0 +1,36 @@ +#pragma once +#include "Editor.h" +#include "cru/common/Event.h" +#include "cru/ui/controls/Container.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/controls/IconButton.h" +#include "cru/ui/controls/TextBlock.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components { +class HeadBodyEditor : public Editor { + public: + HeadBodyEditor(); + ~HeadBodyEditor() override; + + public: + ui::controls::Control* GetRootControl() override { return &border_; } + + ui::controls::FlexLayout* GetContainer() { return &container_; } + ui::controls::FlexLayout* GetHeadContainer() { return &head_container_; } + + String GetLabel() const { return label_.GetText(); } + void SetLabel(String label) { label_.SetText(std::move(label)); } + + IEvent<std::nullptr_t>* RemoveEvent() { return &remove_event_; } + + private: + ui::controls::Container border_; + ui::controls::FlexLayout container_; + ui::controls::FlexLayout head_container_; + ui::controls::TextBlock label_; + ui::controls::IconButton remove_button_; + + Event<std::nullptr_t> remove_event_; +}; +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/MainWindow.cpp b/src/ThemeBuilder/components/MainWindow.cpp new file mode 100644 index 00000000..0c78ef25 --- /dev/null +++ b/src/ThemeBuilder/components/MainWindow.cpp @@ -0,0 +1,37 @@ +#include "MainWindow.h" +#include "cru/ui/Base.h" +#include "cru/ui/controls/StackLayout.h" +#include "cru/ui/controls/TextBlock.h" + +namespace cru::theme_builder::components { +using namespace cru::ui; +using namespace cru::ui::controls; +using namespace cru::platform::gui; + +MainWindow::MainWindow() { + window_.GetNativeWindow()->SetTitle(u"CruUI Theme Builder"); + main_layout_.SetFlexDirection(FlexDirection::Horizontal); + window_.AddChild(&main_layout_); + main_layout_.AddChild(&preview_layout_); + + preview_button_text_.SetText(u"Preview"); + preview_button_.SetChild(&preview_button_text_); + preview_layout_.AddChild(&preview_button_); + preview_layout_.SetChildLayoutData( + 0, StackChildLayoutData{Alignment::Center, Alignment::Center}); + + style_rule_set_editor_.BindStyleRuleSet( + preview_button_.GetStyleRuleSet()->GetParent()); + main_layout_.AddChild(style_rule_set_editor_.GetRootControl()); + + main_layout_.SetChildLayoutData(0, {1, 0}); + main_layout_.SetChildLayoutData(1, {0, 1}); +} + +MainWindow::~MainWindow() {} + +void MainWindow::Show() { + window_.GetNativeWindow()->SetVisibility(WindowVisibilityType::Show); + window_.GetNativeWindow()->SetToForeground(); +} +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/MainWindow.h b/src/ThemeBuilder/components/MainWindow.h new file mode 100644 index 00000000..ede1c38f --- /dev/null +++ b/src/ThemeBuilder/components/MainWindow.h @@ -0,0 +1,32 @@ +#pragma once +#include "StyleRuleSetEditor.h" +#include "cru/ui/components/Component.h" +#include "cru/ui/controls/Button.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/controls/StackLayout.h" +#include "cru/ui/controls/TextBlock.h" +#include "cru/ui/controls/Window.h" + +namespace cru::theme_builder::components { +class MainWindow : public ui::components::Component { + public: + MainWindow(); + + CRU_DELETE_COPY(MainWindow) + CRU_DELETE_MOVE(MainWindow) + + ~MainWindow() override; + + ui::controls::Control* GetRootControl() override { return &window_; } + + void Show(); + + private: + ui::controls::Window window_; + ui::controls::FlexLayout main_layout_; + ui::controls::StackLayout preview_layout_; + ui::controls::Button preview_button_; + ui::controls::TextBlock preview_button_text_; + StyleRuleSetEditor style_rule_set_editor_; +}; +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/StyleRuleEditor.cpp b/src/ThemeBuilder/components/StyleRuleEditor.cpp new file mode 100644 index 00000000..dcb33184 --- /dev/null +++ b/src/ThemeBuilder/components/StyleRuleEditor.cpp @@ -0,0 +1,62 @@ +#include "StyleRuleEditor.h" +#include "Common.h" +#include "conditions/ConditionEditor.h" +#include "cru/ui/ThemeManager.h" +#include "cru/ui/style/StyleRule.h" + +namespace cru::theme_builder::components { +StyleRuleEditor::StyleRuleEditor() { + container_.SetChild(&main_layout_); + container_.SetBackgroundBrush(CreateRandomEditorBackgroundBrush()); + + main_layout_.SetFlexDirection(ui::controls::FlexDirection::Vertical); + main_layout_.SetItemCrossAlign(ui::controls::FlexCrossAlignment::Start); + + main_layout_.AddChild(&head_layout_); + + label_.SetText(u"Style Rule"); + head_layout_.AddChild(&label_); + head_layout_.AddChild(&remove_button_); + + remove_button_.GetStyleRuleSet()->SetParent( + ui::ThemeManager::GetInstance()->GetResourceStyleRuleSet( + u"cru.theme_builder.icon-button.style")); + remove_button_.SetIconWithSvgPathDataStringResourceKey(u"icon.close", + {0, 0, 16, 16}); + remove_button_.SetIconFillColor(ui::colors::red); + + main_layout_.AddChild(&body_layout_); + body_layout_.SetFlexDirection(ui::controls::FlexDirection::Vertical); + body_layout_.SetItemCrossAlign(ui::controls::FlexCrossAlignment::Start); + body_layout_.SetMargin({10, 0, 0, 0}); + + remove_button_.ClickEvent()->AddSpyOnlyHandler( + [this] { remove_event_.Raise(nullptr); }); +} + +StyleRuleEditor::~StyleRuleEditor() {} + +ui::style::StyleRule StyleRuleEditor::GetValue() const { + return ui::style::StyleRule(condition_editor_->GetCondition(), + styler_editor_->GetStyler()); +} + +void StyleRuleEditor::SetValue(const ui::style::StyleRule& style_rule, + bool trigger_change) { + body_layout_.ClearChildren(); + condition_editor_ = + components::conditions::CreateConditionEditor(style_rule.GetCondition()); + styler_editor_ = + components::stylers::CreateStylerEditor(style_rule.GetStyler()); + body_layout_.AddChild(condition_editor_->GetRootControl()); + body_layout_.AddChild(styler_editor_->GetRootControl()); + condition_editor_->ChangeEvent()->AddSpyOnlyHandler( + [this] { change_event_.Raise(nullptr); }); + styler_editor_->ChangeEvent()->AddSpyOnlyHandler( + [this] { change_event_.Raise(nullptr); }); + + if (trigger_change) { + change_event_.Raise(nullptr); + } +} +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/StyleRuleEditor.h b/src/ThemeBuilder/components/StyleRuleEditor.h new file mode 100644 index 00000000..8e3db3de --- /dev/null +++ b/src/ThemeBuilder/components/StyleRuleEditor.h @@ -0,0 +1,45 @@ +#pragma once +#include "conditions/ConditionEditor.h" +#include "cru/ui/components/Component.h" +#include "cru/ui/controls/Button.h" +#include "cru/ui/controls/Control.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/controls/IconButton.h" +#include "cru/ui/controls/TextBlock.h" +#include "cru/ui/style/StyleRule.h" +#include "stylers/StylerEditor.h" + +namespace cru::theme_builder::components { +class StyleRuleEditor : public ui::components::Component { + public: + StyleRuleEditor(); + + CRU_DELETE_COPY(StyleRuleEditor) + CRU_DELETE_MOVE(StyleRuleEditor) + + ~StyleRuleEditor() override; + + public: + ui::controls::Control* GetRootControl() override { return &container_; } + + ui::style::StyleRule GetValue() const; + void SetValue(const ui::style::StyleRule& style_rule, + bool trigger_change = true); + + IEvent<std::nullptr_t>* ChangeEvent() { return &change_event_; } + IEvent<std::nullptr_t>* RemoveEvent() { return &remove_event_; } + + private: + ui::controls::Container container_; + ui::controls::FlexLayout main_layout_; + ui::controls::TextBlock label_; + ui::controls::FlexLayout head_layout_; + ui::controls::IconButton remove_button_; + ui::controls::FlexLayout body_layout_; + std::unique_ptr<components::conditions::ConditionEditor> condition_editor_; + std::unique_ptr<components::stylers::StylerEditor> styler_editor_; + + Event<std::nullptr_t> change_event_; + Event<std::nullptr_t> remove_event_; +}; +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/StyleRuleSetEditor.cpp b/src/ThemeBuilder/components/StyleRuleSetEditor.cpp new file mode 100644 index 00000000..8cf5af6d --- /dev/null +++ b/src/ThemeBuilder/components/StyleRuleSetEditor.cpp @@ -0,0 +1,123 @@ +#include "StyleRuleSetEditor.h" +#include "cru/common/Exception.h" +#include "cru/common/String.h" +#include "cru/ui/DeleteLater.h" +#include "cru/ui/ThemeManager.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/model/IListChangeNotify.h" +#include "cru/ui/render/FlexLayoutRenderObject.h" +#include "cru/ui/style/Condition.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components { +using namespace cru::ui::controls; +StyleRuleSetEditor::StyleRuleSetEditor() { + scroll_view_.SetChild(&container_); + + container_.SetFlexDirection(ui::render::FlexDirection::Vertical); + container_.AddChild(&rules_layout_); + container_.AddChild(&add_button_); + + rules_layout_.SetFlexDirection(ui::controls::FlexDirection::Vertical); + rules_layout_.SetItemCrossAlign(ui::controls::FlexCrossAlignment::Stretch); + + add_button_.GetStyleRuleSet()->SetParent( + ui::ThemeManager::GetInstance()->GetResourceStyleRuleSet( + u"cru.theme_builder.icon-button.style")); + add_button_.SetIconWithSvgPathDataStringResourceKey(u"icon.plus", + {0, 0, 16, 16}); + add_button_.SetPreferredSize({24, 24}); + add_button_.SetPadding(ui::Thickness(2)); + add_button_.SetIconFillColor(ui::colors::green); + + add_button_.ClickEvent()->AddSpyOnlyHandler([this] { + auto rule_set = ui::style::StyleRule(ui::style::NoCondition::Create(), + ui::style::CompoundStyler::Create({})); + style_rule_set_->AddStyleRule(rule_set); + }); +} + +StyleRuleSetEditor::~StyleRuleSetEditor() {} + +void StyleRuleSetEditor::BindStyleRuleSet( + std::shared_ptr<ui::style::StyleRuleSet> rule_set) { + Expects(style_rule_set_ == nullptr && rule_set); + style_rule_set_ = std::move(rule_set); + UpdateView(style_rule_set_.get()); + style_rule_set_->ListChangeEvent()->AddHandler( + [this](const ui::model::ListChange& change) { + UpdateView(style_rule_set_.get(), change); + }); +} + +Index StyleRuleSetEditor::IndexOfRuleEditor(StyleRuleEditor* editor) { + auto iter = + std::find_if(style_rule_editors_.cbegin(), style_rule_editors_.cend(), + [editor](const ui::DeleteLaterPtr<StyleRuleEditor>& p) { + return p.get() == editor; + }); + return iter - style_rule_editors_.cbegin(); +} + +void StyleRuleSetEditor::UpdateView( + ui::style::StyleRuleSet* style_rule_set, + std::optional<ui::model::ListChange> change) { + if (change) { + switch (change->type) { + case ui::model::ListChangeType::kItemAdd: { + for (auto i = change->position; i < change->position + change->count; + ++i) { + const auto& rule = style_rule_set->GetStyleRule(i); + auto style_rule_editor = ui::MakeDeleteLaterPtr<StyleRuleEditor>(); + style_rule_editor->SetValue(rule, false); + style_rule_editor->RemoveEvent()->AddSpyOnlyHandler( + [this, editor = style_rule_editor.get()] { + style_rule_set_->RemoveStyleRule(IndexOfRuleEditor(editor)); + }); + style_rule_editor->ChangeEvent()->AddSpyOnlyHandler( + [this, editor = style_rule_editor.get()]() { + suppress_next_set_ = true; + style_rule_set_->SetStyleRule(IndexOfRuleEditor(editor), + editor->GetValue()); + }); + style_rule_editors_.insert(style_rule_editors_.cbegin() + i, + std::move(style_rule_editor)); + rules_layout_.AddChildAt(style_rule_editors_.back()->GetRootControl(), + i); + } + break; + } + case ui::model::ListChangeType::kItemRemove: { + for (auto i = change->position; i < change->position + change->count; + ++i) { + style_rule_editors_.erase(style_rule_editors_.begin() + i); + } + break; + } + case ui::model::ListChangeType::kItemSet: { + if (suppress_next_set_) { + suppress_next_set_ = false; + break; + } + for (auto i = change->position; i < change->position + change->count; + ++i) { + const auto& rule = style_rule_set->GetStyleRule(i); + style_rule_editors_[i]->SetValue(rule, false); + } + break; + } + case ui::model::ListChangeType::kItemMove: { + throw Exception(u"Not supported now!"); + break; + } + case ui::model::ListChangeType::kClear: { + style_rule_editors_.clear(); + } + } + } else { + UpdateView(style_rule_set, ui::model::ListChange::Clear()); + UpdateView(style_rule_set, + ui::model::ListChange::ItemAdd(0, style_rule_set->GetSize())); + } +} +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/StyleRuleSetEditor.h b/src/ThemeBuilder/components/StyleRuleSetEditor.h new file mode 100644 index 00000000..03148889 --- /dev/null +++ b/src/ThemeBuilder/components/StyleRuleSetEditor.h @@ -0,0 +1,44 @@ +#pragma once +#include "StyleRuleEditor.h" +#include "cru/ui/DeleteLater.h" +#include "cru/ui/components/Component.h" +#include "cru/ui/controls/Button.h" +#include "cru/ui/controls/Control.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/controls/ScrollView.h" +#include "cru/ui/model/IListChangeNotify.h" +#include "cru/ui/style/StyleRuleSet.h" + +namespace cru::theme_builder::components { +class StyleRuleSetEditor : public ui::components::Component { + public: + StyleRuleSetEditor(); + + CRU_DELETE_COPY(StyleRuleSetEditor) + CRU_DELETE_MOVE(StyleRuleSetEditor) + + ~StyleRuleSetEditor() override; + + public: + ui::controls::Control* GetRootControl() override { return &scroll_view_; } + + void BindStyleRuleSet(std::shared_ptr<ui::style::StyleRuleSet> rule_set); + + private: + Index IndexOfRuleEditor(StyleRuleEditor* editor); + + void UpdateView(ui::style::StyleRuleSet* style_rule_set, + std::optional<ui::model::ListChange> change = std::nullopt); + + private: + std::shared_ptr<ui::style::StyleRuleSet> style_rule_set_; + + ui::controls::ScrollView scroll_view_; + ui::controls::FlexLayout container_; + ui::controls::FlexLayout rules_layout_; + std::vector<ui::DeleteLaterPtr<StyleRuleEditor>> style_rule_editors_; + ui::controls::IconButton add_button_; + + bool suppress_next_set_ = false; +}; +} // namespace cru::theme_builder::components diff --git a/src/ThemeBuilder/components/conditions/CheckedConditionEditor.cpp b/src/ThemeBuilder/components/conditions/CheckedConditionEditor.cpp new file mode 100644 index 00000000..64370981 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/CheckedConditionEditor.cpp @@ -0,0 +1,26 @@ +#include "CheckedConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +CheckedConditionEditor::CheckedConditionEditor() { + SetLabel(u"Checked Condition"); + + checked_check_box_.SetLabel(u"Checked"); + GetContainer()->AddChild(checked_check_box_.GetRootControl()); + + ConnectChangeEvent(checked_check_box_); +} + +CheckedConditionEditor::~CheckedConditionEditor() {} + +ClonablePtr<ui::style::CheckedCondition> CheckedConditionEditor::GetValue() + const { + return ui::style::CheckedCondition::Create(checked_check_box_.GetValue()); +} + +void CheckedConditionEditor::SetValue(ui::style::CheckedCondition* value, + bool trigger_change) { + checked_check_box_.SetValue(value->IsChecked(), trigger_change); +} +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/CheckedConditionEditor.h b/src/ThemeBuilder/components/conditions/CheckedConditionEditor.h new file mode 100644 index 00000000..7cf14912 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/CheckedConditionEditor.h @@ -0,0 +1,28 @@ +#pragma once +#include "../properties/CheckBoxPropertyEditor.h" +#include "ConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +class CheckedConditionEditor : public ConditionEditor { + public: + CheckedConditionEditor(); + ~CheckedConditionEditor() override; + + public: + ClonablePtr<ui::style::CheckedCondition> GetValue() const; + void SetValue(ui::style::CheckedCondition* value, bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::CheckedCondition>& value, + bool trigger_change = true) { + SetValue(value.get(), trigger_change); + } + + ClonablePtr<ui::style::Condition> GetCondition() override { + return GetValue(); + } + + private: + properties::CheckBoxPropertyEditor checked_check_box_; +}; +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/ClickStateConditionEditor.cpp b/src/ThemeBuilder/components/conditions/ClickStateConditionEditor.cpp new file mode 100644 index 00000000..a8d5cc87 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/ClickStateConditionEditor.cpp @@ -0,0 +1,69 @@ +#include "ClickStateConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/helper/ClickDetector.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +using ui::helper::ClickState; +namespace { +const std::vector<String> kClickStates{ + u"None", + u"Hover", + u"Press", + u"PressInactive", +}; + +Index ConvertClickStateToIndex(ClickState click_state) { + switch (click_state) { + case ClickState::None: + return 0; + case ClickState::Hover: + return 1; + case ClickState::Press: + return 2; + case ClickState::PressInactive: + return 3; + } + return -1; +} + +ClickState ConvertIndexToClickState(Index index) { + switch (index) { + case 0: + return ClickState::None; + case 1: + return ClickState::Hover; + case 2: + return ClickState::Press; + case 3: + return ClickState::PressInactive; + } + return ClickState::None; +} +} // namespace + +ClickStateConditionEditor::ClickStateConditionEditor() { + SetLabel(u"Click State Condition"); + GetContainer()->AddChild(click_state_select_.GetRootControl()); + + click_state_select_.SetLabel(u"Click State"); + click_state_select_.SetItems(kClickStates); + click_state_select_.SetSelectedIndex(0, false); + + ConnectChangeEvent(click_state_select_); +} + +ClickStateConditionEditor::~ClickStateConditionEditor() {} + +ClonablePtr<ui::style::ClickStateCondition> +ClickStateConditionEditor::GetValue() const { + return ui::style::ClickStateCondition::Create( + ConvertIndexToClickState(click_state_select_.GetSelectedIndex())); +} + +void ClickStateConditionEditor::SetValue(ui::style::ClickStateCondition* value, + bool trigger_change) { + click_state_select_.SetSelectedIndex( + ConvertClickStateToIndex(value->GetClickState()), trigger_change); +} +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/ClickStateConditionEditor.h b/src/ThemeBuilder/components/conditions/ClickStateConditionEditor.h new file mode 100644 index 00000000..454a1346 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/ClickStateConditionEditor.h @@ -0,0 +1,30 @@ +#pragma once +#include "../properties/SelectPropertyEditor.h" +#include "ConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/common/Event.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +class ClickStateConditionEditor : public ConditionEditor { + public: + ClickStateConditionEditor(); + ~ClickStateConditionEditor(); + + public: + ClonablePtr<ui::style::ClickStateCondition> GetValue() const; + void SetValue(ui::style::ClickStateCondition* value, + bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::ClickStateCondition>& value, + bool trigger_change = true) { + SetValue(value.get(), trigger_change); + } + + ClonablePtr<ui::style::Condition> GetCondition() override { + return GetValue(); + } + + private: + properties::SelectPropertyEditor click_state_select_; +}; +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/CompoundConditionEditor.cpp b/src/ThemeBuilder/components/conditions/CompoundConditionEditor.cpp new file mode 100644 index 00000000..69b8ed02 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/CompoundConditionEditor.cpp @@ -0,0 +1,109 @@ +#include "CompoundConditionEditor.h" +#include "CheckedConditionEditor.h" +#include "ClickStateConditionEditor.h" +#include "ConditionEditor.h" +#include "FocusConditionEditor.h" +#include "NoConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/platform/Color.h" +#include "cru/ui/Base.h" +#include "cru/ui/ThemeManager.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { + +CompoundConditionEditor::CompoundConditionEditor() { + SetLabel(u"Compound Condition"); + + GetContainer()->AddChild(&children_container_); + children_container_.SetMargin({10, 0, 0, 0}); + children_container_.SetFlexDirection(ui::controls::FlexDirection::Vertical); + children_container_.SetItemCrossAlign( + ui::controls::FlexCrossAlignment::Start); + + GetHeadContainer()->AddChild(add_child_button_.GetRootControl()); + + add_child_button_.GetButton()->GetStyleRuleSet()->SetParent( + ui::ThemeManager::GetInstance()->GetResourceStyleRuleSet( + u"cru.theme_builder.icon-button.style")); + add_child_button_.GetButton()->SetIconWithSvgPathDataStringResourceKey( + u"icon.plus", {0, 0, 16, 16}); + add_child_button_.GetButton()->SetPreferredSize({24, 24}); + add_child_button_.GetButton()->SetPadding(ui::Thickness(2)); + add_child_button_.GetButton()->SetIconFillColor(ui::colors::green); + add_child_button_.SetMenuItems({u"And Condition", u"Or Condition", + u"Click State Condition", u"Focus Condition", + u"Checked Condition", u"No Condition"}); + add_child_button_.MenuItemSelectedEvent()->AddHandler([this](Index index) { + std::unique_ptr<ConditionEditor> editor; + switch (index) { + case 0: + editor = std::make_unique<AndConditionEditor>(); + break; + case 1: + editor = std::make_unique<OrConditionEditor>(); + break; + case 2: + editor = std::make_unique<ClickStateConditionEditor>(); + break; + case 3: + editor = std::make_unique<FocusConditionEditor>(); + break; + case 4: + editor = std::make_unique<CheckedConditionEditor>(); + break; + case 5: + editor = std::make_unique<NoConditionEditor>(); + break; + default: + break; + } + if (editor) { + ConnectChangeEvent(editor.get()); + editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { + auto index = this->children_container_.IndexOf(c->GetRootControl()); + this->children_.erase(this->children_.begin() + index); + this->children_container_.RemoveChildAt(index); + RaiseChangeEvent(); + }); + children_.push_back(std::move(editor)); + children_container_.AddChild(children_.back()->GetRootControl()); + RaiseChangeEvent(); + } + }); +} + +CompoundConditionEditor::~CompoundConditionEditor() {} + +std::vector<ClonablePtr<ui::style::Condition>> +CompoundConditionEditor::GetChildren() { + std::vector<ClonablePtr<ui::style::Condition>> children; + for (auto& child : children_) { + children.push_back(child->GetCondition()); + } + return children; +} + +void CompoundConditionEditor::SetChildren( + std::vector<ClonablePtr<ui::style::Condition>> children, + bool trigger_change) { + children_container_.ClearChildren(); + children_.clear(); + for (const auto& condition : children) { + auto editor = CreateConditionEditor(condition.get()); + ConnectChangeEvent(editor.get()); + editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { + auto index = this->children_container_.IndexOf(c->GetRootControl()); + this->children_.erase(this->children_.begin() + index); + this->children_container_.RemoveChildAt(index); + RaiseChangeEvent(); + }); + children_.push_back(std::move(editor)); + children_container_.AddChild(children_.back()->GetRootControl()); + } + if (trigger_change) { + RaiseChangeEvent(); + } +} +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/CompoundConditionEditor.h b/src/ThemeBuilder/components/conditions/CompoundConditionEditor.h new file mode 100644 index 00000000..e1398514 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/CompoundConditionEditor.h @@ -0,0 +1,72 @@ +#pragma once +#include "ConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/common/Event.h" +#include "cru/ui/components/Component.h" +#include "cru/ui/components/PopupButton.h" +#include "cru/ui/controls/Button.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/controls/TextBlock.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +class CompoundConditionEditor : public ConditionEditor { + public: + CompoundConditionEditor(); + ~CompoundConditionEditor(); + + protected: + std::vector<ClonablePtr<ui::style::Condition>> GetChildren(); + void SetChildren(std::vector<ClonablePtr<ui::style::Condition>> children, + bool trigger_change = true); + + private: + ui::components::PopupMenuIconButton add_child_button_; + ui::controls::FlexLayout children_container_; + std::vector<ui::DeleteLaterPtr<ConditionEditor>> children_; +}; + +class AndConditionEditor : public CompoundConditionEditor { + public: + AndConditionEditor() = default; + ~AndConditionEditor() override = default; + + public: + ClonablePtr<ui::style::AndCondition> GetValue() { + return ui::style::AndCondition::Create(GetChildren()); + } + void SetValue(ui::style::AndCondition* value, bool trigger_change = true) { + SetChildren(value->GetChildren(), trigger_change); + } + void SetValue(const ClonablePtr<ui::style::AndCondition>& value, + bool trigger_change = true) { + SetValue(value.get(), trigger_change); + } + + ClonablePtr<ui::style::Condition> GetCondition() override { + return GetValue(); + } +}; + +class OrConditionEditor : public CompoundConditionEditor { + public: + OrConditionEditor() = default; + ~OrConditionEditor() override = default; + + public: + ClonablePtr<ui::style::OrCondition> GetValue() { + return ui::style::OrCondition::Create(GetChildren()); + } + void SetValue(ui::style::OrCondition* value, bool trigger_change = true) { + SetChildren(value->GetChildren(), trigger_change); + } + void SetValue(const ClonablePtr<ui::style::OrCondition>& value, + bool trigger_change = true) { + SetValue(value.get(), trigger_change); + } + + ClonablePtr<ui::style::Condition> GetCondition() override { + return GetValue(); + } +}; +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/ConditionEditor.cpp b/src/ThemeBuilder/components/conditions/ConditionEditor.cpp new file mode 100644 index 00000000..5b79c639 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/ConditionEditor.cpp @@ -0,0 +1,50 @@ +#include "ConditionEditor.h" +#include "../Common.h" +#include "CheckedConditionEditor.h" +#include "ClickStateConditionEditor.h" +#include "CompoundConditionEditor.h" +#include "FocusConditionEditor.h" +#include "NoConditionEditor.h" +#include "cru/common/Exception.h" +#include "cru/ui/controls/FlexLayout.h" + +namespace cru::theme_builder::components::conditions { +ConditionEditor::ConditionEditor() {} + +ConditionEditor::~ConditionEditor() {} + +std::unique_ptr<ConditionEditor> CreateConditionEditor( + ui::style::Condition* condition) { + if (auto and_condition = dynamic_cast<ui::style::AndCondition*>(condition)) { + auto result = std::make_unique<AndConditionEditor>(); + result->SetValue(and_condition); + return result; + } else if (auto or_condition = + dynamic_cast<ui::style::OrCondition*>(condition)) { + auto result = std::make_unique<OrConditionEditor>(); + result->SetValue(or_condition); + return result; + } else if (auto no_condition = + dynamic_cast<ui::style::NoCondition*>(condition)) { + auto result = std::make_unique<NoConditionEditor>(); + return result; + } else if (auto click_state_condition = + dynamic_cast<ui::style::ClickStateCondition*>(condition)) { + auto result = std::make_unique<ClickStateConditionEditor>(); + result->SetValue(click_state_condition); + return result; + } else if (auto focus_condition = + dynamic_cast<ui::style::FocusCondition*>(condition)) { + auto result = std::make_unique<FocusConditionEditor>(); + result->SetValue(focus_condition); + return result; + } else if (auto checked_condition = + dynamic_cast<ui::style::CheckedCondition*>(condition)) { + auto result = std::make_unique<CheckedConditionEditor>(); + result->SetValue(checked_condition); + return result; + } else { + throw Exception(u"Unknown condition type"); + } +} +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/ConditionEditor.h b/src/ThemeBuilder/components/conditions/ConditionEditor.h new file mode 100644 index 00000000..f20132f6 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/ConditionEditor.h @@ -0,0 +1,17 @@ +#pragma once +#include "../HeadBodyEditor.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +class ConditionEditor : public HeadBodyEditor { + public: + ConditionEditor(); + ~ConditionEditor() override; + + public: + virtual ClonablePtr<ui::style::Condition> GetCondition() = 0; +}; + +std::unique_ptr<ConditionEditor> CreateConditionEditor( + ui::style::Condition* condition); +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/FocusConditionEditor.cpp b/src/ThemeBuilder/components/conditions/FocusConditionEditor.cpp new file mode 100644 index 00000000..1fb99d64 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/FocusConditionEditor.cpp @@ -0,0 +1,25 @@ +#include "FocusConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +FocusConditionEditor::FocusConditionEditor() { + SetLabel(u"Focus Condition"); + GetContainer()->AddChild(focus_check_box_.GetRootControl()); + + focus_check_box_.SetLabel(u"Focus"); + + ConnectChangeEvent(focus_check_box_); +} + +FocusConditionEditor::~FocusConditionEditor() {} + +ClonablePtr<ui::style::FocusCondition> FocusConditionEditor::GetValue() const { + return ui::style::FocusCondition::Create(focus_check_box_.GetValue()); +} + +void FocusConditionEditor::SetValue(ui::style::FocusCondition* value, + bool trigger_change) { + focus_check_box_.SetValue(value->IsHasFocus(), trigger_change); +} +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/FocusConditionEditor.h b/src/ThemeBuilder/components/conditions/FocusConditionEditor.h new file mode 100644 index 00000000..1faf4d7d --- /dev/null +++ b/src/ThemeBuilder/components/conditions/FocusConditionEditor.h @@ -0,0 +1,28 @@ +#pragma once +#include "../properties/CheckBoxPropertyEditor.h" +#include "ConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +class FocusConditionEditor : public ConditionEditor { + public: + FocusConditionEditor(); + ~FocusConditionEditor() override; + + public: + ClonablePtr<ui::style::FocusCondition> GetValue() const; + void SetValue(ui::style::FocusCondition* value, bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::FocusCondition>& value, + bool trigger_change = true) { + SetValue(value.get(), trigger_change); + } + + ClonablePtr<ui::style::Condition> GetCondition() override { + return GetValue(); + } + + private: + properties::CheckBoxPropertyEditor focus_check_box_; +}; +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/NoConditionEditor.cpp b/src/ThemeBuilder/components/conditions/NoConditionEditor.cpp new file mode 100644 index 00000000..a5087159 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/NoConditionEditor.cpp @@ -0,0 +1,7 @@ +#include "NoConditionEditor.h" + +namespace cru::theme_builder::components::conditions { +NoConditionEditor::NoConditionEditor() { SetLabel(u"No condition"); } + +NoConditionEditor::~NoConditionEditor() {} +} // namespace cru::theme_builder::components::conditions diff --git a/src/ThemeBuilder/components/conditions/NoConditionEditor.h b/src/ThemeBuilder/components/conditions/NoConditionEditor.h new file mode 100644 index 00000000..19616319 --- /dev/null +++ b/src/ThemeBuilder/components/conditions/NoConditionEditor.h @@ -0,0 +1,17 @@ +#pragma once +#include "ConditionEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Condition.h" + +namespace cru::theme_builder::components::conditions { +class NoConditionEditor : public ConditionEditor { + public: + NoConditionEditor(); + ~NoConditionEditor() override; + + public: + ClonablePtr<ui::style::Condition> GetCondition() override { + return ui::style::NoCondition::Create(); + } +}; +} // namespace cru::theme_builder::components::conditions 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 diff --git a/src/ThemeBuilder/components/stylers/BorderStylerEditor.cpp b/src/ThemeBuilder/components/stylers/BorderStylerEditor.cpp new file mode 100644 index 00000000..81eb66d3 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/BorderStylerEditor.cpp @@ -0,0 +1,100 @@ +#include "BorderStylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/platform/graphics/Brush.h" +#include "cru/platform/graphics/Factory.h" +#include "cru/platform/gui/UiApplication.h" +#include "cru/ui/style/ApplyBorderStyleInfo.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +BorderStylerEditor::BorderStylerEditor() { + SetLabel(u"Border Styler"); + GetContainer()->AddChild(corner_radius_editor_.GetRootControl()); + GetContainer()->AddChild(thickness_editor_.GetRootControl()); + GetContainer()->AddChild(brush_editor_.GetRootControl()); + GetContainer()->AddChild(foreground_brush_editor_.GetRootControl()); + GetContainer()->AddChild(background_brush_editor_.GetRootControl()); + + thickness_editor_.GetEditor()->SetLabel(u"Thickness"); + brush_editor_.GetEditor()->SetLabel(u"Border"); + foreground_brush_editor_.GetEditor()->SetLabel(u"Foreground"); + background_brush_editor_.GetEditor()->SetLabel(u"Background"); + + ConnectChangeEvent(corner_radius_editor_); + ConnectChangeEvent(thickness_editor_); + ConnectChangeEvent(brush_editor_); + ConnectChangeEvent(foreground_brush_editor_); + ConnectChangeEvent(background_brush_editor_); +} + +BorderStylerEditor::~BorderStylerEditor() {} + +ClonablePtr<ui::style::BorderStyler> BorderStylerEditor::GetValue() { + auto graphics_factory = + platform::gui::IUiApplication::GetInstance()->GetGraphicsFactory(); + + ui::style::ApplyBorderStyleInfo border_style; + border_style.border_radius = corner_radius_editor_.GetValue(); + border_style.border_thickness = thickness_editor_.GetValue(); + + if (brush_editor_.IsEnabled()) { + border_style.border_brush = graphics_factory->CreateSolidColorBrush( + brush_editor_.GetEditor()->GetValue()); + } + + if (foreground_brush_editor_.IsEnabled()) { + border_style.foreground_brush = graphics_factory->CreateSolidColorBrush( + foreground_brush_editor_.GetEditor()->GetValue()); + } + + if (background_brush_editor_.IsEnabled()) { + border_style.background_brush = graphics_factory->CreateSolidColorBrush( + background_brush_editor_.GetEditor()->GetValue()); + } + + return ui::style::BorderStyler::Create(border_style); +} + +void BorderStylerEditor::SetValue(ui::style::BorderStyler* styler, + bool trigger_change) { + Expects(styler); + + auto border_style = styler->GetBorderStyle(); + corner_radius_editor_.SetValue(border_style.border_radius, false); + thickness_editor_.SetValue(border_style.border_thickness, false); + + brush_editor_.SetEnabled(border_style.border_brush.has_value(), false); + if (border_style.border_brush.has_value()) { + brush_editor_.GetEditor()->SetValue( + std::dynamic_pointer_cast<platform::graphics::ISolidColorBrush>( + border_style.border_brush.value()) + ->GetColor(), + false); + } + + foreground_brush_editor_.SetEnabled(border_style.foreground_brush.has_value(), + false); + if (border_style.foreground_brush.has_value()) { + foreground_brush_editor_.GetEditor()->SetValue( + std::dynamic_pointer_cast<platform::graphics::ISolidColorBrush>( + border_style.foreground_brush.value()) + ->GetColor(), + false); + } + + background_brush_editor_.SetEnabled(border_style.background_brush.has_value(), + false); + if (border_style.background_brush.has_value()) { + background_brush_editor_.GetEditor()->SetValue( + std::dynamic_pointer_cast<platform::graphics::ISolidColorBrush>( + border_style.background_brush.value()) + ->GetColor(), + false); + } + + if (trigger_change) { + RaiseChangeEvent(); + } +} + +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/BorderStylerEditor.h b/src/ThemeBuilder/components/stylers/BorderStylerEditor.h new file mode 100644 index 00000000..539262d6 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/BorderStylerEditor.h @@ -0,0 +1,36 @@ +#pragma once +#include "../properties/ColorPropertyEditor.h" +#include "../properties/CornerRadiusPropertyEditor.h" +#include "../properties/OptionalPropertyEditor.h" +#include "../properties/ThicknessPropertyEditor.h" +#include "StylerEditor.h" +#include "cru/common/ClonablePtr.h" + +namespace cru::theme_builder::components::stylers { +class BorderStylerEditor : public StylerEditor { + public: + BorderStylerEditor(); + ~BorderStylerEditor() override; + + ClonablePtr<ui::style::BorderStyler> GetValue(); + void SetValue(ui::style::BorderStyler* styler, bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::BorderStyler>& styler, + bool trigger_change = true) { + SetValue(styler.get(), trigger_change); + } + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + properties::OptionalPropertyEditor<properties::CornerRadiusPropertyEditor> + corner_radius_editor_; + properties::OptionalPropertyEditor<properties::ThicknessPropertyEditor> + thickness_editor_; + properties::OptionalPropertyEditor<properties::ColorPropertyEditor> + brush_editor_; + properties::OptionalPropertyEditor<properties::ColorPropertyEditor> + foreground_brush_editor_; + properties::OptionalPropertyEditor<properties::ColorPropertyEditor> + background_brush_editor_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/CompoundStylerEditor.cpp b/src/ThemeBuilder/components/stylers/CompoundStylerEditor.cpp new file mode 100644 index 00000000..6b8a5033 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/CompoundStylerEditor.cpp @@ -0,0 +1,105 @@ +#include "CompoundStylerEditor.h" +#include "BorderStylerEditor.h" +#include "ContentBrushStylerEditor.h" +#include "CursorStylerEditor.h" +#include "FontStylerEditor.h" +#include "MarginStylerEditor.h" +#include "PaddingStylerEditor.h" +#include "PreferredSizeStylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/ThemeManager.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +CompoundStylerEditor::CompoundStylerEditor() { + SetLabel(u"Compound Styler"); + GetContainer()->AddChild(&children_container_); + children_container_.SetFlexDirection(ui::controls::FlexDirection::Vertical); + children_container_.SetItemCrossAlign( + ui::controls::FlexCrossAlignment::Start); + + GetHeadContainer()->AddChild(add_child_button_.GetRootControl()); + add_child_button_.GetButton()->GetStyleRuleSet()->SetParent( + ui::ThemeManager::GetInstance()->GetResourceStyleRuleSet( + u"cru.theme_builder.icon-button.style")); + add_child_button_.GetButton()->SetIconWithSvgPathDataStringResourceKey( + u"icon.plus", {0, 0, 16, 16}); + add_child_button_.GetButton()->SetPreferredSize({24, 24}); + add_child_button_.GetButton()->SetPadding(ui::Thickness(2)); + add_child_button_.GetButton()->SetIconFillColor(ui::colors::green); + add_child_button_.SetMenuItems({u"Compound Styler", u"Border Styler", + u"Cursor Styler", u"Content Brush Styler", + u"Font Styler", u"Margin Styler", + u"Padding Styler", u"Preferred Size Styler"}); + add_child_button_.MenuItemSelectedEvent()->AddHandler([this](Index index) { + std::unique_ptr<StylerEditor> editor; + switch (index) { + case 0: + editor = std::make_unique<CompoundStylerEditor>(); + break; + case 1: + editor = std::make_unique<BorderStylerEditor>(); + break; + case 2: + editor = std::make_unique<CursorStylerEditor>(); + break; + case 3: + editor = std::make_unique<ContentBrushStylerEditor>(); + break; + case 4: + editor = std::make_unique<FontStylerEditor>(); + break; + case 5: + editor = std::make_unique<MarginStylerEditor>(); + break; + case 6: + editor = std::make_unique<PaddingStylerEditor>(); + break; + case 7: + editor = std::make_unique<PreferredSizeStylerEditor>(); + break; + default: + break; + } + if (editor) { + ConnectChangeEvent(editor.get()); + editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { + auto index = this->children_container_.IndexOf(c->GetRootControl()); + this->children_.erase(this->children_.begin() + index); + this->children_container_.RemoveChildAt(index); + RaiseChangeEvent(); + }); + children_.push_back(std::move(editor)); + children_container_.AddChild(editor->GetRootControl()); + RaiseChangeEvent(); + } + }); +} + +CompoundStylerEditor::~CompoundStylerEditor() {} + +ClonablePtr<ui::style::CompoundStyler> CompoundStylerEditor::GetValue() { + std::vector<ClonablePtr<ui::style::Styler>> children_styler; + for (auto& child : children_) { + children_styler.push_back(child->GetStyler()); + } + return ui::style::CompoundStyler::Create(std::move(children_styler)); +} + +void CompoundStylerEditor::SetValue(ui::style::CompoundStyler* value, + bool trigger_change) { + children_.clear(); + for (const auto& styler : value->GetChildren()) { + auto editor = CreateStylerEditor(styler.get()); + ConnectChangeEvent(editor.get()); + editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { + auto index = this->children_container_.IndexOf(c->GetRootControl()); + this->children_.erase(this->children_.begin() + index); + this->children_container_.RemoveChildAt(index); + RaiseChangeEvent(); + }); + children_.push_back(std::move(editor)); + children_container_.AddChild(children_.back()->GetRootControl()); + } +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/CompoundStylerEditor.h b/src/ThemeBuilder/components/stylers/CompoundStylerEditor.h new file mode 100644 index 00000000..57150e83 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/CompoundStylerEditor.h @@ -0,0 +1,30 @@ +#pragma once +#include "StylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/DeleteLater.h" +#include "cru/ui/components/PopupButton.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +class CompoundStylerEditor : public StylerEditor { + public: + CompoundStylerEditor(); + ~CompoundStylerEditor() override; + + public: + ClonablePtr<ui::style::CompoundStyler> GetValue(); + void SetValue(ui::style::CompoundStyler* styler, bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::CompoundStyler>& styler, + bool trigger_change = true) { + SetValue(styler.get(), trigger_change); + } + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + ui::controls::FlexLayout children_container_; + std::vector<ui::DeleteLaterPtr<StylerEditor>> children_; + ui::components::PopupMenuIconButton add_child_button_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/ContentBrushStylerEditor.cpp b/src/ThemeBuilder/components/stylers/ContentBrushStylerEditor.cpp new file mode 100644 index 00000000..bd649c67 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/ContentBrushStylerEditor.cpp @@ -0,0 +1,31 @@ +#include "ContentBrushStylerEditor.h" +#include "cru/platform/graphics/Factory.h" +#include "cru/platform/gui/UiApplication.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +ContentBrushStylerEditor::ContentBrushStylerEditor() { + GetContainer()->AddChild(color_editor_.GetRootControl()); + + ConnectChangeEvent(color_editor_); +} + +ContentBrushStylerEditor::~ContentBrushStylerEditor() {} + +ClonablePtr<ui::style::ContentBrushStyler> ContentBrushStylerEditor::GetValue() + const { + return ui::style::ContentBrushStyler::Create( + platform::gui::IUiApplication::GetInstance() + ->GetGraphicsFactory() + ->CreateSolidColorBrush(color_editor_.GetValue())); +} + +void ContentBrushStylerEditor::SetValue(ui::style::ContentBrushStyler* value, + bool trigger_change) { + color_editor_.SetValue( + std::dynamic_pointer_cast<platform::graphics::ISolidColorBrush>( + value->GetBrush()) + ->GetColor(), + trigger_change); +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/ContentBrushStylerEditor.h b/src/ThemeBuilder/components/stylers/ContentBrushStylerEditor.h new file mode 100644 index 00000000..8385b7c3 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/ContentBrushStylerEditor.h @@ -0,0 +1,24 @@ +#pragma once +#include "../Editor.h" +#include "../properties/ColorPropertyEditor.h" +#include "StylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +class ContentBrushStylerEditor : public StylerEditor { + public: + ContentBrushStylerEditor(); + ~ContentBrushStylerEditor(); + + public: + ClonablePtr<ui::style::ContentBrushStyler> GetValue() const; + void SetValue(ui::style::ContentBrushStyler* value, + bool trigger_change = true); + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + properties::ColorPropertyEditor color_editor_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/CursorStylerEditor.cpp b/src/ThemeBuilder/components/stylers/CursorStylerEditor.cpp new file mode 100644 index 00000000..9984d81a --- /dev/null +++ b/src/ThemeBuilder/components/stylers/CursorStylerEditor.cpp @@ -0,0 +1,65 @@ +#include "CursorStylerEditor.h" +#include "cru/platform/gui/Cursor.h" +#include "cru/platform/gui/UiApplication.h" + +namespace cru::theme_builder::components::stylers { +CursorStylerEditor::CursorStylerEditor() { + SetLabel(u"Cursor Styler"); + GetContainer()->AddChild(cursor_select_.GetRootControl()); + + cursor_select_.SetLabel(u"Cursor"); + cursor_select_.SetItems({u"arrow", u"hand", u"ibeam"}); + cursor_select_.SetSelectedIndex(0); + + ConnectChangeEvent(cursor_select_); +} + +CursorStylerEditor::~CursorStylerEditor() {} + +ClonablePtr<ui::style::CursorStyler> CursorStylerEditor::GetValue() { + auto cursor_manager = + platform::gui::IUiApplication::GetInstance()->GetCursorManager(); + + std::shared_ptr<platform::gui::ICursor> cursor; + + switch (cursor_select_.GetSelectedIndex()) { + case 0: + cursor = cursor_manager->GetSystemCursor( + platform::gui::SystemCursorType::Arrow); + break; + case 1: + cursor = cursor_manager->GetSystemCursor( + platform::gui::SystemCursorType::Hand); + break; + case 2: + cursor = cursor_manager->GetSystemCursor( + platform::gui::SystemCursorType::IBeam); + break; + } + + return ui::style::CursorStyler::Create(cursor); +} + +void CursorStylerEditor::SetValue(ui::style::CursorStyler* styler, + bool trigger_change) { + auto cursor_manager = + platform::gui::IUiApplication::GetInstance()->GetCursorManager(); + + auto cursor = styler->GetCursor(); + + if (cursor == + cursor_manager->GetSystemCursor(platform::gui::SystemCursorType::Arrow)) { + cursor_select_.SetSelectedIndex(0); + } else if (cursor == cursor_manager->GetSystemCursor( + platform::gui::SystemCursorType::Hand)) { + cursor_select_.SetSelectedIndex(1); + } else if (cursor == cursor_manager->GetSystemCursor( + platform::gui::SystemCursorType::IBeam)) { + cursor_select_.SetSelectedIndex(2); + } + + if (trigger_change) { + RaiseChangeEvent(); + } +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/CursorStylerEditor.h b/src/ThemeBuilder/components/stylers/CursorStylerEditor.h new file mode 100644 index 00000000..552619a0 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/CursorStylerEditor.h @@ -0,0 +1,24 @@ +#pragma once +#include "../properties/SelectPropertyEditor.h" +#include "StylerEditor.h" + +namespace cru::theme_builder::components::stylers { +class CursorStylerEditor : public StylerEditor { + public: + CursorStylerEditor(); + ~CursorStylerEditor() override; + + public: + ClonablePtr<ui::style::CursorStyler> GetValue(); + void SetValue(ui::style::CursorStyler* styler, bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::CursorStyler>& styler, + bool trigger_change = true) { + SetValue(styler.get(), trigger_change); + } + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + properties::SelectPropertyEditor cursor_select_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/FontStylerEditor.cpp b/src/ThemeBuilder/components/stylers/FontStylerEditor.cpp new file mode 100644 index 00000000..9787a4fd --- /dev/null +++ b/src/ThemeBuilder/components/stylers/FontStylerEditor.cpp @@ -0,0 +1,23 @@ +#include "FontStylerEditor.h" +#include "cru/platform/graphics/Factory.h" +#include "cru/platform/gui/UiApplication.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +FontStylerEditor::FontStylerEditor() { + GetContainer()->AddChild(font_editor_.GetRootControl()); + + ConnectChangeEvent(font_editor_); +} + +FontStylerEditor::~FontStylerEditor() {} + +ClonablePtr<ui::style::FontStyler> FontStylerEditor::GetValue() const { + return ui::style::FontStyler::Create(font_editor_.GetValue()); +} + +void FontStylerEditor::SetValue(ui::style::FontStyler* value, + bool trigger_change) { + font_editor_.SetValue(value->GetFont(), trigger_change); +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/FontStylerEditor.h b/src/ThemeBuilder/components/stylers/FontStylerEditor.h new file mode 100644 index 00000000..ccd12113 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/FontStylerEditor.h @@ -0,0 +1,23 @@ +#pragma once +#include "../Editor.h" +#include "../properties/FontPropertyEditor.h" +#include "StylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +class FontStylerEditor : public StylerEditor { + public: + FontStylerEditor(); + ~FontStylerEditor(); + + public: + ClonablePtr<ui::style::FontStyler> GetValue() const; + void SetValue(ui::style::FontStyler* value, bool trigger_change = true); + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + properties::FontPropertyEditor font_editor_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/MarginStylerEditor.cpp b/src/ThemeBuilder/components/stylers/MarginStylerEditor.cpp new file mode 100644 index 00000000..d7d89acb --- /dev/null +++ b/src/ThemeBuilder/components/stylers/MarginStylerEditor.cpp @@ -0,0 +1,28 @@ +#include "MarginStylerEditor.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +MarginStylerEditor::MarginStylerEditor() { + SetLabel(u"Margin Styler"); + GetContainer()->AddChild(thickness_editor_.GetRootControl()); + + thickness_editor_.SetLabel(u"Thickness"); + + ConnectChangeEvent(thickness_editor_); +} + +MarginStylerEditor::~MarginStylerEditor() {} + +ClonablePtr<ui::style::MarginStyler> MarginStylerEditor::GetValue() { + return ui::style::MarginStyler::Create(thickness_editor_.GetValue()); +} + +void MarginStylerEditor::SetValue(ui::style::MarginStyler* styler, + bool trigger_change) { + thickness_editor_.SetValue(styler->GetMargin(), false); + + if (trigger_change) { + RaiseChangeEvent(); + } +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/MarginStylerEditor.h b/src/ThemeBuilder/components/stylers/MarginStylerEditor.h new file mode 100644 index 00000000..9de6f1a2 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/MarginStylerEditor.h @@ -0,0 +1,25 @@ +#pragma once +#include "../properties/ThicknessPropertyEditor.h" +#include "StylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +class MarginStylerEditor : public StylerEditor { + public: + MarginStylerEditor(); + ~MarginStylerEditor() override; + + ClonablePtr<ui::style::MarginStyler> GetValue(); + void SetValue(ui::style::MarginStyler* styler, bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::MarginStyler>& styler, + bool trigger_change = true) { + SetValue(styler.get(), trigger_change); + } + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + properties::ThicknessPropertyEditor thickness_editor_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/PaddingStylerEditor.cpp b/src/ThemeBuilder/components/stylers/PaddingStylerEditor.cpp new file mode 100644 index 00000000..476d21f1 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/PaddingStylerEditor.cpp @@ -0,0 +1,28 @@ +#include "PaddingStylerEditor.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +PaddingStylerEditor::PaddingStylerEditor() { + SetLabel(u"Padding Styler"); + GetContainer()->AddChild(thickness_editor_.GetRootControl()); + + thickness_editor_.SetLabel(u"Thickness"); + + ConnectChangeEvent(thickness_editor_); +} + +PaddingStylerEditor::~PaddingStylerEditor() {} + +ClonablePtr<ui::style::PaddingStyler> PaddingStylerEditor::GetValue() { + return ui::style::PaddingStyler::Create(thickness_editor_.GetValue()); +} + +void PaddingStylerEditor::SetValue(ui::style::PaddingStyler* styler, + bool trigger_change) { + thickness_editor_.SetValue(styler->GetPadding(), false); + + if (trigger_change) { + RaiseChangeEvent(); + } +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/PaddingStylerEditor.h b/src/ThemeBuilder/components/stylers/PaddingStylerEditor.h new file mode 100644 index 00000000..b78d310b --- /dev/null +++ b/src/ThemeBuilder/components/stylers/PaddingStylerEditor.h @@ -0,0 +1,25 @@ +#pragma once +#include "../properties/ThicknessPropertyEditor.h" +#include "StylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +class PaddingStylerEditor : public StylerEditor { + public: + PaddingStylerEditor(); + ~PaddingStylerEditor() override; + + ClonablePtr<ui::style::PaddingStyler> GetValue(); + void SetValue(ui::style::PaddingStyler* styler, bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::PaddingStyler>& styler, + bool trigger_change = true) { + SetValue(styler.get(), trigger_change); + } + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + properties::ThicknessPropertyEditor thickness_editor_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/PreferredSizeStylerEditor.cpp b/src/ThemeBuilder/components/stylers/PreferredSizeStylerEditor.cpp new file mode 100644 index 00000000..fb713c8c --- /dev/null +++ b/src/ThemeBuilder/components/stylers/PreferredSizeStylerEditor.cpp @@ -0,0 +1,34 @@ +#include "PreferredSizeStylerEditor.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +PreferredSizeStylerEditor::PreferredSizeStylerEditor() { + SetLabel(u"Preferred Size Styler"); + GetContainer()->AddChild(width_editor_.GetRootControl()); + GetContainer()->AddChild(height_editor_.GetRootControl()); + + width_editor_.SetLabel(u"Width"); + height_editor_.SetLabel(u"Height"); + + ConnectChangeEvent(width_editor_); + ConnectChangeEvent(height_editor_); +} + +PreferredSizeStylerEditor::~PreferredSizeStylerEditor() {} + +ClonablePtr<ui::style::PreferredSizeStyler> +PreferredSizeStylerEditor::GetValue() { + return ui::style::PreferredSizeStyler::Create(ui::render::MeasureSize{ + width_editor_.GetValue(), height_editor_.GetValue()}); +} + +void PreferredSizeStylerEditor::SetValue(ui::style::PreferredSizeStyler* styler, + bool trigger_change) { + width_editor_.SetValue(styler->GetPreferredSize().width, false); + height_editor_.SetValue(styler->GetPreferredSize().height, false); + + if (trigger_change) { + RaiseChangeEvent(); + } +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/PreferredSizeStylerEditor.h b/src/ThemeBuilder/components/stylers/PreferredSizeStylerEditor.h new file mode 100644 index 00000000..4a64da10 --- /dev/null +++ b/src/ThemeBuilder/components/stylers/PreferredSizeStylerEditor.h @@ -0,0 +1,27 @@ +#pragma once +#include "../properties/MeasureLengthPropertyEditor.h" +#include "StylerEditor.h" +#include "cru/common/ClonablePtr.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +class PreferredSizeStylerEditor : public StylerEditor { + public: + PreferredSizeStylerEditor(); + ~PreferredSizeStylerEditor() override; + + ClonablePtr<ui::style::PreferredSizeStyler> GetValue(); + void SetValue(ui::style::PreferredSizeStyler* styler, + bool trigger_change = true); + void SetValue(const ClonablePtr<ui::style::PreferredSizeStyler>& styler, + bool trigger_change = true) { + SetValue(styler.get(), trigger_change); + } + + ClonablePtr<ui::style::Styler> GetStyler() override { return GetValue(); } + + private: + properties::MeasureLengthPropertyEditor width_editor_; + properties::MeasureLengthPropertyEditor height_editor_; +}; +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/StylerEditor.cpp b/src/ThemeBuilder/components/stylers/StylerEditor.cpp new file mode 100644 index 00000000..0348adbd --- /dev/null +++ b/src/ThemeBuilder/components/stylers/StylerEditor.cpp @@ -0,0 +1,63 @@ +#include "StylerEditor.h" +#include "../Common.h" +#include "BorderStylerEditor.h" +#include "CompoundStylerEditor.h" +#include "ContentBrushStylerEditor.h" +#include "CursorStylerEditor.h" +#include "FontStylerEditor.h" +#include "MarginStylerEditor.h" +#include "PaddingStylerEditor.h" +#include "PreferredSizeStylerEditor.h" +#include "cru/ui/controls/FlexLayout.h" +#include "cru/ui/render/FlexLayoutRenderObject.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +StylerEditor::StylerEditor() {} + +StylerEditor::~StylerEditor() {} + +std::unique_ptr<StylerEditor> CreateStylerEditor(ui::style::Styler* styler) { + if (auto compound_styler = dynamic_cast<ui::style::CompoundStyler*>(styler)) { + auto result = std::make_unique<CompoundStylerEditor>(); + result->SetValue(compound_styler); + return result; + } else if (auto border_styler = + dynamic_cast<ui::style::BorderStyler*>(styler)) { + auto editor = std::make_unique<BorderStylerEditor>(); + editor->SetValue(border_styler); + return editor; + } else if (auto cursor_styler = + dynamic_cast<ui::style::CursorStyler*>(styler)) { + auto editor = std::make_unique<CursorStylerEditor>(); + editor->SetValue(cursor_styler); + return editor; + } else if (auto preferred_size_styler = + dynamic_cast<ui::style::PreferredSizeStyler*>(styler)) { + auto editor = std::make_unique<PreferredSizeStylerEditor>(); + editor->SetValue(preferred_size_styler); + return editor; + } else if (auto margin_styler = + dynamic_cast<ui::style::MarginStyler*>(styler)) { + auto editor = std::make_unique<MarginStylerEditor>(); + editor->SetValue(margin_styler); + return editor; + } else if (auto padding_styler = + dynamic_cast<ui::style::PaddingStyler*>(styler)) { + auto editor = std::make_unique<PaddingStylerEditor>(); + editor->SetValue(padding_styler); + return editor; + } else if (auto content_brush_styler = + dynamic_cast<ui::style::ContentBrushStyler*>(styler)) { + auto editor = std::make_unique<ContentBrushStylerEditor>(); + editor->SetValue(content_brush_styler); + return editor; + } else if (auto font_styler = dynamic_cast<ui::style::FontStyler*>(styler)) { + auto editor = std::make_unique<FontStylerEditor>(); + editor->SetValue(font_styler); + return editor; + } else { + throw Exception(u"Unknown styler type"); + } +} +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/components/stylers/StylerEditor.h b/src/ThemeBuilder/components/stylers/StylerEditor.h new file mode 100644 index 00000000..8aa52bda --- /dev/null +++ b/src/ThemeBuilder/components/stylers/StylerEditor.h @@ -0,0 +1,16 @@ +#pragma once +#include "../HeadBodyEditor.h" +#include "cru/ui/style/Styler.h" + +namespace cru::theme_builder::components::stylers { +class StylerEditor : public HeadBodyEditor { + public: + StylerEditor(); + ~StylerEditor() override; + + public: + virtual ClonablePtr<ui::style::Styler> GetStyler() = 0; +}; + +std::unique_ptr<StylerEditor> CreateStylerEditor(ui::style::Styler* styler); +} // namespace cru::theme_builder::components::stylers diff --git a/src/ThemeBuilder/main.cpp b/src/ThemeBuilder/main.cpp new file mode 100644 index 00000000..0c5b2159 --- /dev/null +++ b/src/ThemeBuilder/main.cpp @@ -0,0 +1,25 @@ +#include "components/MainWindow.h" +#include "cru/common/io/Resource.h" +#include "cru/platform/bootstrap/Bootstrap.h" +#include "cru/ui/ThemeManager.h" +#include "cru/ui/ThemeResourceDictionary.h" + +int main() { + using namespace cru::theme_builder::components; + using namespace cru::ui; + + auto resource_dir = cru::io::GetResourceDir(); + + ThemeManager::GetInstance()->PrependThemeResourceDictionary( + ThemeResourceDictionary::FromFile( + resource_dir / "cru/theme_builder/ThemeResources.xml")); + + std::unique_ptr<cru::platform::gui::IUiApplication> application( + cru::platform::bootstrap::CreateUiApplication()); + + auto main_window = std::make_unique<MainWindow>(); + + main_window->Show(); + + return application->Run(); +} |