1 /**
2     Gui Overlay
3 
4     Copyright: (c) Enalye 2017
5     License: Zlib
6     Authors: Enalye
7 */
8 
9 module atelier.ui.gui_overlay;
10 
11 import std.algorithm.comparison : max;
12 import atelier.core, atelier.render, atelier.common;
13 import atelier.ui.gui_element, atelier.ui.label, atelier.ui.text, atelier.ui.gui_manager;
14 
15 private {
16     HintWindow _hintWindow;
17     Hint _displayedHint;
18     GuiElement[] _backupGuis;
19     GuiElement[] _overlayGuiElements;
20     bool _isOverlay = false;
21 }
22 
23 /// Create a Hint for a gui element.
24 Hint makeHint(string text) {
25     return new Hint(text);
26 }
27 
28 /// Displays text next to the cursor when hovering over a gui.
29 class Hint {
30     /// The title is rendered above the text.
31     string text;
32 
33     /// Ctor
34     this(string text_) {
35         text = text_;
36     }
37 }
38 
39 /// Create the hint gui.
40 package void openHintWindow(Hint hint) {
41     _hintWindow = new HintWindow;
42     _displayedHint = hint;
43 }
44 
45 /// Is there a gui running as an overlay ?
46 bool isOverlay() {
47     return _isOverlay;
48 }
49 
50 /// Add the gui as an overlay (rendered above all other guis). \
51 /// Doesn't take away events from the other guis (unlike modal).
52 void setOverlay(GuiElement gui) {
53     if (!_isOverlay) {
54         _isOverlay = true;
55         _backupGuis = getRoots();
56         removeRoots();
57     }
58 
59     _overlayGuiElements ~= gui;
60     appendRoot(gui);
61 }
62 
63 /// Remove the current overlay gui.
64 void stopOverlay() {
65     if (!_isOverlay)
66         return;
67     _isOverlay = false;
68     setRoots(_backupGuis);
69     _backupGuis.length = 0L;
70     _overlayGuiElements.length = 0L;
71 }
72 
73 /// Process events from the guis that aren't overlay.
74 package void processOverlayEvent(Event event) {
75     switch (event.type) with (Event.Type) {
76     case quit:
77         foreach (gui; _backupGuis) {
78             gui.onQuit();
79             gui.onEvent(event);
80         }
81         stopOverlay();
82         break;
83     case resize:
84     case custom:
85         foreach (gui; _backupGuis) {
86             gui.onEvent(event);
87         }
88         break;
89     default:
90         break;
91     }
92 }
93 
94 /// Updates and renders guis that are behind.
95 package(atelier) void processOverlayBack() {
96     foreach (gui; _backupGuis) {
97         updateRoots(gui, null);
98         drawRoots(gui);
99     }
100 }
101 
102 /// Updates and renders guis that are in front (like the hint).
103 package(atelier) void processOverlayFront(float deltaTime) {
104     if (_hintWindow is null)
105         return;
106     _hintWindow.hint = _displayedHint;
107     _hintWindow.update(deltaTime);
108     _hintWindow.draw();
109 }
110 
111 /// Stops overlay gui.
112 void endOverlay() {
113     _displayedHint = null;
114 }
115 
116 /// The gui that renders the hint.
117 private final class HintWindow : GuiElement {
118     private {
119         bool _isRendered;
120     }
121     Label text;
122 
123     @property void hint(Hint hint) {
124         _isRendered = hint !is null;
125         if (_isRendered) {
126             text.text = hint.text;
127         }
128     }
129 
130     this() {
131         text = new Label;
132     }
133 
134     override void update(float deltaTime) {
135         if (!_isRendered)
136             return;
137         size = Vec2f(text.size.x + 25f, text.size.y);
138 
139         //They aren't processed by the gui manager, so we use setScreenCoords directly.
140         setScreenCoords(getMousePos() + size / 2f + Vec2f(20f, 10f));
141         text.setScreenCoords(center + Vec2f(0f, (text.size.y - size.y) / 2f));
142     }
143 
144     override void draw() {
145         if (!_isRendered)
146             return;
147         drawFilledRect(origin, size, Color.white * .11f);
148         text.draw();
149     }
150 }