TGUI  1.0-beta
Loading...
Searching...
No Matches
Components.hpp
1
2//
3// TGUI - Texus' Graphical User Interface
4// Copyright (C) 2012-2022 Bruno Van de Velde (vdv_b@tgui.eu)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
25
26#ifndef TGUI_COMPONENTS_HPP
27#define TGUI_COMPONENTS_HPP
28
29#include <TGUI/Text.hpp>
30#include <TGUI/Sprite.hpp>
31#include <TGUI/Texture.hpp>
32#include <TGUI/Outline.hpp>
33
34#include <unordered_map>
35#include <memory>
36#include <set>
37
39
40namespace tgui
41{
42 class BackendRenderTarget;
43
44namespace priv
45{
46namespace dev
47{
49
51 enum class ComponentState : std::uint8_t
52 {
53 Normal = 0,
54 Hover = 1,
55 Active = 2,
56 ActiveHover = 3,
57 Focused = 4,
58 FocusedHover = 5,
59 FocusedActive = 6,
60 FocusedActiveHover = 7,
61 Disabled = 8,
62 DisabledActive = 10
63 };
64
66
68 enum class AlignLayout
69 {
70 None,
71 Top,
72 Left,
73 Right,
74 Bottom,
75 Leftmost,
76 Rightmost,
77 Fill
78 };
79
81
83 enum class PositionAlignment
84 {
85 None,
86 TopLeft,
87 Top,
88 TopRight,
89 Right,
90 BottomRight,
91 Bottom,
92 BottomLeft,
93 Left,
94 Center
95 };
96
98
99 class TGUI_API MessageBroker
100 {
101 public:
102
103 static std::uint64_t createTopic();
104
105 static void destroyTopic(std::uint64_t topicId);
106
107 static std::uint64_t subscribe(std::uint64_t topicId, std::function<void()> func);
108
109 static void unsubscribe(std::uint64_t callbackId);
110
111 static void sendEvent(std::uint64_t topicId);
112
113 private:
114 static std::unordered_map<std::uint64_t, std::set<std::uint64_t>> m_topicIdToCallbackIds;
115 static std::unordered_map<std::uint64_t, std::uint64_t> m_callbackIdToTopicId;
116 static std::unordered_map<std::uint64_t, std::function<void()>> m_listeners; // CallbackId -> Func
117
118 // All topic and callback ids are unique and non-overlapping
119 static std::uint64_t m_lastId;
120 };
121
123
124 class TGUI_API StylePropertyBase
125 {
126 public:
127 virtual ~StylePropertyBase() = default;
128 };
129
131
132 template <typename ValueType>
133 class TGUI_API StyleProperty : public StylePropertyBase
134 {
135 public:
136
137 StyleProperty() :
138 m_defaultValue {},
139 m_messageTopicId{MessageBroker::createTopic()}
140 {
141 }
142
143 explicit StyleProperty(ValueType defaultValue) :
144 m_defaultValue {std::move(defaultValue)},
145 m_messageTopicId{MessageBroker::createTopic()}
146 {
147 }
148
149 StyleProperty(const StyleProperty& other) :
150 m_defaultValue {other.m_defaultValue},
151 m_messageTopicId{MessageBroker::createTopic()},
152 m_globalValues {other.m_globalValues}
153 {
154 unsetValue();
155
156 const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
157 const std::uint64_t oldBaseIndex = other.m_propertyData & 0xFFFFFFFFFFFF0000;
158 const std::uint16_t oldStoredStates = static_cast<std::uint16_t>(other.m_propertyData & 0xFFFF);
159
160 std::uint16_t total = 0;
161 std::uint8_t bitIndex = 0;
162 while (total < oldStoredStates)
163 {
164 if (oldStoredStates & (1 << bitIndex))
165 {
166 m_globalValues[baseIndex + bitIndex] = m_globalValues[oldBaseIndex + bitIndex];
167 total += static_cast<std::uint16_t>(1 << bitIndex);
168 }
169 ++bitIndex;
170 }
171
172 m_propertyData = baseIndex | oldStoredStates;
173 }
174
175 StyleProperty(StyleProperty&& other) noexcept :
176 m_defaultValue {std::move(other.m_defaultValue)},
177 m_propertyData {std::move(other.m_propertyData)},
178 m_messageTopicId{std::move(other.m_messageTopicId)},
179 m_globalValues {std::move(other.m_globalValues)}
180 {
181 other.m_messageTopicId = 0;
182 }
183
184 ~StyleProperty() override
185 {
186 if (m_messageTopicId) // Can be 0 on moved object
187 MessageBroker::destroyTopic(m_messageTopicId);
188 unsetValueImpl();
189 }
190
191 StyleProperty& operator=(const StyleProperty& other)
192 {
193 if (&other != this)
194 {
195 StyleProperty temp(other);
196 std::swap(m_defaultValue, temp.m_defaultValue);
197 std::swap(m_propertyData, temp.m_propertyData);
198 std::swap(m_messageTopicId, temp.m_messageTopicId);
199 std::swap(m_globalValues, temp.m_globalValues);
200 }
201
202 return *this;
203 }
204
205 StyleProperty& operator=(StyleProperty&& other) noexcept
206 {
207 if (&other != this)
208 {
209 m_defaultValue = std::move(other.m_defaultValue);
210 m_propertyData = std::move(other.m_propertyData);
211 m_messageTopicId = std::move(other.m_messageTopicId);
212 m_globalValues = std::move(other.m_globalValues);
213
214 other.m_messageTopicId = 0;
215 }
216
217 return *this;
218 }
219
220 StyleProperty& operator=(const ValueType& value)
221 {
222 unsetValueImpl();
223 setValue(value, ComponentState::Normal);
224 return *this;
225 }
226
227 void setValue(const ValueType& value, ComponentState state = ComponentState::Normal)
228 {
229 const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
230 m_propertyData |= static_cast<std::uint64_t>(1) << static_cast<std::uint8_t>(state);
231 m_globalValues[baseIndex + static_cast<std::uint8_t>(state)] = value;
232
233 MessageBroker::sendEvent(m_messageTopicId);
234 }
235
236 void unsetValue(ComponentState state)
237 {
238 const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
239 m_propertyData &= ~(static_cast<std::uint64_t>(1) << static_cast<std::uint8_t>(state));
240 m_globalValues.erase(baseIndex + static_cast<std::uint8_t>(state));
241
242 MessageBroker::sendEvent(m_messageTopicId);
243 }
244
245 void unsetValue()
246 {
247 unsetValueImpl();
248 MessageBroker::sendEvent(m_messageTopicId);
249 }
250
251 const ValueType& getValue(ComponentState state = ComponentState::Normal) const
252 {
253 const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
254 const std::uint16_t storedStates = static_cast<std::uint16_t>(m_propertyData & 0xFFFF);
255
256 // If we don't have a value for any state then we can just return the default value
257 if (storedStates == 0)
258 return m_defaultValue;
259
260 // If we only have a value for the Normal state then always use this value
261 if (storedStates == 1)
262 return m_globalValues.at(baseIndex);
263
264 if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Disabled))
265 {
266 if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Active)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::DisabledActive))))
267 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::DisabledActive));
268 if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Disabled)))
269 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Disabled));
270 }
271
272 if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Active))
273 {
274 if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Hover))
275 {
276 if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Focused)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::FocusedActiveHover))))
277 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::FocusedActiveHover));
278 if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::ActiveHover)))
279 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::ActiveHover));
280 }
281
282 if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Focused)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::FocusedActive))))
283 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::FocusedActive));
284 if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Active)))
285 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Active));
286 }
287
288 if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Hover))
289 {
290 if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Focused)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::FocusedHover))))
291 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::FocusedHover));
292 if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Hover)))
293 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Hover));
294 }
295
296 if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Focused))
297 {
298 if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Focused)))
299 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Focused));
300 }
301
302 if (storedStates & 1)
303 {
304 // We have a value for the Normal state, so return it. It is possible to pass here while storedStates != 1 when there
305 // is e.g. a value for both Normal and Disabled state and the widget is enabled.
306 return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Normal));
307 }
308 else
309 {
310 // We don't have any relevant values, so return the default value. It is possible to pass here while storedStates > 0 when
311 // there is e.g. only a value for the Disabled state and the widget is enabled.
312 return m_defaultValue;
313 }
314 }
315
316 std::uint64_t connectCallback(std::function<void()> func)
317 {
318 return MessageBroker::subscribe(m_messageTopicId, std::move(func));
319 }
320
321 void disconnectCallback(std::uint64_t id)
322 {
323 return MessageBroker::unsubscribe(id);
324 }
325
326 private:
327
328 void unsetValueImpl()
329 {
330 const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
331 const std::uint16_t storedStates = static_cast<std::uint16_t>(m_propertyData & 0xFFFF);
332
333 std::uint16_t total = 0;
334 std::uint8_t bitIndex = 0;
335 while (total < storedStates)
336 {
337 if (storedStates & (1 << bitIndex))
338 {
339 m_globalValues.erase(baseIndex + bitIndex);
340 total += static_cast<std::uint16_t>(1 << bitIndex);
341 }
342 ++bitIndex;
343 }
344
345 m_propertyData = baseIndex; // Forget about the states that were stored
346 }
347
348 private:
349
350 ValueType m_defaultValue;
351
352 // The highest 48 bits store the index in a static map of values (see below).
353 // The next 16 bits are used to indicate the set of states that are present for this property.
354 std::uint64_t m_propertyData = 0;
355
356 // Index of the event that we publish to when the property changes.
357 std::uint64_t m_messageTopicId = 0;
358
359 // All values are stored in a static map, which can be seen as a very large sparse list.
360 // Each instance holds a unique 48-bit id that can be seen as the high bits of the index in the list.
361 // The lowest 4 bits of the index contain the widget state. The remaining 12 bits inbetween are always 0.
364
367 std::unordered_map<std::uint64_t, ValueType> m_globalValues;
368 };
369
371
372 struct TGUI_API StylePropertyBackground
373 {
374 StyleProperty<Color> borderColor{Color::Black};
375 StyleProperty<Color> color{Color::White};
376 StyleProperty<Texture> texture;
377 StyleProperty<Outline> borders;
378 StyleProperty<Outline> padding;
379
380 float roundedBorderRadius = 0;
381 };
382
384
385 struct TGUI_API StylePropertyText
386 {
387 StyleProperty<Color> color{Color::Black};
388 StyleProperty<TextStyles> style;
389 };
390
392
393 class GroupComponent;
394
395 class TGUI_API Component
396 {
397 public:
398
399 Component() = default;
400 virtual ~Component() = default;
401
402 Component(const Component&);
403 Component& operator=(const Component&);
404
405 Component(Component&&) = default;
406 Component& operator=(Component&&) = default;
407
408 void setPosition(Vector2f position);
409
410 Vector2f getPosition() const;
411
412 Vector2f getSize() const;
413
414 void setPositionAlignment(PositionAlignment alignment);
415
416 void setVisible(bool visible);
417
418 bool isVisible() const;
419
420 void setParent(GroupComponent* parent);
421
422 virtual void draw(BackendRenderTarget& target, RenderStates states) const = 0;
423
424 virtual void updateLayout();
425
426 virtual std::shared_ptr<Component> clone() const = 0;
427
428 protected:
429
430 friend void swap(Component& first, Component& second);
431
432 protected:
433
434 ComponentState m_state = ComponentState::Normal;
435 PositionAlignment m_positionAlignment = PositionAlignment::None;
436 Vector2f m_position;
437 Vector2f m_size;
438 bool m_visible = true;
439 float m_opacity = 1;
440 GroupComponent* m_parent = nullptr;
441 };
442
443 class TGUI_API GroupComponent : public Component
444 {
445 public:
446
447 GroupComponent(const GroupComponent&);
448 GroupComponent& operator=(const GroupComponent&);
449
450 GroupComponent(GroupComponent&&) = default;
451 GroupComponent& operator=(GroupComponent&&) = default;
452
453 Vector2f getClientSize() const;
454
455 void addComponent(const std::shared_ptr<Component>& component);
456
457 const std::vector<std::shared_ptr<Component>>& getComponents() const;
458
459 void draw(BackendRenderTarget& target, RenderStates states) const override;
460
461 void updateLayout() override;
462
463 std::shared_ptr<Component> clone() const override;
464
465 friend void swap(GroupComponent& first, GroupComponent& second);
466
467 protected:
468
469 GroupComponent() = default;
470
471 protected:
472 std::vector<std::shared_ptr<Component>> m_children;
473 Vector2f m_clientSize;
474 };
475
477
478 class TGUI_API BackgroundComponent : public GroupComponent
479 {
480 public:
481
482 BackgroundComponent(StylePropertyBackground* backgroundStyle);
483
484 ~BackgroundComponent() override;
485
486 BackgroundComponent(const BackgroundComponent& other, StylePropertyBackground* backgroundStyle = nullptr);
487 BackgroundComponent& operator=(const BackgroundComponent& other);
488
489 void init();
490
491 void setSize(Vector2f size);
492
493 void setBorders(const Outline& border);
494
495 const Outline& getBorders() const;
496
497 void setPadding(const Outline& padding);
498
499 const Outline& getPadding() const;
500
501 void setOpacity(float opacity);
502
503 void setComponentState(ComponentState state);
504
505 bool isTransparentPixel(Vector2f pos, bool transparentTexture) const;
506
507 void draw(BackendRenderTarget& target, RenderStates states) const override;
508
509 Vector2f getSizeWithoutBorders() const;
510
511 void updateLayout() override;
512
513 std::shared_ptr<Component> clone() const override;
514
515 private:
516
517 struct ColorRect
518 {
519 Color color;
520 FloatRect rect;
521 };
522
523 StylePropertyBackground* m_backgroundStyle;
524
525 ColorRect m_background{Color::White, {}};
526 Color m_borderColor = Color::Black;
527 Sprite m_sprite;
528 Outline m_borders;
529 Outline m_padding;
530
531 std::uint64_t m_borderColorCallbackId = 0;
532 std::uint64_t m_backgroundColorCallbackId = 0;
533 std::uint64_t m_textureCallbackId = 0;
534 std::uint64_t m_bordersCallbackId = 0;
535 std::uint64_t m_paddingCallbackId = 0;
536 };
537
539
540 class TGUI_API TextComponent : public Component
541 {
542 public:
543
544 TextComponent(StylePropertyText* textStyle);
545
546 ~TextComponent() override;
547
548 TextComponent(const TextComponent& other, StylePropertyText* textStyle = nullptr);
549 TextComponent& operator=(const TextComponent& other);
550
551 void init();
552
553 void setString(const String& caption);
554
555 const String& getString() const;
556
557 void setCharacterSize(unsigned int size);
558
559 unsigned int getCharacterSize() const;
560
561 void setFont(const Font& font);
562
563 Font getFont() const;
564
565 void setOutlineColor(Color color);
566
567 Color getOutlineColor() const;
568
569 void setOutlineThickness(float thickness);
570
571 float getOutlineThickness() const;
572
573 float getLineHeight() const;
574
575 void setOpacity(float opacity);
576
577 void updateLayout() override;
578
579 void setComponentState(ComponentState state);
580
581 void draw(BackendRenderTarget& target, RenderStates states) const override;
582
583 std::shared_ptr<Component> clone() const override;
584
585 private:
586 Text m_text;
587 StylePropertyText* m_textStyle;
588
589 Color m_color = Color::Black;
590 TextStyles m_style = TextStyle::Regular;
591
592 std::uint64_t m_colorCallbackId = 0;
593 std::uint64_t m_styleCallbackId = 0;
594 };
595
597
598 class TGUI_API ImageComponent : public Component
599 {
600 public:
601
602 ImageComponent(StyleProperty<Texture>* textureStyle);
603
604 ~ImageComponent() override;
605
606 ImageComponent(const ImageComponent& other, StyleProperty<Texture>* textureStyle = nullptr);
607 ImageComponent& operator=(const ImageComponent& other);
608
609 void init();
610
611 void setSize(Vector2f size);
612
613 void setOpacity(float opacity);
614
615 void setComponentState(ComponentState state);
616
617 bool isTransparentPixel(Vector2f pos, bool transparentTexture) const;
618
619 void draw(BackendRenderTarget& target, RenderStates states) const override;
620
621 std::shared_ptr<Component> clone() const override;
622
623 private:
624
625 StyleProperty<Texture>* m_textureStyle;
626 Sprite m_sprite;
627
628 std::uint64_t m_textureCallbackId = 0;
629 };
630
632
633 inline ComponentState getStateFromFlags(bool hover, bool active, bool focused = false, bool enabled = true)
634 {
635 if (!enabled)
636 {
637 if (active)
638 return ComponentState::DisabledActive;
639 else
640 return ComponentState::Disabled;
641 }
642 else if (focused)
643 {
644 if (active)
645 {
646 if (hover)
647 return ComponentState::FocusedActiveHover;
648 else
649 return ComponentState::FocusedActive;
650 }
651 else if (hover)
652 return ComponentState::FocusedHover;
653 else
654 return ComponentState::Focused;
655 }
656 else if (active)
657 {
658 if (hover)
659 return ComponentState::ActiveHover;
660 else
661 return ComponentState::Active;
662 }
663 else if (hover)
664 return ComponentState::Hover;
665 else
666 return ComponentState::Normal;
667 }
668
670
671 inline void setOptionalPropertyValue(StyleProperty<Color>& property, const Color& color, ComponentState state)
672 {
673 if (color.isSet())
674 property.setValue(color, state);
675 else
676 property.unsetValue(state);
677 }
678
680
681 inline void setOptionalPropertyValue(StyleProperty<TextStyles>& property, const TextStyles& style, ComponentState state)
682 {
683 if (style.isSet())
684 property.setValue(style, state);
685 else
686 property.unsetValue(state);
687 }
688
690
691 inline void setOptionalPropertyValue(StyleProperty<Texture>& property, const Texture& texture, ComponentState state)
692 {
693 if (texture.getData())
694 property.setValue(texture, state);
695 else
696 property.unsetValue(state);
697 }
698
700
701} // namespace dev
702} // namespace priv
703} // namespace tgui
704
706
707#endif // TGUI_COMPONENTS_HPP
Namespace that contains all TGUI functions and classes.
Definition: AbsoluteOrRelativeValue.hpp:36