TGUI  0.9.1
Components.hpp
1 //
3 // TGUI - Texus' Graphical User Interface
4 // Copyright (C) 2012-2021 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 
40 namespace tgui
41 {
42  class BackendRenderTargetBase;
43 
44 namespace priv
45 {
46 namespace 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_propertyData {0},
140  m_messageTopicId{MessageBroker::createTopic()}
141  {
142  }
143 
144  explicit StyleProperty(const ValueType& defaultValue) :
145  m_defaultValue {defaultValue},
146  m_propertyData {0},
147  m_messageTopicId{MessageBroker::createTopic()}
148  {
149  }
150 
151  StyleProperty(const StyleProperty& other) :
152  m_defaultValue {other.m_defaultValue},
153  m_propertyData {0},
154  m_messageTopicId{MessageBroker::createTopic()},
155  m_globalValues {other.m_globalValues}
156  {
157  unsetValue();
158 
159  const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
160  const std::uint64_t oldBaseIndex = other.m_propertyData & 0xFFFFFFFFFFFF0000;
161  const std::uint16_t oldStoredStates = static_cast<std::uint16_t>(other.m_propertyData & 0xFFFF);
162 
163  std::uint16_t total = 0;
164  std::uint8_t bitIndex = 0;
165  while (total < oldStoredStates)
166  {
167  if (oldStoredStates & (1 << bitIndex))
168  {
169  m_globalValues[baseIndex + bitIndex] = m_globalValues[oldBaseIndex + bitIndex];
170  total += (1 << bitIndex);
171  }
172  ++bitIndex;
173  }
174 
175  m_propertyData = baseIndex | oldStoredStates;
176  }
177 
178  StyleProperty(StyleProperty&& other) :
179  m_defaultValue {std::move(other.m_defaultValue)},
180  m_propertyData {std::move(other.m_propertyData)},
181  m_messageTopicId{std::move(other.m_messageTopicId)},
182  m_globalValues {std::move(other.m_globalValues)}
183  {
184  other.m_messageTopicId = 0;
185  }
186 
187  ~StyleProperty()
188  {
189  if (m_messageTopicId) // Can be 0 on moved object
190  MessageBroker::destroyTopic(m_messageTopicId);
191  unsetValueImpl();
192  }
193 
194  StyleProperty& operator=(const StyleProperty& other)
195  {
196  if (&other != this)
197  {
198  StyleProperty temp(other);
199  std::swap(m_defaultValue, temp.m_defaultValue);
200  std::swap(m_propertyData, temp.m_propertyData);
201  std::swap(m_messageTopicId, temp.m_messageTopicId);
202  std::swap(m_globalValues, temp.m_globalValues);
203  }
204 
205  return *this;
206  }
207 
208  StyleProperty& operator=(StyleProperty&& other)
209  {
210  if (&other != this)
211  {
212  m_defaultValue = std::move(other.m_defaultValue);
213  m_propertyData = std::move(other.m_propertyData);
214  m_messageTopicId = std::move(other.m_messageTopicId);
215  m_globalValues = std::move(other.m_globalValues);
216 
217  other.m_messageTopicId = 0;
218  }
219 
220  return *this;
221  }
222 
223  StyleProperty& operator=(const ValueType& value)
224  {
225  unsetValueImpl();
226  setValue(value, ComponentState::Normal);
227  return *this;
228  }
229 
230  void setValue(const ValueType& value, ComponentState state = ComponentState::Normal)
231  {
232  const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
233  m_propertyData |= static_cast<std::uint64_t>(1) << static_cast<std::uint8_t>(state);
234  m_globalValues[baseIndex + static_cast<std::uint8_t>(state)] = value;
235 
236  MessageBroker::sendEvent(m_messageTopicId);
237  }
238 
239  void unsetValue(ComponentState state)
240  {
241  const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
242  m_propertyData &= ~(1 << static_cast<std::uint8_t>(state));
243  m_globalValues.erase(baseIndex + static_cast<std::uint8_t>(state));
244 
245  MessageBroker::sendEvent(m_messageTopicId);
246  }
247 
248  void unsetValue()
249  {
250  unsetValueImpl();
251  MessageBroker::sendEvent(m_messageTopicId);
252  }
253 
254  const ValueType& getValue(ComponentState state = ComponentState::Normal) const
255  {
256  const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
257  const std::uint16_t storedStates = static_cast<std::uint16_t>(m_propertyData & 0xFFFF);
258 
259  // If we don't have a value for any state then we can just return the default value
260  if (storedStates == 0)
261  return m_defaultValue;
262 
263  // If we only have a value for the Normal state then always use this value
264  if (storedStates == 1)
265  return m_globalValues.at(baseIndex);
266 
267  if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Disabled))
268  {
269  if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Active)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::DisabledActive))))
270  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::DisabledActive));
271  else if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Disabled)))
272  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Disabled));
273  }
274  else if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Focused))
275  {
276  if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Active))
277  {
278  if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Hover)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::FocusedActiveHover))))
279  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::FocusedActiveHover));
280  else if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::FocusedActive)))
281  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::FocusedActive));
282  }
283 
284  if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Hover)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::FocusedHover))))
285  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::FocusedHover));
286  else if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Focused)))
287  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Focused));
288  }
289 
290  if (static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Active))
291  {
292  if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Hover)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::ActiveHover))))
293  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::ActiveHover));
294  else if (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Active)))
295  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Active));
296  }
297 
298  if ((static_cast<std::uint8_t>(state) & static_cast<std::uint8_t>(ComponentState::Hover)) && (storedStates & (1 << static_cast<std::uint8_t>(ComponentState::Hover))))
299  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Hover));
300  else if (storedStates & 1)
301  {
302  // We have a value for the Normal state, so return it. It is possible to pass here while storedStates != 1 when there
303  // is e.g. a value for both Normal and Disabled state and the widget is enabled.
304  return m_globalValues.at(baseIndex + static_cast<std::uint8_t>(ComponentState::Normal));
305  }
306  else
307  {
308  // We don't have any relevant values, so return the default value. It is possible to pass here while storedStates > 0 when
309  // there is e.g. only a value for the Disabled state and the widget is enabled.
310  return m_defaultValue;
311  }
312  }
313 
314  std::uint64_t connectCallback(std::function<void()> func)
315  {
316  return MessageBroker::subscribe(m_messageTopicId, std::move(func));
317  }
318 
319  void disconnectCallback(std::uint64_t id)
320  {
321  return MessageBroker::unsubscribe(id);
322  }
323 
324  private:
325 
326  void unsetValueImpl()
327  {
328  const std::uint64_t baseIndex = m_propertyData & 0xFFFFFFFFFFFF0000;
329  const std::uint16_t storedStates = static_cast<std::uint16_t>(m_propertyData & 0xFFFF);
330 
331  std::uint16_t total = 0;
332  std::uint8_t bitIndex = 0;
333  while (total < storedStates)
334  {
335  if (storedStates & (1 << bitIndex))
336  {
337  m_globalValues.erase(baseIndex + bitIndex);
338  total += (1 << bitIndex);
339  }
340  ++bitIndex;
341  }
342 
343  m_propertyData = baseIndex; // Forget about the states that were stored
344  }
345 
346  private:
347 
348  ValueType m_defaultValue;
349 
350  // The highest 48 bits store the index in a static map of values (see below).
351  // The next 16 bits are used to indicate the set of states that are present for this property.
352  std::uint64_t m_propertyData = 0;
353 
354  // Index of the event that we publish to when the property changes.
355  std::uint64_t m_messageTopicId = 0;
356 
357  // All values are stored in a static map, which can be seen as a very large sparse list.
358  // Each instance holds a unique 48-bit id that can be seen as the high bits of the index in the list.
359  // The lowest 4 bits of the index contain the widget state. The remaining 12 bits inbetween are always 0.
362 
365  std::unordered_map<std::uint64_t, ValueType> m_globalValues;
366  };
367 
369 
370  struct TGUI_API StylePropertyBackground
371  {
372  StyleProperty<Color> borderColor{Color::Black};
373  StyleProperty<Color> color{Color::White};
374  StyleProperty<Texture> texture;
375  StyleProperty<Outline> borders;
376  StyleProperty<Outline> padding;
377 
378  float roundedBorderRadius = 0;
379  };
380 
382 
383  struct TGUI_API StylePropertyText
384  {
385  StyleProperty<Color> color{Color::Black};
386  StyleProperty<TextStyles> style;
387  };
388 
390 
391  class GroupComponent;
392 
393  class TGUI_API Component
394  {
395  public:
396 
397  Component(const Component&);
398  Component& operator=(const Component&);
399 
400  Component(Component&&) = default;
401  Component& operator=(Component&&) = default;
402 
403  void setPosition(Vector2f position);
404 
405  Vector2f getPosition() const;
406 
407  Vector2f getSize() const;
408 
409  void setPositionAlignment(PositionAlignment alignment);
410 
411  void setVisible(bool visible);
412 
413  bool isVisible() const;
414 
415  void setParent(GroupComponent* parent);
416 
417  virtual void draw(BackendRenderTargetBase& target, RenderStates states) const = 0;
418 
419  virtual void updateLayout();
420 
421  virtual std::shared_ptr<Component> clone() const = 0;
422 
423  protected:
424 
425  Component() = default;
426 
427  virtual ~Component() = default;
428 
429  friend void swap(Component& first, Component& second);
430 
431  protected:
432 
433  ComponentState m_state = ComponentState::Normal;
434  PositionAlignment m_positionAlignment = PositionAlignment::None;
435  Vector2f m_position;
436  Vector2f m_size;
437  bool m_visible = true;
438  float m_opacity = 1;
439  GroupComponent* m_parent = nullptr;
440  };
441 
442  class TGUI_API GroupComponent : public Component
443  {
444  public:
445 
446  GroupComponent(const GroupComponent&);
447  GroupComponent& operator=(const GroupComponent&);
448 
449  GroupComponent(GroupComponent&&) = default;
450  GroupComponent& operator=(GroupComponent&&) = default;
451 
452  Vector2f getClientSize() const;
453 
454  void addComponent(const std::shared_ptr<Component>& component);
455 
456  const std::vector<std::shared_ptr<Component>>& getComponents() const;
457 
458  void draw(BackendRenderTargetBase& target, RenderStates states) const override;
459 
460  void updateLayout() override;
461 
462  std::shared_ptr<Component> clone() const override;
463 
464  friend void swap(GroupComponent& first, GroupComponent& second);
465 
466  protected:
467 
468  GroupComponent() = default;
469 
470  protected:
471  std::vector<std::shared_ptr<Component>> m_children;
472  Vector2f m_clientSize;
473  };
474 
476 
477  class TGUI_API BackgroundComponent : public GroupComponent
478  {
479  public:
480 
481  BackgroundComponent(StylePropertyBackground* backgroundStyle);
482 
483  ~BackgroundComponent();
484 
485  BackgroundComponent(const BackgroundComponent& other, StylePropertyBackground* backgroundStyle = nullptr);
486  BackgroundComponent& operator=(const BackgroundComponent& other);
487 
488  void init();
489 
490  void setSize(Vector2f size);
491 
492  void setBorders(const Outline& border);
493 
494  const Outline& getBorders() const;
495 
496  void setPadding(const Outline& padding);
497 
498  const Outline& getPadding() const;
499 
500  void setOpacity(float opacity);
501 
502  void setComponentState(ComponentState state);
503 
504  bool isTransparentPixel(Vector2f pos, bool transparentTexture) const;
505 
506  void draw(BackendRenderTargetBase& target, RenderStates states) const override;
507 
508  Vector2f getSizeWithoutBorders() const;
509 
510  void updateLayout() override;
511 
512  std::shared_ptr<Component> clone() const override;
513 
514  private:
515 
516  struct ColorRect
517  {
518  Color color;
519  FloatRect rect;
520  };
521 
522  StylePropertyBackground* m_backgroundStyle;
523 
524  ColorRect m_background{Color::White, {}};
525  Color m_borderColor = Color::Black;
526  Sprite m_sprite;
527  Outline m_borders;
528  Outline m_padding;
529 
530  std::uint64_t m_borderColorCallbackId = 0;
531  std::uint64_t m_backgroundColorCallbackId = 0;
532  std::uint64_t m_textureCallbackId = 0;
533  std::uint64_t m_bordersCallbackId = 0;
534  std::uint64_t m_paddingCallbackId = 0;
535  };
536 
538 
539  class TGUI_API TextComponent : public Component
540  {
541  public:
542 
543  TextComponent(StylePropertyText* textStyle);
544 
545  ~TextComponent();
546 
547  TextComponent(const TextComponent& other, StylePropertyText* textStyle = nullptr);
548  TextComponent& operator=(const TextComponent& other);
549 
550  void init();
551 
552  void setString(const String& caption);
553 
554  const String& getString() const;
555 
556  void setCharacterSize(unsigned int size);
557 
558  unsigned int getCharacterSize() const;
559 
560  void setFont(Font font);
561 
562  Font getFont() const;
563 
564  void setOutlineColor(Color color);
565 
566  Color getOutlineColor() const;
567 
568  void setOutlineThickness(float thickness);
569 
570  float getOutlineThickness() const;
571 
572  float getLineHeight() const;
573 
574  void setOpacity(float opacity);
575 
576  void updateLayout() override;
577 
578  void setComponentState(ComponentState state);
579 
580  void draw(BackendRenderTargetBase& target, RenderStates states) const override;
581 
582  std::shared_ptr<Component> clone() const override;
583 
584  private:
585  Text m_text;
586  StylePropertyText* m_textStyle;
587 
588  Color m_color = Color::Black;
589  TextStyles m_style = TextStyle::Regular;
590 
591  std::uint64_t m_colorCallbackId = 0;
592  std::uint64_t m_styleCallbackId = 0;
593  };
594 
596 
597  class TGUI_API ImageComponent : public Component
598  {
599  public:
600 
601  ImageComponent(StyleProperty<Texture>* textureStyle);
602 
603  ~ImageComponent();
604 
605  ImageComponent(const ImageComponent& other, StyleProperty<Texture>* textureStyle = nullptr);
606  ImageComponent& operator=(const ImageComponent& other);
607 
608  void init();
609 
610  void setSize(Vector2f size);
611 
612  void setOpacity(float opacity);
613 
614  void setComponentState(ComponentState state);
615 
616  bool isTransparentPixel(Vector2f pos, bool transparentTexture) const;
617 
618  void draw(BackendRenderTargetBase& target, RenderStates states) const override;
619 
620  std::shared_ptr<Component> clone() const override;
621 
622  private:
623 
624  StyleProperty<Texture>* m_textureStyle;
625  Sprite m_sprite;
626 
627  std::uint64_t m_textureCallbackId = 0;
628  };
629 
631 
632  inline ComponentState getStateFromFlags(bool hover, bool active, bool focused = false, bool enabled = true)
633  {
634  if (!enabled)
635  {
636  if (active)
637  return ComponentState::DisabledActive;
638  else
639  return ComponentState::Disabled;
640  }
641  else if (focused)
642  {
643  if (active)
644  {
645  if (hover)
646  return ComponentState::FocusedActiveHover;
647  else
648  return ComponentState::FocusedActive;
649  }
650  else if (hover)
651  return ComponentState::FocusedHover;
652  else
653  return ComponentState::Focused;
654  }
655  else if (active)
656  {
657  if (hover)
658  return ComponentState::ActiveHover;
659  else
660  return ComponentState::Active;
661  }
662  else if (hover)
663  return ComponentState::Hover;
664  else
665  return ComponentState::Normal;
666  }
667 
669 
670  inline void setOptionalPropertyValue(StyleProperty<Color>& property, const Color& color, ComponentState state)
671  {
672  if (color.isSet())
673  property.setValue(color, state);
674  else
675  property.unsetValue(state);
676  }
677 
679 
680  inline void setOptionalPropertyValue(StyleProperty<TextStyles>& property, const TextStyles& style, ComponentState state)
681  {
682  if (style.isSet())
683  property.setValue(style, state);
684  else
685  property.unsetValue(state);
686  }
687 
689 
690  inline void setOptionalPropertyValue(StyleProperty<Texture>& property, const Texture& texture, ComponentState state)
691  {
692  if (texture.getData())
693  property.setValue(texture, state);
694  else
695  property.unsetValue(state);
696  }
697 
699 
700 } // namespace dev
701 } // namespace priv
702 } // namespace tgui
703 
705 
706 #endif // TGUI_COMPONENTS_HPP
static const Color White
White predefined color.
Definition: Color.hpp:237
static const Color Black
Black predefined color.
Definition: Color.hpp:236
Namespace that contains all TGUI functions and classes.
Definition: AbsoluteOrRelativeValue.hpp:36