Loading...
Searching...
No Matches
Component.h
Go to the documentation of this file.
1
2// Copyright (c) 2025 Dave R. Smith.
3// Malena Framework — Licensed under PolyForm Noncommercial 1.0.0; commercial use requires a paid license. See LICENSE.
4
5#ifndef MALENA_COMPONENT_H
6#define MALENA_COMPONENT_H
7
10#include <Malena/Core/Core.h>
18#include <type_traits>
20
21namespace ml
22{
23 // =========================================================================
24 // Manifest collision detection helpers
25 // =========================================================================
26
28
33 template<typename... Ts>
34 struct ImageCount
35 {
36 static constexpr int value = (has_Image<Ts>::value + ... + 0);
37 };
38
42 template<typename... Ts>
43 struct FontCount
44 {
45 static constexpr int value = (has_Font<Ts>::value + ... + 0);
46 };
47
51 template<typename... Ts>
52 struct SoundCount
53 {
54 static constexpr int value = (has_Sound<Ts>::value + ... + 0);
55 };
56
60 template<typename T, typename = void>
61 struct has_Text : std::false_type {};
62 template<typename T>
63 struct has_Text<T, std::void_t<typename T::Text>> : std::true_type {};
64
65 template<typename... Ts>
66 struct TextCount
67 {
68 static constexpr int value = (has_Text<Ts>::value + ... + 0);
69 };
70
75 template<typename T, typename = void>
76 struct extract_manifest_or_void { using type = void; };
77
78 template<typename T>
79 struct extract_manifest_or_void<T, std::void_t<typename T::manifest_type>>
80 { using type = typename T::manifest_type; };
81
83
84 // =========================================================================
85 // HasCoreTraits
86 // =========================================================================
87
89 template<typename... Traits>
90 struct HasCoreTraits : std::disjunction<
91 std::is_same<Traits, Subscribable>...,
92 std::is_same<Traits, Flaggable>...,
93 std::is_same<Traits, Positionable>...
94 > {};
96
97 // =========================================================================
98 // ComponentCore
99 // =========================================================================
100
106 template<typename ComponentManifest = void, typename... Traits>
107 struct ComponentCore : public Core,
108 public Draggable,
109 public Traits...,
110 public GatherFlags <ComponentManifest, Draggable, Traits...>::type,
111 public GatherStates<ComponentManifest, Draggable, Traits...>::type
112 {
113 static_assert(
114 !HasCoreTraits<Traits...>::value,
115 "[Malena] Subscribable, Flaggable, and Positionable are already "
116 "included via Core — do not pass them as traits."
117 );
118
120 {
121 // These subscriptions happen while THIS object is still being
122 // constructed, so nothing here may use RTTI on it. static_cast to
123 // Core is valid (Core is an unambiguous base already constructed);
124 // dynamic_cast would be undefined and hard-fails on MSVC.
125 Core* self = static_cast<Core*>(this);
126 EventManager::subscribe(ml::Event::DRAG, static_cast<Draggable*>(this), self);
127 Fireable::addCallback(ml::Event::CLICK, static_cast<Clickable*>(this),
128 [](const std::optional<sf::Event>&){}, true, self);
129 Fireable::addCallback(ml::Event::HOVER, static_cast<Hoverable*>(this),
130 [](const std::optional<sf::Event>&){}, true, self);
131 }
132
133 // ── System flags (ml::Flag) ──────────────────────────────────────────
139
140 // ── Custom flags from manifest + traits ──────────────────────────────
141 using GatherFlags<ComponentManifest, Draggable, Traits...>::type::enableFlag;
142 using GatherFlags<ComponentManifest, Draggable, Traits...>::type::disableFlag;
143 using GatherFlags<ComponentManifest, Draggable, Traits...>::type::checkFlag;
144 using GatherFlags<ComponentManifest, Draggable, Traits...>::type::setFlag;
145 using GatherFlags<ComponentManifest, Draggable, Traits...>::type::toggleFlag;
146
147 // ── Custom states from manifest + traits ─────────────────────────────
148 using GatherStates<ComponentManifest, Draggable, Traits...>::type::setState;
149 using GatherStates<ComponentManifest, Draggable, Traits...>::type::getState;
150 using GatherStates<ComponentManifest, Draggable, Traits...>::type::isState;
151 using GatherStates<ComponentManifest, Draggable, Traits...>::type::onStateEnter;
152 using GatherStates<ComponentManifest, Draggable, Traits...>::type::onStateExit;
153 using GatherStates<ComponentManifest, Draggable, Traits...>::type::syncState;
154 };
155
156 // =========================================================================
157 // ComponentBase — with manifest
158 // =========================================================================
159
174 template<typename ComponentManifest, typename... Traits>
176 public ComponentCore<ComponentManifest, Traits...>,
177 public ManifestResources<ComponentManifest>
178 {
179 private:
180 // Collect all manifest types from traits for collision checking
181 // (component manifest + each trait's manifest_type if it has one)
182 using _AllManifests = std::tuple<
183 ComponentManifest,
184 typename extract_manifest_or_void<Traits>::type...
185 >;
186
187 // ── Images collision check ────────────────────────────────────────────
188 static_assert(
189 ImageCount<
190 ComponentManifest,
191 typename extract_manifest_or_void<Traits>::type...
192 >::value <= 1,
193 "[Malena] Images alias is ambiguous — two or more manifests in "
194 "this component's inheritance chain both declare an Images enum. "
195 "Use MyManifest::Images::VALUE syntax to qualify explicitly."
196 );
197
198 // ── Fonts collision check ─────────────────────────────────────────────
199 static_assert(
200 FontCount<
201 ComponentManifest,
202 typename extract_manifest_or_void<Traits>::type...
203 >::value <= 1,
204 "[Malena] Fonts alias is ambiguous — two or more manifests in "
205 "this component's inheritance chain both declare a Fonts enum. "
206 "Use MyManifest::Fonts::VALUE syntax to qualify explicitly."
207 );
208
209 // ── Sounds collision check ────────────────────────────────────────────
210 static_assert(
211 SoundCount<
212 ComponentManifest,
213 typename extract_manifest_or_void<Traits>::type...
214 >::value <= 1,
215 "[Malena] Sounds alias is ambiguous — two or more manifests in "
216 "this component's inheritance chain both declare a Sounds enum. "
217 "Use MyManifest::Sounds::VALUE syntax to qualify explicitly."
218 );
219
220 // ── Text collision check ──────────────────────────────────────────────
221 static_assert(
222 TextCount<
223 ComponentManifest,
224 typename extract_manifest_or_void<Traits>::type...
225 >::value <= 1,
226 "[Malena] Text alias is ambiguous — two or more manifests in "
227 "this component's inheritance chain both declare a Text enum. "
228 "Use MyManifest::Text::VALUE syntax to qualify explicitly."
229 );
230
231 public:
233
241 void draw(sf::RenderTarget& target, sf::RenderStates states) const override
242 {
243 this->drawChildren(target, states);
244 }
245 };
246
247 // =========================================================================
248 // ComponentBase — no manifest
249 // =========================================================================
250
252 template<typename... Traits>
253 struct ComponentBase<void, Traits...> : public sf::Drawable,
254 public ComponentCore<void, Traits...>
255 {
256 void draw(sf::RenderTarget& target, sf::RenderStates states) const override
257 {
258 this->drawChildren(target, states);
259 }
260 };
262
263 // =========================================================================
264 // Component
265 // =========================================================================
266
294 template<typename First = void, typename... Rest>
295 class Component : public std::conditional_t<
296 std::is_base_of_v<Manifest, First>,
297 ComponentBase<First, Rest...>,
298 ComponentBase<void, First, Rest...>
299 > {};
300
302 template<>
303 class Component<void> : public ComponentBase<void> {};
305
306 // =========================================================================
307 // ComponentWith alias
308 // =========================================================================
309
322 template<typename M, typename... Traits>
323 using ComponentWith = Component<M, Traits...>;
324
325} // namespace ml
326
327#endif // MALENA_COMPONENT_H
Primary base class for all user-facing Malena components.
Definition Component.h:299
void drawChildren(sf::RenderTarget &target, sf::RenderStates states) const
Receiver trait that adds mouse-drag behavior to any Component.
Definition Draggable.h:71
Draggable()=default
static void subscribe(EnumType eventEnum, EventReceiver *component, Core *core=nullptr)
Register a component for an enum-keyed event.
Enum-keyed boolean flag store.
Definition FlagManager.h:60
void toggleFlag(State state)
void disableFlag(State state)
void setFlag(State state, bool status)
bool checkFlag(State state) const
void enableFlag(State state)
Trait that adds mouse-hover and mouse-leave callbacks to any Core object.
Definition Hoverable.h:47
Component< M, Traits... > ComponentWith
Alias for Component<M, Traits...>.
Definition Component.h:323
@ CLICK
Mouse button released over component.
Definition Event.h:39
@ HOVER
Mouse entered component bounds.
Definition Event.h:40
@ DRAG
Component is being dragged.
Definition Event.h:45
Flag
System-level boolean flags available on every ml::Core object.
Definition Flag.h:65
Definition Animate.h:25
Intermediate drawable layer — with manifest.
Definition Component.h:178
void draw(sf::RenderTarget &target, sf::RenderStates states) const override
Definition Component.h:241
ml::ManifestResources< ComponentManifest > Resources
Definition Component.h:232
Collects all Flag enums from a component manifest and its traits.
Collects all State enums from a component manifest and its traits.
Unified manifest resource and config accessor.