Gamedev Framework (gf)  0.2.0
A C++11 framework for 2D games
UI.h
1 /*
2  * Gamedev Framework (gf)
3  * Copyright (C) 2016 Julien Bernard
4  *
5  * This software is provided 'as-is', without any express or implied
6  * warranty. In no event will the authors be held liable for any damages
7  * 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
11  * freely, subject to the following restrictions:
12  *
13  * 1. The origin of this software must not be misrepresented; you must not
14  * claim that you wrote the original software. If you use this software
15  * in a product, an acknowledgment in the product documentation would be
16  * appreciated but is not required.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  * misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  */
21 #ifndef GF_UI_H
22 #define GF_UI_H
23 
24 #include <cstdint>
25 #include <string>
26 #include <vector>
27 
28 #include "Alignment.h"
29 #include "Drawable.h"
30 #include "Event.h"
31 #include "Flags.h"
32 #include "Portability.h"
33 #include "Rect.h"
34 #include "Vector.h"
35 
36 namespace gf {
37 #ifndef DOXYGEN_SHOULD_SKIP_THIS
38 inline namespace v1 {
39 #endif
40 
41  class Font;
42 
43  /**
44  * @ingroup graphics
45  * @brief Properties of a user interface
46  *
47  * @sa gf::UI, gf::UIFlags
48  */
49  enum class UIProperties : uint64_t {
50  Enabled = 0x0001, ///< The widget is enabled
51  Hot = 0x0002, ///< The widget is hot
52  Active = 0x0004, ///< The widget is active
53 
54  Selectable = 0x0100, ///< The widget is selectable
55  Draggable = 0x0200, ///< The widget is draggable
56  Reactive = 0x0400, ///< The widget is reactive
57  Underlying = 0x0800, ///< The widget is underlying
58  Useless = 0x1000, ///< The widget is useless
59  };
60 
61  /**
62  * @ingroup graphics
63  * @brief The flags corresponding to user interface properties
64  *
65  * @sa gf::UI, gf::Flags, gf::UIProperties
66  */
67  using UIFlags = Flags<UIProperties>;
68 
69  /**
70  * @ingroup graphics
71  * @brief Layout of a user interface
72  *
73  * The layout contains several metrics that are used to compute the size
74  * and the shape of the widgets.
75  *
76  * @sa gf::UI
77  */
78  struct GF_API UILayout {
79  // general
80  float widgetHeight = 20.0f; ///< The height of the widgets
81  float textHeight = 12.0f; ///< The size of the text
82  float defaultSpacing = 4.0f; ///< The default spacing between widgets
83  float areaHeader = 20.0f; ///< The height of the area header
84  float scrollAreaPadding = 6.0f; ///< The padding of the scroll area
85  float scrollAreaCorner = 6.0f; ///< The corner size of the scroll area
86  // widget specific
87  float buttonCorner = 9.0f; ///< The corner size of a button
88  float itemCorner = 2.0f; ///< The corner size of an item
89  float checkSize = 11.0f; ///< The size of a check
90  float checkCorner = 4.0f; ///< The corner size of a check
91  float sliderCorner = 4.0f; ///< The corner size of a slider
92  float sliderMarkerWidth = 10.0f; ///< The width of a marker
93  float cycleCorner = 4.0f; ///< The corner size of a cycle
94  // misc
95  float indentSize = 16.0f; ///< The indent size
96  };
97 
98  /**
99  * @ingroup graphics
100  * @brief An icon of a user interface
101  *
102  * @sa gf::UI, gf::UIRenderer
103  */
104  enum class UIIcon {
105  Check, ///< A check icon (generally a cross)
106  Collapsed, ///< A collapsed icon (generally a triangle pointing sideways)
107  Expanded, ///< An expanded icon (generally a triangle pointing downwards)
108  Loop, ///< A loop icon
109  };
110 
111  /**
112  * @ingroup graphics
113  * @brief A renderer for a user interface
114  *
115  * The renderer is responsible for displaying the elements of the widgets.
116  * An element is a rectangle, a text or an icon. The flags indicate the
117  * status of the element that can be represented by a color.
118  *
119  * @sa gf::UI, gf::DefaultUIRenderer, gf::UIProperties
120  */
122  public:
123  /**
124  * @brief Virtual destructor
125  */
126  virtual ~UIRenderer();
127 
128  /**
129  * @brief Draw a rectangle
130  *
131  * @param target The target to draw on
132  * @param rect The coordinates of the rectangle to draw
133  * @param corner The corner size (or no corner if 0)
134  * @param flags The status of the widget
135  */
136  virtual void drawRect(RenderTarget& target, const RectF& rect, float corner, UIFlags flags) const = 0;
137 
138  /**
139  * @brief Draw a text
140  *
141  * @param target The target to draw on
142  * @param text The text to draw
143  * @param size The character size of the text
144  * @param pos The position of the text (top-left corner)
145  * @param width The availabe width for the text
146  * @param alignment The alignment of the text
147  * @param flags The status of the widget
148  */
149  virtual void drawText(RenderTarget& target, const std::string& text, unsigned size, Vector2f pos, float width, Alignment alignment, UIFlags flags) const = 0;
150 
151  /**
152  * @brief Draw an icon
153  *
154  * @param target The target to draw on
155  * @param pos The position of the icon (center)
156  * @param icon The icon to draw
157  * @param flags The status of the widget
158  */
159  virtual void drawIcon(RenderTarget& target, Vector2f pos, UIIcon icon, UIFlags flags) const = 0;
160  };
161 
162  /**
163  * @ingroup graphics
164  * @brief The default renderer for user interface
165  *
166  * @sa gf::UI, gf::UIRenderer
167  */
169  public:
170  /**
171  * @brief Constructor
172  *
173  * @param font The font to display the text
174  */
175  DefaultUIRenderer(gf::Font& font);
176 
177  virtual void drawRect(RenderTarget& target, const RectF& rect, float corner, UIFlags flags) const override;
178 
179  virtual void drawText(RenderTarget& target, const std::string& text, unsigned size, Vector2f pos, float width, Alignment alignment, UIFlags flags) const override;
180 
181  virtual void drawIcon(RenderTarget& target, Vector2f pos, UIIcon icon, UIFlags flags) const override;
182  private:
183  gf::Font *m_font;
184  };
185 
186  /**
187  * @ingroup graphics
188  * @brief An immediate mode user interface class
189  *
190  * ~~~{.cc}
191  * // initialisation
192  *
193  * gf::DefaultUIRenderer uiRenderer(font);
194  * gf::UILayout uiLayout;
195  * gf::UI ui(uiRenderer, uiLayout);
196  *
197  * // state
198  *
199  * bool checked1 = false;
200  * bool checked2 = false;
201  * bool checked3 = true;
202  * bool checked4 = false;
203  * float scrollArea = 0;
204  * float value1 = 50.0f;
205  * float value2 = 30.0f;
206  *
207  * std::vector<std::string> choices = { "First Choice", "Next Choice", "Last Choice" };
208  * std::size_t choice = 0;
209  *
210  * // ...
211  *
212  * // in the main loop
213  * ui.beginScrollArea("Scroll area", { 10.0f, 10.0f, 200.0f, 400.0f }, &scrollArea);
214  *
215  * ui.separatorLine();
216  * ui.separator();
217  *
218  * ui.button("Button");
219  * ui.button("Disabled Button", false);
220  * ui.item("Item");
221  * ui.item("Disabled item", false);
222  *
223  * if (ui.check("Checkbox", checked1)) {
224  * checked1 = !checked1;
225  * }
226  *
227  * if (ui.check("Disabled checkbox", checked2, false)) {
228  * checked2 = !checked2;
229  * }
230  *
231  * if (ui.collapse("Collapse", checked3)) {
232  * checked3 = !checked3;
233  * }
234  *
235  * if (checked3) {
236  * ui.indent();
237  * ui.label("Collapsible element");
238  * ui.unindent();
239  * }
240  *
241  *
242  * if (ui.collapse("Disabled collapse", checked4, false)) {
243  * checked4 = !checked4;
244  * }
245  *
246  * ui.label("Label");
247  * ui.value("Value");
248  *
249  * ui.slider("Slider", &value1, 0.0f, 100.0f, 1.0f);
250  * ui.slider("Disabled slider", &value2, 0.0f, 100.0f, 1.0f, false);
251  *
252  * ui.indent();
253  * ui.label("Indented");
254  * ui.unindent();
255  * ui.label("Unindented");
256  *
257  * if (ui.cycle(choices, choice)) {
258  * choice = (choice + 1) % choices.size();
259  * }
260  *
261  * ui.endScrollArea();
262  * ~~~
263  *
264  * The result of the previous code is given in the following figure.
265  *
266  * ![UI example](@ref ui.png)
267  *
268  * @sa gf::UIRenderer, gf::UILayout, gf::UIFlags, gf::UIIcon
269  */
270  class GF_API UI : public Drawable {
271  public:
272  /**
273  * @brief Constructor
274  *
275  * @param renderer A renderer for user interface
276  * @param layout A layout for user interface
277  */
278  UI(const UIRenderer& renderer, const UILayout& layout);
279 
280  /**
281  * @brief Update the internal state with an event
282  *
283  * This function must be called for every event that occur in a frame.
284  *
285  * @param event An event
286  */
287  void update(const Event& event);
288 
289  /**
290  * @brief Clear the internal state for this frame
291  *
292  * This function must be called at the beginning of every frame, before
293  * any other call to any function.
294  */
295  void clear();
296 
297  /**
298  * @brief Define the main widget
299  *
300  * A scroll area is the main widget. It is the container for the other
301  * widgets. It may have a scrollbar on the right side if the widgets are
302  * too high.
303  *
304  * @param name The name of the scroll area
305  * @param area The area of the scroll area
306  * @param scroll A pointer to the value of the scroll
307  *
308  * @sa endScrollArea()
309  * @sa [Scrollbar - Wikipedia](https://en.wikipedia.org/wiki/Scrollbar)
310  */
311  bool beginScrollArea(const std::string& name, const RectF& area, float *scroll);
312 
313  /**
314  * @brief End the main widget
315  *
316  * @sa beginScrollArea()
317  */
318  void endScrollArea();
319 
320  /**
321  * @brief Increase the indent size
322  *
323  * After this call, all the widgets will be indented. It is possible to
324  * call this function multiple times.
325  *
326  * @sa unindent()
327  */
328  void indent();
329 
330  /**
331  * @brief Decrease the indent size
332  *
333  * After this call, all the widgets will be unindented. It is possible to
334  * call this function multiple times.
335  *
336  * @sa indent()
337  */
338  void unindent();
339 
340  /**
341  * @brief Add an invisible separator
342  */
343  void separator();
344 
345  /**
346  * @brief Add a separator line
347  */
348  void separatorLine();
349 
350  /**
351  * @brief Add a button
352  *
353  * @param text The text on the button
354  * @param enabled True if the button can be pressed
355  * @returns True if the button has been activated
356  *
357  * @sa [Button - Wikipedia](https://en.wikipedia.org/wiki/Button_%28computing%29)
358  */
359  bool button(const std::string& text, bool enabled = true);
360 
361  /**
362  * @brief Add an item
363  *
364  * @param text The text on the item
365  * @param enabled True if the item can be pressed
366  * @returns True if the item has been activated
367  */
368  bool item(const std::string& text, bool enabled = true);
369 
370  /**
371  * @brief Add a checkbox
372  *
373  * @param text The text of the checkbox
374  * @param checked True if the checkbox is checked
375  * @param enabled True if the checkbox can be checked
376  * @returns True if the checkbox has been activated
377  *
378  * @sa [Checkbox - Wikipedia](https://en.wikipedia.org/wiki/Checkbox)
379  */
380  bool check(const std::string& text, bool checked, bool enabled = true);
381 
382  /**
383  * @brief Add a disclosure widget
384  *
385  * @param text The text of the widget
386  * @param checked True if the widget is expanded
387  * @param enabled True if the widget if enabled
388  * @returns True if the widget has been activated
389  *
390  * @sa [Disclosure widget - Wikipedia](https://en.wikipedia.org/wiki/Disclosure_widget)
391  */
392  bool collapse(const std::string& text, bool checked, bool enabled = true);
393 
394  /**
395  * @brief Add a label
396  *
397  * A label is a left aligned text
398  *
399  * @param text The text of the label
400  * @sa value()
401  * @sa [Label - Wikipedia](https://en.wikipedia.org/wiki/Label_%28control%29)
402  */
403  void label(const std::string& text);
404 
405  /**
406  * @brief Add a value
407  *
408  * A value is a right aligned text
409  *
410  * @param text The text of the value
411  * @sa label()
412  */
413  void value(const std::string& text);
414 
415  /**
416  * @brief Add a slider
417  *
418  * @param text The text of the slider
419  * @param val A pointer to the value of the slider
420  * @param vmin The minimum value of the slider
421  * @param vmax The maximum value of the slider
422  * @param vinc The increment of the slider
423  * @param enabled True if the slider is enabled
424  * @returns True if the slider has been activated
425  *
426  * @sa [Slider - Wikipedia](https://en.wikipedia.org/wiki/Slider_%28computing%29)
427  */
428  bool slider(const std::string& text, float *val, float vmin, float vmax, float vinc, bool enabled = true);
429 
430  /**
431  * @brief Add a cycle
432  *
433  * @param choices The possible values of the cycles
434  * @param choice The current value of the cycle
435  * @param enabled True if the cycle is enabled
436  * @returns True if the cycle has been activated
437  *
438  * @sa [Cycle button - Wikipedia](https://en.wikipedia.org/wiki/Cycle_button)
439  */
440  bool cycle(const std::vector<std::string>& choices, std::size_t choice, bool enabled = true);
441 
442  virtual void draw(RenderTarget &target, RenderStates states) override;
443 
444  private:
445  const UIRenderer& m_renderer;
446  const UILayout& m_layout;
447 
448  enum class CommandType {
449  Scissor,
450  Rect,
451  Text,
452  Icon,
453  };
454 
455  struct Command {
456  CommandType type;
457  UIFlags flags;
458  };
459 
460  std::vector<Command> m_commands;
461 
462  enum class ScissorAction {
463  Set,
464  Reset,
465  };
466 
467  struct ScissorCommand {
468  ScissorAction action;
469  RectF rect;
470  };
471 
472  std::vector<ScissorCommand> m_scissorCommands;
473 
474  struct RectCommand {
475  RectF rect;
476  float corner;
477  };
478 
479  std::vector<RectCommand> m_rectCommands;
480 
481  struct TextCommand {
482  Vector2f pos;
483  float width;
484  std::string text;
485  unsigned size;
486  Alignment alignment;
487  };
488 
489  std::vector<TextCommand> m_textCommands;
490 
491  struct IconCommand {
492  Vector2f pos;
493  UIIcon icon;
494  };
495 
496  std::vector<IconCommand> m_iconCommands;
497 
498  void resetCommandQueue();
499  void addScissorCommand(ScissorAction action, const RectF& rect = RectF());
500  void addRectCommand(const RectF& rect, float corner, UIFlags flags);
501  void addTextCommand(Vector2f pos, float width, const std::string& text, unsigned size, Alignment alignment, UIFlags flags);
502  void addIconCommand(Vector2f pos, UIIcon icon, UIFlags flags);
503 
504 
505  private:
506  uint64_t m_areaId;
507  uint64_t m_widgetId;
508 
509  uint64_t generateId();
510 
511  private:
512  bool m_leftPressed;
513  bool m_leftReleased;
514 
515  Vector2i m_mouseCursor;
516  int m_scroll;
517 
518  uint64_t m_active;
519  uint64_t m_hot;
520  uint64_t m_nextHot;
521 
522  bool m_wentActive;
523 
524  Vector2i m_dragPos;
525  float m_dragState;
526 
527  float m_widgetX;
528  float m_widgetY;
529  float m_widgetW;
530 
531  bool m_insideCurrentScroll;
532 
533  RectF m_scrollRect;
534  float m_scrollStart;
535  float *m_scrollValue;
536 
537  uint64_t m_scrollId;
538  bool m_insideScrollArea;
539 
540  // helpers
541 
542  bool isAnyActive() const {
543  return m_active != 0;
544  }
545 
546  bool isActive(uint64_t id) const {
547  return m_active == id;
548  }
549 
550  bool isHot(uint64_t id) const {
551  return m_hot == id;
552  }
553 
554  bool inWidget(const RectF& rect, bool checkScroll = true) const;
555 
556  void clearInput() {
557  m_leftPressed = false;
558  m_leftReleased = false;
559  m_scroll = 0;
560  }
561 
562  void clearActive() {
563  m_active = 0;
564  clearInput();
565  }
566 
567  void setActive(uint64_t id) {
568  m_active = id;
569  m_wentActive = true;
570  }
571 
572  void setHot(uint64_t id) {
573  m_nextHot = id;
574  }
575 
576  bool processWidget(uint64_t id, bool over);
577  RectF reserveWidget(float height, bool spacing = true);
578 
579  };
580 
581 #ifndef DOXYGEN_SHOULD_SKIP_THIS
582 }
583 #endif
584 
585 #ifndef DOXYGEN_SHOULD_SKIP_THIS
586 template<>
587 struct EnableBitmaskOperators<UIProperties> {
588  static constexpr bool value = true;
589 };
590 #endif
591 
592 }
593 
594 #endif // GF_UI_H
bool check(const std::string &text, bool checked, bool enabled=true)
Add a checkbox.
float checkCorner
The corner size of a check.
Definition: UI.h:90
virtual void drawText(RenderTarget &target, const std::string &text, unsigned size, Vector2f pos, float width, Alignment alignment, UIFlags flags) const override
Draw a text.
void clear()
Clear the internal state for this frame.
float sliderCorner
The corner size of a slider.
Definition: UI.h:91
float cycleCorner
The corner size of a cycle.
Definition: UI.h:93
void update(const Event &event)
Update the internal state with an event.
DefaultUIRenderer(gf::Font &font)
Constructor.
Base class for all render targets (window, texture, ...)
Definition: RenderTarget.h:65
Define the states used for drawing to a RenderTarget.
Definition: RenderStates.h:82
A loop icon.
Bitfield relying on an enumeration.
Definition: Flags.h:60
A check icon (generally a cross)
A collapsed icon (generally a triangle pointing sideways)
void separatorLine()
Add a separator line.
bool button(const std::string &text, bool enabled=true)
Add a button.
An immediate mode user interface class.
Definition: UI.h:270
float checkSize
The size of a check.
Definition: UI.h:89
bool slider(const std::string &text, float *val, float vmin, float vmax, float vinc, bool enabled=true)
Add a slider.
bool beginScrollArea(const std::string &name, const RectF &area, float *scroll)
Define the main widget.
Layout of a user interface.
Definition: UI.h:78
float scrollAreaCorner
The corner size of the scroll area.
Definition: UI.h:85
Abstract base class for objects that can be drawn to a render window.
Definition: Drawable.h:79
UIProperties
Properties of a user interface.
Definition: UI.h:49
void endScrollArea()
End the main widget.
float indentSize
The indent size.
Definition: UI.h:95
virtual ~UIRenderer()
Virtual destructor.
The widget is underlying.
void separator()
Add an invisible separator.
float areaHeader
The height of the area header.
Definition: UI.h:83
virtual void drawIcon(RenderTarget &target, Vector2f pos, UIIcon icon, UIFlags flags) const override
Draw an icon.
An expanded icon (generally a triangle pointing downwards)
bool cycle(const std::vector< std::string > &choices, std::size_t choice, bool enabled=true)
Add a cycle.
float textHeight
The size of the text.
Definition: UI.h:81
The widget is enabled.
A renderer for a user interface.
Definition: UI.h:121
void value(const std::string &text)
Add a value.
The widget is reactive.
The widget is hot.
void indent()
Increase the indent size.
The widget is selectable.
virtual void drawText(RenderTarget &target, const std::string &text, unsigned size, Vector2f pos, float width, Alignment alignment, UIFlags flags) const =0
Draw a text.
Definition: Action.h:34
A character font.
Definition: Font.h:130
bool collapse(const std::string &text, bool checked, bool enabled=true)
Add a disclosure widget.
virtual void draw(RenderTarget &target, RenderStates states) override
Draw the object to a render target.
float widgetHeight
The height of the widgets.
Definition: UI.h:80
virtual void drawIcon(RenderTarget &target, Vector2f pos, UIIcon icon, UIFlags flags) const =0
Draw an icon.
The widget is active.
float defaultSpacing
The default spacing between widgets.
Definition: UI.h:82
UIIcon
An icon of a user interface.
Definition: UI.h:104
Alignment
The alignement of a text.
Definition: Alignment.h:32
float scrollAreaPadding
The padding of the scroll area.
Definition: UI.h:84
The widget is draggable.
The default renderer for user interface.
Definition: UI.h:168
constexpr Rect() noexcept
Default constructor.
Definition: Rect.h:111
void label(const std::string &text)
Add a label.
float itemCorner
The corner size of an item.
Definition: UI.h:88
virtual void drawRect(RenderTarget &target, const RectF &rect, float corner, UIFlags flags) const =0
Draw a rectangle.
The widget is useless.
float buttonCorner
The corner size of a button.
Definition: UI.h:87
Defines a system event and its parameters.
Definition: Event.h:115
#define GF_API
Definition: Portability.h:35
UI(const UIRenderer &renderer, const UILayout &layout)
Constructor.
bool item(const std::string &text, bool enabled=true)
Add an item.
float sliderMarkerWidth
The width of a marker.
Definition: UI.h:92
virtual void drawRect(RenderTarget &target, const RectF &rect, float corner, UIFlags flags) const override
Draw a rectangle.
void unindent()
Decrease the indent size.