aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/common/HandlerRegistry.hpp86
-rw-r--r--include/cru/ui/controls/Button.hpp12
-rw-r--r--include/cru/ui/controls/IClickableControl.hpp12
-rw-r--r--include/cru/ui/helper/Styler.hpp47
-rw-r--r--include/cru/ui/style/Styler.hpp0
-rw-r--r--include/cru/ui/style/Trigger.hpp75
-rw-r--r--src/ui/CMakeLists.txt4
-rw-r--r--src/ui/helper/Styler.cpp27
-rw-r--r--src/ui/style/Styler.cpp0
-rw-r--r--src/ui/style/Trigger.cpp49
-rw-r--r--test/common/HandlerRegistryTest.cpp36
11 files changed, 271 insertions, 77 deletions
diff --git a/include/cru/common/HandlerRegistry.hpp b/include/cru/common/HandlerRegistry.hpp
new file mode 100644
index 00000000..bd74a9e0
--- /dev/null
+++ b/include/cru/common/HandlerRegistry.hpp
@@ -0,0 +1,86 @@
+#pragma once
+#include "Base.hpp"
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+#include <vector>
+
+namespace cru {
+
+template <typename T>
+class HandlerRegistryIterator {
+ public:
+ using RawIterator =
+ typename std::vector<std::pair<int, std::function<T>>>::const_iterator;
+
+ explicit HandlerRegistryIterator(RawIterator raw) : raw_(std::move(raw)) {}
+
+ CRU_DELETE_COPY(HandlerRegistryIterator)
+ CRU_DELETE_MOVE(HandlerRegistryIterator)
+
+ ~HandlerRegistryIterator() = default;
+
+ const std::function<T>& operator*() const { return raw_->second; }
+ const std::function<T>* operator->() const { return &raw_->second; }
+
+ HandlerRegistryIterator& operator++() {
+ ++raw_;
+ return *this;
+ }
+
+ HandlerRegistryIterator operator++(int) {
+ auto c = *this;
+ this->operator++();
+ return c;
+ }
+
+ bool operator==(const HandlerRegistryIterator<T>& other) const {
+ return this->raw_ == other.raw_;
+ }
+
+ bool operator!=(const HandlerRegistryIterator<T>& other) const {
+ return !this->operator==(other);
+ }
+
+ private:
+ RawIterator raw_;
+};
+
+template <typename T>
+class HandlerRegistry final {
+ public:
+ HandlerRegistry() = default;
+ CRU_DEFAULT_COPY(HandlerRegistry)
+ CRU_DEFAULT_MOVE(HandlerRegistry)
+ ~HandlerRegistry() = default;
+
+ public:
+ int AddHandler(std::function<T> handler) {
+ auto id = current_id_++;
+ handler_list_.push_back({id, std::move(handler)});
+ }
+
+ void RemoveHandler(int id) {
+ auto result = std::find_if(handler_list_.cbegin(), handler_list_.cend(),
+ [id](const std::pair<int, std::function<T>>& d) {
+ return d.first == id;
+ });
+ if (result != handler_list_.cend()) {
+ handler_list_.erase(result);
+ }
+ }
+
+ HandlerRegistryIterator<T> begin() const {
+ return HandlerRegistryIterator<T>(handler_list_.begin());
+ }
+
+ HandlerRegistryIterator<T> end() const {
+ return HandlerRegistryIterator<T>(handler_list_.begin());
+ }
+
+ private:
+ int current_id_ = 1;
+ std::vector<std::pair<int, std::function<T>>> handler_list_;
+};
+} // namespace cru
diff --git a/include/cru/ui/controls/Button.hpp b/include/cru/ui/controls/Button.hpp
index 5619ec89..0d2f4898 100644
--- a/include/cru/ui/controls/Button.hpp
+++ b/include/cru/ui/controls/Button.hpp
@@ -2,9 +2,11 @@
#include "ContentControl.hpp"
#include "../helper/ClickDetector.hpp"
+#include "IClickableControl.hpp"
+#include "cru/common/Event.hpp"
namespace cru::ui::controls {
-class Button : public ContentControl {
+class Button : public ContentControl, public virtual IClickableControl {
public:
static constexpr std::u16string_view control_type = u"Button";
@@ -25,6 +27,14 @@ class Button : public ContentControl {
render::RenderObject* GetRenderObject() const override;
public:
+ helper::ClickState GetClickState() override {
+ return click_detector_.GetState();
+ }
+
+ IEvent<helper::ClickState>* ClickStateChangeEvent() override {
+ return click_detector_.StateChangeEvent();
+ }
+
const ButtonStyle& GetStyle() const { return style_; }
void SetStyle(ButtonStyle style);
diff --git a/include/cru/ui/controls/IClickableControl.hpp b/include/cru/ui/controls/IClickableControl.hpp
new file mode 100644
index 00000000..aa7f13ab
--- /dev/null
+++ b/include/cru/ui/controls/IClickableControl.hpp
@@ -0,0 +1,12 @@
+#pragma once
+#include "Base.hpp"
+
+#include "cru/common/Event.hpp"
+#include "cru/ui/helper/ClickDetector.hpp"
+
+namespace cru::ui::controls {
+struct IClickableControl : virtual Interface {
+ virtual helper::ClickState GetClickState() = 0;
+ virtual IEvent<helper::ClickState>* ClickStateChangeEvent() = 0;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/helper/Styler.hpp b/include/cru/ui/helper/Styler.hpp
deleted file mode 100644
index ed8bfbdc..00000000
--- a/include/cru/ui/helper/Styler.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-#include "cru/common/Base.hpp"
-#include "cru/common/Event.hpp"
-#include "cru/ui/Base.hpp"
-#include "cru/ui/helper/ClickDetector.hpp"
-#include "gsl/pointers"
-
-#include <memory>
-
-namespace cru::ui::helper {
-struct ControlStyleState {
- ClickState click_state;
- bool focus;
-};
-
-class Styler : public Object {
- public:
- // You could provide your click detector. Otherwise a new one will be created.
- explicit Styler(gsl::not_null<controls::Control*> control,
- ClickDetector* click_detector = nullptr);
-
- CRU_DELETE_COPY(Styler)
- CRU_DELETE_MOVE(Styler)
-
- ~Styler();
-
- public:
- gsl::not_null<controls::Control*> GetControl() const { return control_; }
- gsl::not_null<ClickDetector*> GetClickDetector() const {
- return click_detector_;
- }
-
- IEvent<ControlStyleState>* StateChangeEvent() { return &state_change_event_; }
-
- private:
- void RaiseStateChangeEvent();
-
- private:
- gsl::not_null<controls::Control*> control_;
- std::unique_ptr<ClickDetector> managed_click_detector_;
- gsl::not_null<ClickDetector*> click_detector_;
-
- Event<ControlStyleState> state_change_event_;
-
- EventRevokerListGuard event_guard_;
-};
-} // namespace cru::ui::helper
diff --git a/include/cru/ui/style/Styler.hpp b/include/cru/ui/style/Styler.hpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/include/cru/ui/style/Styler.hpp
diff --git a/include/cru/ui/style/Trigger.hpp b/include/cru/ui/style/Trigger.hpp
new file mode 100644
index 00000000..ab012a3e
--- /dev/null
+++ b/include/cru/ui/style/Trigger.hpp
@@ -0,0 +1,75 @@
+#pragma once
+#include "../Base.hpp"
+#include "cru/common/Base.hpp"
+#include "cru/common/Event.hpp"
+#include "cru/ui/controls/IClickableControl.hpp"
+#include "cru/ui/helper/ClickDetector.hpp"
+
+#include <utility>
+#include <vector>
+
+namespace cru::ui::style {
+class Trigger {
+ public:
+ virtual ~Trigger() = default;
+
+ bool GetState() const { return current_; }
+ IEvent<bool>* ChangeEvent() { return &change_event_; }
+
+ protected:
+ void Raise(bool value);
+
+ private:
+ bool current_ = false;
+ Event<bool> change_event_;
+
+ protected:
+ EventRevokerListGuard guard_;
+};
+
+class CompoundTrigger : public Trigger {
+ public:
+ explicit CompoundTrigger(std::vector<Trigger*> triggers);
+
+ const std::vector<Trigger*>& GetTriggers() const { return triggers_; }
+
+ protected:
+ virtual bool CalculateState(const std::vector<Trigger*>& triggers) const = 0;
+
+ private:
+ std::vector<Trigger*> triggers_;
+};
+
+class AndTrigger : public CompoundTrigger {
+ public:
+ using CompoundTrigger::CompoundTrigger;
+
+ protected:
+ bool CalculateState(const std::vector<Trigger*>& triggers) const override;
+};
+
+class OrTrigger : public CompoundTrigger {
+ public:
+ using CompoundTrigger::CompoundTrigger;
+
+ protected:
+ bool CalculateState(const std::vector<Trigger*>& triggers) const override;
+};
+
+class FocusTrigger : public Trigger {
+ public:
+ FocusTrigger(controls::Control* control, bool has_focus);
+
+ private:
+ bool has_focus_;
+};
+
+class ClickStateTrigger : public Trigger {
+ public:
+ ClickStateTrigger(controls::IClickableControl* control,
+ helper::ClickState click_state);
+
+ private:
+ helper::ClickState click_state_;
+};
+} // namespace cru::ui::style
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 974a3959..5cb50ce3 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -24,7 +24,6 @@ add_library(cru_ui STATIC
helper/BorderStyle.cpp
helper/ClickDetector.cpp
helper/ShortcutHub.cpp
- helper/Styler.cpp
host/LayoutPaintCycler.cpp
host/WindowHost.cpp
render/BorderRenderObject.cpp
@@ -35,6 +34,7 @@ add_library(cru_ui STATIC
render/ScrollRenderObject.cpp
render/StackLayoutRenderObject.cpp
render/TextRenderObject.cpp
+ style/Trigger.cpp
)
target_sources(cru_ui PUBLIC
${CRU_UI_INCLUDE_DIR}/Base.hpp
@@ -58,7 +58,6 @@ target_sources(cru_ui PUBLIC
${CRU_UI_INCLUDE_DIR}/helper/BorderStyle.hpp
${CRU_UI_INCLUDE_DIR}/helper/ClickDetector.hpp
${CRU_UI_INCLUDE_DIR}/helper/ShortcutHub.hpp
- ${CRU_UI_INCLUDE_DIR}/helper/Styler.hpp
${CRU_UI_INCLUDE_DIR}/host/LayoutPaintCycler.hpp
${CRU_UI_INCLUDE_DIR}/host/WindowHost.hpp
${CRU_UI_INCLUDE_DIR}/render/Base.hpp
@@ -72,5 +71,6 @@ target_sources(cru_ui PUBLIC
${CRU_UI_INCLUDE_DIR}/render/ScrollRenderObject.hpp
${CRU_UI_INCLUDE_DIR}/render/StackLayoutRenderObject.hpp
${CRU_UI_INCLUDE_DIR}/render/TextRenderObject.hpp
+ ${CRU_UI_INCLUDE_DIR}/style/Trigger.hpp
)
target_link_libraries(cru_ui PUBLIC cru_platform_gui)
diff --git a/src/ui/helper/Styler.cpp b/src/ui/helper/Styler.cpp
deleted file mode 100644
index 6500a3f7..00000000
--- a/src/ui/helper/Styler.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "cru/ui/helper/Styler.hpp"
-#include "cru/ui/helper/ClickDetector.hpp"
-#include "gsl/pointers"
-
-namespace cru::ui::helper {
-Styler::Styler(gsl::not_null<controls::Control*> control,
- ClickDetector* click_detector)
- : control_(control),
- managed_click_detector_(click_detector ? nullptr
- : new ClickDetector(control)),
- click_detector_(click_detector ? click_detector
- : managed_click_detector_.get()) {
- event_guard_ += control_->GainFocusEvent()->Direct()->AddHandler(
- [this](auto) { this->RaiseStateChangeEvent(); });
- event_guard_ += control_->LoseFocusEvent()->Direct()->AddHandler(
- [this](auto) { this->RaiseStateChangeEvent(); });
- event_guard_ += click_detector_->StateChangeEvent()->AddHandler(
- [this](auto) { this->RaiseStateChangeEvent(); });
-}
-
-Styler::~Styler() = default;
-
-void Styler::RaiseStateChangeEvent() {
- this->state_change_event_.Raise(ControlStyleState{
- this->click_detector_->GetState(), this->control_->HasFocus()});
-}
-} // namespace cru::ui::helper
diff --git a/src/ui/style/Styler.cpp b/src/ui/style/Styler.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/ui/style/Styler.cpp
diff --git a/src/ui/style/Trigger.cpp b/src/ui/style/Trigger.cpp
new file mode 100644
index 00000000..b7292ce3
--- /dev/null
+++ b/src/ui/style/Trigger.cpp
@@ -0,0 +1,49 @@
+#include "cru/ui/style/Trigger.hpp"
+
+#include "cru/ui/controls/Control.hpp"
+#include "cru/ui/helper/ClickDetector.hpp"
+
+namespace cru::ui::style {
+void Trigger::Raise(bool value) {
+ if (value == current_) return;
+ current_ = value;
+ change_event_.Raise(value);
+}
+
+CompoundTrigger::CompoundTrigger(std::vector<Trigger*> triggers)
+ : triggers_(std::move(triggers)) {
+ for (auto trigger : triggers_) {
+ guard_ += trigger->ChangeEvent()->AddHandler(
+ [this](bool) { Raise(this->CalculateState(triggers_)); });
+ }
+}
+
+bool AndTrigger::CalculateState(const std::vector<Trigger*>& triggers) const {
+ for (auto trigger : triggers) {
+ if (!trigger->GetState()) return false;
+ }
+ return true;
+}
+
+bool OrTrigger::CalculateState(const std::vector<Trigger*>& triggers) const {
+ for (auto trigger : triggers) {
+ if (trigger->GetState()) return true;
+ }
+ return false;
+}
+
+FocusTrigger::FocusTrigger(controls::Control* control, bool has_focus)
+ : has_focus_(has_focus) {
+ guard_ += control->GainFocusEvent()->Direct()->AddHandler(
+ [this](auto) { Raise(has_focus_); });
+ guard_ += control->LoseFocusEvent()->Direct()->AddHandler(
+ [this](auto) { Raise(!has_focus_); });
+}
+
+ClickStateTrigger::ClickStateTrigger(controls::IClickableControl* control,
+ helper::ClickState click_state)
+ : click_state_(click_state) {
+ guard_ += control->ClickStateChangeEvent()->AddHandler(
+ [this](helper::ClickState cs) { Raise(cs == click_state_); });
+}
+} // namespace cru::ui::style
diff --git a/test/common/HandlerRegistryTest.cpp b/test/common/HandlerRegistryTest.cpp
new file mode 100644
index 00000000..d1792c7c
--- /dev/null
+++ b/test/common/HandlerRegistryTest.cpp
@@ -0,0 +1,36 @@
+#include "cru/common/HandlerRegistry.hpp"
+
+#include <gtest/gtest.h>
+#include <algorithm>
+
+TEST(HandlerRegistry, Work) {
+ using namespace cru;
+ HandlerRegistry<void()> registry;
+
+ int counter = 1;
+
+ auto tag1 = registry.AddHandler([&counter] { counter++; });
+ auto tag2 = registry.AddHandler([&counter] { counter++; });
+
+ for (const auto& handler : registry) {
+ handler();
+ }
+
+ ASSERT_EQ(counter, 3);
+
+ registry.RemoveHandler(tag1);
+
+ for (const auto& handler : registry) {
+ handler();
+ }
+
+ ASSERT_EQ(counter, 4);
+
+ registry.RemoveHandler(tag2);
+
+ for (const auto& handler : registry) {
+ handler();
+ }
+
+ ASSERT_EQ(counter, 4);
+}