Skip to content

Commit

Permalink
Feature/cla 42 add event listener scope guards (#99)
Browse files Browse the repository at this point in the history
* cla-42: Added event listener scope guards

* cla-42: Added listener scope guard to theme changer
  • Loading branch information
ebasconp authored Jan 1, 2025
1 parent 4f1fe10 commit 0f645eb
Show file tree
Hide file tree
Showing 20 changed files with 169 additions and 56 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# CHANGELOG #

* 20250101 added `event_listener_scope_guard`

* 20241230 added `layer_manager` to handle layers
* 20241220 created `system_backend` containing SDL2 event loop
* 20241212 removed `ui_manager` singleton. Now is injected through all controls.
Expand Down
1 change: 1 addition & 0 deletions code/LOC.csv
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Date,Value
2024-10-02,5472
2024-11-01,6077
2024-12-01,6734
2025-01-01,7637

1 change: 1 addition & 0 deletions code/classeine-demo/demo_xy_layout_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <clsn/ui/button.h>
#include <clsn/ui/label.h>
#include <clsn/ui/ui_manager.h>
#include <clsn/ui/ui_theme_manager.h>

namespace clsn::demo
Expand Down
22 changes: 18 additions & 4 deletions code/classeine-lib/clsn/core/entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,49 @@ namespace clsn::core
entity::entity()
{
#ifdef _CLSN_DEBUG_
++m_instances_created;
inc_instances_created();
m_living_entities.insert(this);
#endif
}

entity::entity(const entity&)
{
#ifdef _CLSN_DEBUG_
++m_instances_created;
inc_instances_created();
m_living_entities.insert(this);
#endif
}

entity::entity(entity&&) noexcept
{
#ifdef _CLSN_DEBUG_
++m_instances_created;
inc_instances_created();
m_living_entities.insert(this);
#endif
}

entity::~entity()
{
#ifdef _CLSN_DEBUG_
++m_instances_destroyed;
inc_instances_destroyed();
m_living_entities.erase(this);
#endif
}

void entity::inc_instances_created()
{
#ifdef _CLSN_DEBUG_
m_instances_created++;
#endif
}

void entity::inc_instances_destroyed()
{
#ifdef _CLSN_DEBUG_
m_instances_destroyed++;
#endif
}

void entity::debug_count(std::string_view key) const
{
#ifdef _CLSN_DEBUG_
Expand Down
3 changes: 3 additions & 0 deletions code/classeine-lib/clsn/core/entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ namespace clsn::core

void debug_count(std::string_view key) const;

static void inc_instances_created();
static void inc_instances_destroyed();

virtual ~entity();

virtual auto to_string() const -> std::string;
Expand Down
88 changes: 79 additions & 9 deletions code/classeine-lib/clsn/core/event_listener_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,102 @@
namespace clsn::core
{
template <typename EventType>
class event_listener_list final : public entity
struct event_listener_callback_container final
{
event_listener<EventType> callback;
bool enabled;
};

template <typename EventType>
struct event_listener_scope final
{
struct event_listener_container final
std::shared_ptr<std::vector<event_listener_callback_container<EventType>>> m_listeners;
int m_index;
};

class event_listener_scope_guard_base
{
public:
virtual ~event_listener_scope_guard_base() = default;
};

template <typename EventType>
class event_listener_scope_guard_impl : public event_listener_scope_guard_base
{
std::weak_ptr<std::vector<event_listener_callback_container<EventType>>> m_listeners;
int m_index;

public:
explicit event_listener_scope_guard_impl(std::shared_ptr<std::vector<event_listener_callback_container<EventType>>>&& listeners, int index)
: m_listeners{listeners}
, m_index{index}
{
event_listener<EventType> callback;
bool enabled;
};
entity::inc_instances_created();
}

std::unique_ptr<std::vector<event_listener_container>> m_listeners;
~event_listener_scope_guard_impl() override
{
entity::inc_instances_destroyed();

if (auto listeners = m_listeners.lock())
{
(*listeners)[m_index].enabled = false;
(*listeners)[m_index].callback = nullptr;
}
}
};

class event_listener_scope_guard final
{
std::unique_ptr<event_listener_scope_guard_base> m_impl;

public:
event_listener_scope_guard() = default;
~event_listener_scope_guard() = default;

template <typename EventType>
explicit event_listener_scope_guard(const event_listener_scope<EventType>& scope)
: m_impl{std::make_unique<event_listener_scope_guard_impl<EventType>>(scope.m_listeners, scope.m_index)}
{
}

template <typename EventType>
event_listener_scope_guard& operator=(const event_listener_scope<EventType>& src)
{
m_impl = std::make_unique<event_listener_scope_guard_impl<EventType>>(src.m_listeners, src.m_index);
return *this;
}

template <typename EventType>
event_listener_scope_guard& operator=(event_listener_scope<EventType>&& src)
{
m_impl = std::make_unique<event_listener_scope_guard_impl<EventType>>(std::move(src.m_listeners), src.m_index);
return *this;
}
};

template <typename EventType>
class event_listener_list final : public entity
{
std::shared_ptr<std::vector<event_listener_callback_container<EventType>>> m_listeners;

public:
event_listener_list()
{
this->debug_count("event_listener_list::ctor()");
}
auto add(event_listener<EventType> callback) -> int

[[nodiscard]] auto add(event_listener<EventType> callback) -> event_listener_scope<EventType>
{
if (m_listeners == nullptr)
{
this->debug_count("event_listener_list::add()[first time]");
m_listeners = std::make_unique<std::vector<event_listener_container>>();
m_listeners = std::make_shared<std::vector<event_listener_callback_container<EventType>>>();
}

m_listeners->emplace_back(callback, true);

return static_cast<int>(m_listeners->size()) - 1;
return { m_listeners, static_cast<int>(m_listeners->size()) - 1 };
}

void notify(EventType& e)
Expand Down
33 changes: 20 additions & 13 deletions code/classeine-lib/clsn/core/property.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ namespace clsn::core
/// @return An identifier for the added listener.
template <bool B = HasValueChangedEventListener,
std::enable_if_t<B, int> = 0>
auto add_value_changed_listener(
event_listener<value_changed_event<T>> listener) -> int
[[nodiscard]] auto add_value_changed_listener(
event_listener<value_changed_event<T>> listener) ->
event_listener_scope<value_changed_event<T>>
{
return m_value_changed_listeners.add(std::move(listener));
}
Expand Down Expand Up @@ -125,9 +126,10 @@ namespace clsn::core
public: \
void set_##name(const type& value) noexcept { m_##name.set(value); } \
auto get_##name() const noexcept -> const type& { return m_##name.get(); } \
auto add_##name##_changed_listener( \
[[nodiscard]] auto add_##name##_changed_listener( \
const clsn::core::event_listener<clsn::core::value_changed_event<type>>& \
listener) -> int \
listener) -> clsn::core::event_listener_scope< \
clsn::core::value_changed_event<type>> \
{ \
return m_##name.add_value_changed_listener(listener); \
}
Expand All @@ -153,9 +155,10 @@ private:
public: \
void set_##name(bool value) noexcept { m_##name.set(value); } \
auto is_##name() const noexcept -> bool { return m_##name.get(); } \
auto add_##name##_changed_listener( \
[[nodiscard]] auto add_##name##_changed_listener( \
const clsn::core::event_listener<clsn::core::value_changed_event<bool>>& \
listener) -> int \
listener) -> clsn::core::event_listener_scope< \
clsn::core::value_changed_event<bool>> \
{ \
return m_##name.add_value_changed_listener(listener); \
}
Expand All @@ -181,9 +184,10 @@ private:
public: \
void set_##name(const type& value) noexcept; \
auto get_##name() const noexcept -> const type&; \
auto add_##name##_changed_listener( \
const clsn::core::event_listener<clsn::core::value_changed_event<type>>& \
listener) -> int;
[[nodiscard]] auto add_##name##_changed_listener( \
const clsn::core::event_listener<clsn::core::value_changed_event<type>>& \
listener) -> clsn::core::event_listener_scope< \
clsn::core::value_changed_event<type>>;

/// @def CLSN_HEADER_PROPERTY
/// @brief Macro to declare a property in the header file.
Expand All @@ -206,9 +210,10 @@ private:
public: \
void set_##name(bool value) noexcept; \
auto is_##name() const noexcept -> bool; \
auto add_##name##_changed_listener( \
[[nodiscard]] auto add_##name##_changed_listener( \
const clsn::core::event_listener<clsn::core::value_changed_event<bool>>& \
listener) -> int;
listener) -> clsn::core::event_listener_scope< \
clsn::core::value_changed_event<bool>>;

/// @def CLSN_HEADER_BOOL_PROPERTY
/// @brief Macro to declare a boolean property in the header file.
Expand All @@ -232,7 +237,8 @@ void className::set_##name(const type& value) noexcept { m_##name.set(value); }
auto className::get_##name() const noexcept -> const type& { return m_##name.get(); } \
auto className::add_##name##_changed_listener( \
const clsn::core::event_listener<clsn::core::value_changed_event<type>>& \
listener) -> int \
listener) -> clsn::core::event_listener_scope< \
clsn::core::value_changed_event<type>> \
{ \
return m_##name.add_value_changed_listener(listener); \
}
Expand All @@ -244,7 +250,8 @@ void className::set_##name(bool value) noexcept { m_##name.set(value); }
auto className::is_##name() const noexcept -> bool { return m_##name.get(); } \
auto className::add_##name##_changed_listener( \
const clsn::core::event_listener<clsn::core::value_changed_event<bool>>& \
listener) -> int \
listener) -> clsn::core::event_listener_scope< \
clsn::core::value_changed_event<bool>> \
{ \
return m_##name.add_value_changed_listener(listener); \
}
2 changes: 1 addition & 1 deletion code/classeine-lib/clsn/ui/captionable_control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace clsn::ui
invalidate();
};

add_caption_changed_listener(proc);
std::ignore = add_caption_changed_listener(proc);
}

auto captionable_control::to_string() const -> std::string
Expand Down
5 changes: 3 additions & 2 deletions code/classeine-lib/clsn/ui/clickable_control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace clsn::ui
void clickable_control::add_action_listener(
core::event_listener<events::action_event> event)
{
m_action_listeners.add(std::move(event));
std::ignore = m_action_listeners.add(std::move(event));
}

void clickable_control::notify_action_event(events::action_event& e)
Expand All @@ -41,7 +41,8 @@ namespace clsn::ui

void clickable_control::init_clickable_control_events()
{
add_caption_changed_listener([this](auto&) { invalidate(); });
std::ignore = add_caption_changed_listener(
[this](auto&) { invalidate(); });
}

void clickable_control::perform_click()
Expand Down
9 changes: 5 additions & 4 deletions code/classeine-lib/clsn/ui/container.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include "ui_theme.h"

#include <clsn/ui/paintable_control.h>
#include <clsn/ui/ui_manager.h>

#include <algorithm>
#include <ranges>
Expand Down Expand Up @@ -262,7 +261,8 @@ namespace clsn::ui
{
control.set_parent_control(*this);

control.add_visible_changed_listener(
// TODO(ETO): Add a scope guard here
std::ignore = control.add_visible_changed_listener(
[this](auto&)
{
do_layout();
Expand Down Expand Up @@ -307,7 +307,7 @@ namespace clsn::ui
private:
void init_container_events()
{
add_enabled_changed_listener(
std::ignore = add_enabled_changed_listener(
[this](auto& e)
{
for (auto& c : get_controls())
Expand All @@ -316,7 +316,8 @@ namespace clsn::ui
}
});

add_actual_size_changed_listener([this](auto&) { do_layout(); });
std::ignore = add_actual_size_changed_listener(
[this](auto&) { do_layout(); });
}

auto get_theme_background_color() const -> draw::color override
Expand Down
2 changes: 1 addition & 1 deletion code/classeine-lib/clsn/ui/content_pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace clsn::ui

void content_pane::init_content_pane_events()
{
add_actual_size_changed_listener([this](auto& )
std::ignore = add_actual_size_changed_listener([this](auto& )
{
do_layout();
});
Expand Down
16 changes: 10 additions & 6 deletions code/classeine-lib/clsn/ui/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ namespace clsn::ui
process_mouse_click_event(e);
}

void control::add_mouse_moved_listener(core::event_listener<events::mouse_moved_event> event)
core::event_listener_scope<events::mouse_moved_event>
control::add_mouse_moved_listener(
core::event_listener<events::mouse_moved_event> event)
{
m_mouse_moved_listeners.add(std::move(event));
return m_mouse_moved_listeners.add(std::move(event));
}

void control::notify_mouse_moved_event(events::mouse_moved_event& e)
Expand Down Expand Up @@ -103,9 +105,11 @@ namespace clsn::ui
m_mouse_moved_listeners.notify(e);
}

void control::add_mouse_click_listener(core::event_listener<events::mouse_click_event> event)
core::event_listener_scope<events::mouse_click_event>
control::add_mouse_click_listener(
core::event_listener<events::mouse_click_event> event)
{
m_mouse_click_listeners.add(std::move(event));
return m_mouse_click_listeners.add(std::move(event));
}

void control::set_renderer(std::unique_ptr<renderer_base>&& renderer) const
Expand Down Expand Up @@ -158,8 +162,8 @@ namespace clsn::ui
{
auto invalidate_proc = [this](auto&) { invalidate(); };

add_visible_changed_listener(invalidate_proc);
add_enabled_changed_listener(invalidate_proc);
std::ignore = add_visible_changed_listener(invalidate_proc);
std::ignore = add_enabled_changed_listener(invalidate_proc);
}

auto control::contains_point(const draw::point& point) const -> bool
Expand Down
Loading

0 comments on commit 0f645eb

Please sign in to comment.