Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Attributes and Add Property Support #77

Merged
merged 5 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 236 additions & 11 deletions nui/include/nui/frontend/attributes/impl/attribute_factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <nui/frontend/event_system/event_context.hpp>
#include <nui/frontend/event_system/observed_value_combinator.hpp>
#include <nui/utility/fixed_string.hpp>
#include <nui/frontend/property.hpp>

#include <nui/frontend/val.hpp>

Expand All @@ -14,14 +15,16 @@ namespace Nui::Attributes
namespace Detail
{
template <typename ElementT, typename T>
EventContext::EventIdType defaultSetEvent(std::weak_ptr<ElementT> element, T const& obs, char const* name)
EventContext::EventIdType changeEventHandler(
std::weak_ptr<ElementT> element,
T const& obs,
std::function<bool(std::shared_ptr<ElementT> const&)> onLock)
{
const auto eventId = globalEventContext.registerEvent(Event{
[element, obs, name](auto eventId) {
[element, obs, onLock = std::move(onLock)](auto eventId) {
if (auto shared = element.lock(); shared)
{
shared->setAttribute(name, obs.value());
return true;
return onLock(shared);
}
obs.unattachEvent(eventId);
return false;
Expand All @@ -32,8 +35,145 @@ namespace Nui::Attributes
obs.attachEvent(eventId);
return eventId;
}

template <typename ElementT, typename T>
EventContext::EventIdType defaultAttributeEvent(std::weak_ptr<ElementT> element, T const& obs, char const* name)
{
return changeEventHandler(
element,
obs,
std::function<bool(std::shared_ptr<ElementT> const&)>{
[name, obs](std::shared_ptr<ElementT> const& shared) {
shared->setAttribute(name, obs.value());
return true;
}});
}

template <typename ElementT, typename T>
EventContext::EventIdType defaultPropertyEvent(std::weak_ptr<ElementT> element, T const& obs, char const* name)
{
return changeEventHandler(
element,
obs,
std::function<bool(std::shared_ptr<ElementT> const&)>{
[name, obs](std::shared_ptr<ElementT> const& shared) {
shared->setProperty(name, obs.value());
return true;
}});
}
}

class PropertyFactory
{
public:
explicit constexpr PropertyFactory(char const* name)
: name_{name}
{}

// Dont use this class like a value
PropertyFactory(PropertyFactory const&) = delete;
PropertyFactory(PropertyFactory&&) = delete;
PropertyFactory& operator=(PropertyFactory const&) = delete;
PropertyFactory& operator=(PropertyFactory&&) = delete;

constexpr char const* name() const
{
return name_;
};

template <typename U>
requires(
!IsObserved<std::decay_t<U>> && !std::invocable<U, Nui::val> && !std::invocable<U> &&
!Nui::Detail::IsProperty<std::decay_t<U>>)
Attribute operator=(U val) const
{
return Attribute{
[name = name(), val = std::move(val)](Dom::ChildlessElement& element) {
element.setProperty(name, val);
},
};
}

template <typename U>
requires(IsObserved<std::decay_t<U>>)
Attribute operator=(U& val) const
{
return Attribute{
[name = name(), &val](Dom::ChildlessElement& element) {
element.setProperty(name, val.value());
},
[name = name(), &val](std::weak_ptr<Dom::ChildlessElement>&& element) {
return Detail::defaultPropertyEvent(
std::move(element), Nui::Detail::CopiableObservedWrap{val}, name);
},
[&val](EventContext::EventIdType const& id) {
val.unattachEvent(id);
},
};
}

template <typename RendererType, typename... ObservedValues>
Attribute
operator=(ObservedValueCombinatorWithPropertyGenerator<RendererType, ObservedValues...> const& combinator) const
{
return Attribute{
[name = name(), combinator](Dom::ChildlessElement& element) {
element.setProperty(name, combinator.value());
},
[name = name(), combinator](std::weak_ptr<Dom::ChildlessElement>&& element) {
return Detail::defaultPropertyEvent(std::move(element), combinator, name);
},
[combinator](EventContext::EventIdType const& id) {
combinator.unattachEvent(id);
},
};
}

template <typename RendererType, typename... ObservedValues>
Attribute
operator=(ObservedValueCombinatorWithGenerator<RendererType, ObservedValues...> const& combinator) const
{
return Attribute{
[name = name(), combinator](Dom::ChildlessElement& element) {
element.setProperty(name, combinator.value());
},
[name = name(), combinator](std::weak_ptr<Dom::ChildlessElement>&& element) {
return Detail::defaultPropertyEvent(std::move(element), combinator, name);
},
[combinator](EventContext::EventIdType const& id) {
combinator.unattachEvent(id);
},
};
}

Attribute operator=(std::function<void(Nui::val)> func) const
{
return Attribute{
[name = name(), func = std::move(func)](Dom::ChildlessElement& element) {
element.setProperty(name, [func](Nui::val val) {
func(val);
globalEventContext.executeActiveEventsImmediately();
});
},
};
}

Attribute operator=(std::function<void()> func) const
{
return Attribute{
[name = name(), func = std::move(func)](Dom::ChildlessElement& element) {
element.setProperty(name, [func](Nui::val) {
func();
globalEventContext.executeActiveEventsImmediately();
});
},
};
}

private:
char const* name_;
};

class AttributeFactory
{
public:
Expand All @@ -42,18 +182,20 @@ namespace Nui::Attributes
{}

// Dont use this class like a value
AttributeFactory(Attribute const&) = delete;
AttributeFactory(Attribute&&) = delete;
AttributeFactory& operator=(Attribute const&) = delete;
AttributeFactory& operator=(Attribute&&) = delete;
AttributeFactory(AttributeFactory const&) = delete;
AttributeFactory(AttributeFactory&&) = delete;
AttributeFactory& operator=(AttributeFactory const&) = delete;
AttributeFactory& operator=(AttributeFactory&&) = delete;

constexpr char const* name() const
{
return name_;
};

template <typename U>
requires(!IsObserved<std::decay_t<U>> && !std::invocable<U, Nui::val> && !std::invocable<U>)
requires(
!IsObserved<std::decay_t<U>> && !std::invocable<U, Nui::val> && !std::invocable<U> &&
!Nui::Detail::IsProperty<std::decay_t<U>>)
Attribute operator=(U val) const
{
return Attribute{
Expand All @@ -62,6 +204,7 @@ namespace Nui::Attributes
},
};
}

template <typename U>
requires(IsObserved<std::decay_t<U>>)
Attribute operator=(U& val) const
Expand All @@ -71,13 +214,59 @@ namespace Nui::Attributes
element.setAttribute(name, val.value());
},
[name = name(), &val](std::weak_ptr<Dom::ChildlessElement>&& element) {
return Detail::defaultSetEvent(std::move(element), Nui::Detail::CopiableObservedWrap{val}, name);
return Detail::defaultAttributeEvent(
std::move(element), Nui::Detail::CopiableObservedWrap{val}, name);
},
[&val](EventContext::EventIdType const& id) {
val.unattachEvent(id);
},
};
}

template <typename U>
requires(IsObserved<std::decay_t<U>>)
Attribute operator=(Nui::Detail::Property<U> const& prop) const
{
return Attribute{
[name = name(), p = prop.prop](Dom::ChildlessElement& element) {
element.setProperty(name, p->value());
},
[name = name(), p = prop.prop](std::weak_ptr<Dom::ChildlessElement>&& element) {
return Detail::defaultPropertyEvent(
std::move(element), Nui::Detail::CopiableObservedWrap{*p}, name);
},
[p = prop.prop](EventContext::EventIdType const& id) {
p->unattachEvent(id);
},
};
}

template <typename U>
requires(!IsObserved<std::decay_t<U>> && !std::invocable<U, Nui::val> && !std::invocable<U>)
Attribute operator=(Nui::Detail::Property<U> const& prop) const
{
return Attribute{[name = name(), p = std::move(prop.prop)](Dom::ChildlessElement& element) {
element.setProperty(name, p);
}};
}

template <typename RendererType, typename... ObservedValues>
Attribute
operator=(ObservedValueCombinatorWithPropertyGenerator<RendererType, ObservedValues...> const& combinator) const
{
return Attribute{
[name = name(), combinator](Dom::ChildlessElement& element) {
element.setProperty(name, combinator.value());
},
[name = name(), combinator](std::weak_ptr<Dom::ChildlessElement>&& element) {
return Detail::defaultPropertyEvent(std::move(element), combinator, name);
},
[combinator](EventContext::EventIdType const& id) {
combinator.unattachEvent(id);
},
};
}

template <typename RendererType, typename... ObservedValues>
Attribute
operator=(ObservedValueCombinatorWithGenerator<RendererType, ObservedValues...> const& combinator) const
Expand All @@ -87,7 +276,7 @@ namespace Nui::Attributes
element.setAttribute(name, combinator.value());
},
[name = name(), combinator](std::weak_ptr<Dom::ChildlessElement>&& element) {
return Detail::defaultSetEvent(std::move(element), combinator, name);
return Detail::defaultAttributeEvent(std::move(element), combinator, name);
},
[combinator](EventContext::EventIdType const& id) {
combinator.unattachEvent(id);
Expand Down Expand Up @@ -119,9 +308,45 @@ namespace Nui::Attributes
};
}

Attribute operator=(Nui::Detail::Property<std::function<void(Nui::val)>> func) const
{
return Attribute{
[name = name(), func = std::move(func.prop)](Dom::ChildlessElement& element) {
element.setProperty(name, [func](Nui::val val) {
func(val);
globalEventContext.executeActiveEventsImmediately();
});
},
};
}

Attribute operator=(Nui::Detail::Property<std::function<void()>> func) const
{
return Attribute{
[name = name(), func = std::move(func.prop)](Dom::ChildlessElement& element) {
element.setProperty(name, [func](Nui::val) {
func();
globalEventContext.executeActiveEventsImmediately();
});
},
};
}

private:
char const* name_;
};

inline namespace Literals
{
constexpr AttributeFactory operator""_attr(char const* name, std::size_t)
{
return AttributeFactory{name};
}
constexpr PropertyFactory operator""_prop(char const* name, std::size_t)
{
return PropertyFactory{name};
}
}
}

#define MAKE_HTML_VALUE_ATTRIBUTE_RENAME(NAME, HTML_NAME) \
Expand Down
Loading