]> git.localhorst.tv Git - blobs.git/blob - src/ui/widgets.cpp
b0d44e20969fa23a55bbfe589e76d4b0fa3332be
[blobs.git] / src / ui / widgets.cpp
1 #include "Label.hpp"
2 #include "Meter.hpp"
3 #include "Panel.hpp"
4 #include "Widget.hpp"
5
6 #include "../app/Assets.hpp"
7 #include "../graphics/Font.hpp"
8 #include "../graphics/Viewport.hpp"
9
10 #include <iomanip>
11 #include <sstream>
12 #include <glm/gtx/transform.hpp>
13
14
15 namespace blobs {
16 namespace ui {
17
18 Label::Label(const graphics::Font &f)
19 : font(&f)
20 , text()
21 , tex()
22 , fg_color(0.0f, 0.0f, 0.0f, 1.0f)
23 , bg_color(0.0f, 0.0f, 0.0f, 0.0f)
24 , dirty(true) {
25 }
26
27 Label::~Label() {
28 }
29
30 Label *Label::Text(const std::string &t) {
31         if (text != t) {
32                 dirty = true;
33         }
34         text = t;
35         return this;
36 }
37
38 Label *Label::Decimal(double n, int prec) {
39         std::stringstream s;
40         s << std::fixed << std::setprecision(prec) << n;
41         return Text(s.str());
42 }
43
44 Label *Label::Length(double m) {
45         std::stringstream s;
46         s << std::fixed << std::setprecision(3);
47         if (m > 1500.0) {
48                 s << (m * 0.001) << "km";
49         } else if (m < 0.1) {
50                 s << (m * 1000.0) << "mm";
51         } else {
52                 s << m << "m";
53         }
54         return Text(s.str());
55 }
56
57 Label *Label::Mass(double kg) {
58         std::stringstream s;
59         s << std::fixed << std::setprecision(3);
60         if (kg > 1500.0) {
61                 s << (kg * 0.001) << "t";
62         } else if (kg < 0.1) {
63                 s << (kg * 1000.0) << "g";
64         } else if (kg < 0.0001) {
65                 s << (kg * 1.0e6) << "mg";
66         } else {
67                 s << kg << "kg";
68         }
69         return Text(s.str());
70 }
71
72 Label *Label::Percentage(double n) {
73         std::stringstream s;
74         s << std::fixed << std::setprecision(1) << (n * 100.0) << '%';
75         return Text(s.str());
76 }
77
78 Label *Label::Time(double s) {
79         int is = int(s);
80         std::stringstream ss;
81         if (is >= 3600) {
82                 ss << (is / 3600) << "h ";
83                 is %= 3600;
84         }
85         if (is >= 60) {
86                 ss << (is / 60) << "m ";
87                 is %= 60;
88         }
89         ss << is << 's';
90         return Text(ss.str());
91 }
92
93 Label *Label::Font(const graphics::Font &f) {
94         if (font != &f) {
95                 dirty = true;
96         }
97         font = &f;
98         return this;
99 }
100
101 Label *Label::Foreground(const glm::vec4 &c) {
102         fg_color = c;
103         return this;
104 }
105
106 Label *Label::Background(const glm::vec4 &c) {
107         bg_color = c;
108         return this;
109 }
110
111 glm::vec2 Label::Size() {
112         if (text.empty()) {
113                 return glm::vec2(0.0f);
114         }
115         Update();
116         return tex.Size();
117 }
118
119 void Label::Draw(app::Assets &assets, graphics::Viewport &viewport) noexcept {
120         if (text.empty()) return;
121         Update();
122         glm::vec2 size = Size();
123
124         assets.shaders.alpha_sprite.Activate();
125         assets.shaders.alpha_sprite.SetM(glm::translate(glm::vec3(Position() + (size * 0.5f), -ZIndex()))
126                 * glm::scale(glm::vec3(size.x, size.y, 1.0f)));
127         assets.shaders.alpha_sprite.SetTexture(tex);
128         assets.shaders.alpha_sprite.SetFgColor(fg_color);
129         assets.shaders.alpha_sprite.SetBgColor(bg_color);
130         assets.shaders.alpha_sprite.DrawRect();
131 }
132
133 void Label::Update() {
134         if (!dirty || text.empty()) return;
135         font->Render(text, tex);
136         dirty = false;
137 }
138
139
140 Meter::Meter()
141 : fill_color(1.0f)
142 , border_color(1.0f)
143 , size(3.0f)
144 , padding(1.0f)
145 , border(1.0f)
146 , value(0.0f) {
147 }
148
149 Meter::~Meter() {
150 }
151
152 glm::vec2 Meter::Size() {
153         return size + (2.0f * padding) + glm::vec2(2.0f * border);
154 }
155
156 void Meter::Draw(app::Assets &assets, graphics::Viewport &viewport) noexcept {
157         glm::vec2 fullsize = Size();
158         assets.shaders.canvas.Activate();
159         assets.shaders.canvas.ZIndex(ZIndex());
160
161         if (border > 0.0f) {
162                 assets.shaders.canvas.SetColor(border_color);
163                 assets.shaders.canvas.DrawRect(
164                         Position() + glm::vec2(border * 0.5f),
165                         Position() + fullsize - glm::vec2(border * 0.5f),
166                         border
167                 );
168         }
169
170         if (value > 0.0f) {
171                 glm::vec2 bottom_right = Position() + fullsize - glm::vec2(border) - padding;
172                 bottom_right.x -= size.x * (1.0f - value);
173                 assets.shaders.canvas.SetColor(fill_color);
174                 assets.shaders.canvas.FillRect(
175                         Position() + glm::vec2(border) + padding,
176                         bottom_right
177                 );
178         }
179 }
180
181
182 Panel::Panel()
183 : widgets()
184 , bg_color(0.0f, 0.0f, 0.0f, 0.0f)
185 , padding(0.0f)
186 , spacing(0.0f)
187 , dir(VERTICAL)
188 , size(0.0f, 0.0f) {
189 }
190
191 Panel::~Panel() {
192 }
193
194 Panel *Panel::Add(Widget *w) {
195         std::unique_ptr<Widget> widget(w);
196         glm::vec2 wsize = widget->Size();
197         if (dir == HORIZONTAL) {
198                 size.x += wsize.x;
199                 size.y = std::max(size.y, wsize.y);
200         } else {
201                 size.x = std::max(size.x, wsize.x);
202                 size.y += wsize.y;
203         }
204         widgets.emplace_back(std::move(widget));
205         return this;
206 }
207
208 Panel *Panel::Clear() {
209         widgets.clear();
210         size = glm::vec2(0.0f);
211         return this;
212 }
213
214 Panel *Panel::Background(const glm::vec4 &c) {
215         bg_color = c;
216         return this;
217 }
218
219 Panel *Panel::Padding(const glm::vec2 &p) {
220         padding = p;
221         return this;
222 }
223
224 Panel *Panel::Spacing(float s) {
225         spacing = s;
226         return this;
227 }
228
229 Panel *Panel::Direction(Dir d) {
230         dir = d;
231         Layout();
232         return this;
233 }
234
235 glm::vec2 Panel::Size() {
236         glm::vec2 space(0.0f);
237         space[dir] = (widgets.size() - 1) * spacing;
238         return (2.0f * padding) + space + size;
239 }
240
241 void Panel::Layout() {
242         size = glm::vec2(0.0f);
243         if (dir == HORIZONTAL) {
244                 for (auto &w : widgets) {
245                         glm::vec2 wsize = w->Size();
246                         size.x += wsize.x;
247                         size.y = std::max(size.y, wsize.y);
248                 }
249         } else {
250                 for (auto &w : widgets) {
251                         glm::vec2 wsize = w->Size();
252                         size.x = std::max(size.x, wsize.x);
253                         size.y += wsize.y;
254                 }
255         }
256 }
257
258 void Panel::Draw(app::Assets &assets, graphics::Viewport &viewport) noexcept {
259         // TODO: separate draw and layout, it's inefficient and the wrong tree order anyway
260         Layout();
261         if (bg_color.a > 0.0f) {
262                 assets.shaders.canvas.Activate();
263                 assets.shaders.canvas.ZIndex(ZIndex());
264                 assets.shaders.canvas.SetColor(bg_color);
265                 assets.shaders.canvas.FillRect(Position(), Position() + Size());
266         }
267
268         glm::vec2 cursor = Position() + padding;
269         for (auto &w : widgets) {
270                 w->Position(cursor)->ZIndex(ZIndex() + 1.0f);
271                 w->Draw(assets, viewport);
272                 cursor[dir] += w->Size()[dir] + spacing;
273         }
274 }
275
276
277 Widget::Widget()
278 : pos(0.0f)
279 , z_index(1.0f)  {
280 }
281
282 Widget::~Widget() {
283 }
284
285 }
286 }