1 /* --------------------------------------------------------------------
2 EXTREME TUXRACER
3 
4 Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer)
5 Copyright (C) 2010 Extreme Tux Racer Team
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 ---------------------------------------------------------------------*/
17 
18 #ifdef HAVE_CONFIG_H
19 #include <etr_config.h>
20 #endif
21 
22 #include "gui.h"
23 #include "textures.h"
24 #include "font.h"
25 #include "ogl.h"
26 #include "winsys.h"
27 #include <vector>
28 
29 
30 #define CURSOR_SIZE 10
31 
32 static std::vector<TWidget*> Widgets;
33 static int lock_focussed = -1;
34 static int focussed = -1;
35 static bool locked_LR = false;
36 static bool locked_UD = false;
37 
AddWidget(TWidget * widget)38 static TWidget* AddWidget(TWidget* widget) {
39 	if (Widgets.size() == focussed) {
40 		widget->focus = true;
41 		widget->Focussed();
42 	}
43 	Widgets.push_back(widget);
44 	return widget;
45 }
46 
Inside(int x,int y,const TRect & Rect)47 static bool Inside(int x, int y, const TRect& Rect) {
48 	return (x >= Rect.left
49 	        && x <= Rect.left + Rect.width
50 	        && y >= Rect.top
51 	        && y <= Rect.top + Rect.height);
52 }
53 
TWidget(int x,int y,int width,int height,bool interactive_)54 TWidget::TWidget(int x, int y, int width, int height, bool interactive_)
55 	: active(true)
56 	, visible(true)
57 	, interactive(interactive_)
58 	, focus(false) {
59 	mouseRect.top = y;
60 	mouseRect.left = x;
61 	mouseRect.height = height;
62 	mouseRect.width = width;
63 	position.x = x;
64 	position.y = y;
65 }
66 
Click(int x,int y)67 bool TWidget::Click(int x, int y) {
68 	return active && visible && Inside(x, y, mouseRect);
69 }
70 
MouseMove(int x,int y)71 void TWidget::MouseMove(int x, int y) {
72 	bool ofocus = focus;
73 	focus = interactive && active && visible && Inside(x, y, mouseRect);
74 	if (ofocus != focus)
75 		Focussed();
76 }
77 
78 
TLabel(const sf::String & string,int x,int y,const sf::Color & color)79 TLabel::TLabel(const sf::String& string, int x, int y, const sf::Color& color)
80 	: TWidget(x, y, 0, 0, false)
81 	, text(string, FT.getCurrentFont(), FT.GetSize()) {
82 	if (x == CENTER)
83 		text.setPosition((Winsys.resolution.width - text.getLocalBounds().width) / 2, y);
84 	else
85 		text.setPosition(x, y);
86 	text.setFillColor(color);
87 	text.setOutlineColor(color);
88 }
89 
Focussed(bool masterFocus)90 void TLabel::Focussed(bool masterFocus) {
91 	focus = masterFocus && active;
92 	if (focus) {
93 		text.setFillColor(colDYell);
94 		text.setOutlineColor(colDYell);
95 	} else {
96 		text.setFillColor(colWhite);
97 		text.setOutlineColor(colWhite);
98 	}
99 }
100 
Draw() const101 void TLabel::Draw() const {
102 	Winsys.draw(text);
103 }
104 
GetSize() const105 sf::Vector2f TLabel::GetSize() const {
106 	return sf::Vector2f(text.getLocalBounds().width, text.getLocalBounds().height);
107 
108 }
109 
AddLabel(const sf::String & string,int x,int y,const sf::Color & color)110 TLabel* AddLabel(const sf::String& string, int x, int y, const sf::Color& color) {
111 	return static_cast<TLabel*>(AddWidget(new TLabel(string, x, y, color)));
112 }
113 
114 
TFramedText(int x,int y,int width,int height,int line,const sf::Color & backcol,const sf::String & string,unsigned int ftsize,bool borderFocus_)115 TFramedText::TFramedText(int x, int y, int width, int height, int line, const sf::Color& backcol, const sf::String& string, unsigned int ftsize, bool borderFocus_)
116 	: TWidget(x, y, width, height, false)
117 	, frame(sf::Vector2f(width - line * 2, height - line * 2))
118 	, text(string, FT.getCurrentFont(), ftsize)
119 	, borderFocus(borderFocus_) {
120 	text.setPosition(x + line + 20, y + line);
121 	if (!borderFocus) {
122 		text.setFillColor(colWhite);
123 		text.setOutlineColor(colWhite);
124 	} else {
125 		text.setFillColor(colDYell);
126 		text.setOutlineColor(colDYell);
127 	}
128 	frame.setPosition(x + line, y + line);
129 	frame.setOutlineThickness(line);
130 	frame.setFillColor(backcol);
131 	frame.setOutlineColor(colWhite);
132 }
133 
Activated()134 void TFramedText::Activated() {
135 	if (!active) {
136 		text.setFillColor(colLGrey);
137 		text.setOutlineColor(colLGrey);
138 	} else if (borderFocus || focus) {
139 		text.setFillColor(colDYell);
140 		text.setOutlineColor(colDYell);
141 	} else {
142 		text.setFillColor(colWhite);
143 		text.setOutlineColor(colWhite);
144 	}
145 }
146 
Focussed(bool masterFocus)147 void TFramedText::Focussed(bool masterFocus) {
148 	focus = masterFocus && active;
149 	if (focus) {
150 		frame.setOutlineColor(colDYell);
151 		if (!borderFocus) {
152 			text.setFillColor(colDYell);
153 			text.setOutlineColor(colDYell);
154 		}
155 	} else {
156 		frame.setOutlineColor(colWhite);
157 		if (!borderFocus) {
158 			text.setFillColor(colWhite);
159 			text.setOutlineColor(colWhite);
160 		}
161 	}
162 }
163 
Draw() const164 void TFramedText::Draw() const {
165 	Winsys.draw(frame);
166 	Winsys.draw(text);
167 }
168 
AddFramedText(int x,int y,int width,int height,int line,const sf::Color & backcol,const sf::String & text,unsigned int ftsize,bool borderFocus)169 TFramedText* AddFramedText(int x, int y, int width, int height, int line, const sf::Color& backcol, const sf::String& text, unsigned int ftsize, bool borderFocus) {
170 	return static_cast<TFramedText*>(AddWidget(new TFramedText(x, y, width, height, line, backcol, text, ftsize, borderFocus)));
171 }
172 
TTextButton(int x,int y,const sf::String & text_,int ftsize)173 TTextButton::TTextButton(int x, int y, const sf::String& text_, int ftsize)
174 	: TWidget(x, y, 0, 0)
175 	, text(text_, FT.getCurrentFont(), ftsize) {
176 	if (ftsize < 0) text.setCharacterSize(FT.AutoSizeN(4));
177 
178 	int len = text.getLocalBounds().width;
179 	if (x == CENTER) position.x = (Winsys.resolution.width - len) / 2;
180 	text.setPosition(position.x, position.y);
181 	int offs = ftsize / 5;
182 	mouseRect.left = position.x-20;
183 	mouseRect.top = position.y+offs;
184 	mouseRect.width = len+40;
185 	mouseRect.height = ftsize+offs;
186 }
187 
Focussed()188 void TTextButton::Focussed() {
189 	if (focus) {
190 		text.setFillColor(colDYell);
191 		text.setOutlineColor(colDYell);
192 	} else {
193 		text.setFillColor(colWhite);
194 		text.setOutlineColor(colWhite);
195 	}
196 }
197 
Draw() const198 void TTextButton::Draw() const {
199 	Winsys.draw(text);
200 }
201 
AddTextButton(const sf::String & text,int x,int y,int ftsize)202 TTextButton* AddTextButton(const sf::String& text, int x, int y, int ftsize) {
203 	return static_cast<TTextButton*>(AddWidget(new TTextButton(x, y, text, ftsize)));
204 }
205 
AddTextButtonN(const sf::String & text,int x,int y,int rel_ftsize)206 TTextButton* AddTextButtonN(const sf::String& text, int x, int y, int rel_ftsize) {
207 	unsigned int siz = FT.AutoSizeN(rel_ftsize);
208 	return AddTextButton(text, x, y, siz);
209 }
210 
211 
TTextField(int x,int y,int width,int height,const sf::String & text_)212 TTextField::TTextField(int x, int y, int width, int height, const sf::String& text_)
213 	: TWidget(x, y, width, height)
214 	, text(text_, FT.getCurrentFont(), FT.AutoSizeN(5))
215 	, frame(sf::Vector2f(width-6.f, height-6.f))
216 	, cursorShape(sf::Vector2f(2.f, 30.f * Winsys.scale))
217 	, maxLng(32)
218 	, time(0.0)
219 	, cursor(false) {
220 	text.setPosition(mouseRect.left + 20, mouseRect.top);
221 	cursorShape.setFillColor(colYellow);
222 	frame.setPosition(x + 3, y + 3);
223 	frame.setOutlineThickness(3);
224 	frame.setFillColor(colMBackgr);
225 	frame.setOutlineColor(colWhite);
226 	SetCursorPos(0);
227 }
228 
Draw() const229 void TTextField::Draw() const {
230 	Winsys.draw(frame);
231 	Winsys.draw(text);
232 	if (cursor && focus)
233 		Winsys.draw(cursorShape);
234 }
235 
TextEnter(char c)236 void TTextField::TextEnter(char c) {
237 	if (c != '\b') {
238 		sf::String string = text.getString();
239 		string.insert(cursorPos, c);
240 		text.setString(string);
241 		SetCursorPos(cursorPos+1);
242 	}
243 }
244 
SetCursorPos(std::size_t new_pos)245 void TTextField::SetCursorPos(std::size_t new_pos) {
246 	cursorPos = new_pos;
247 	cursorShape.setPosition(text.findCharacterPos(cursorPos).x, mouseRect.top + 9);
248 }
249 
Focussed()250 void TTextField::Focussed() {
251 	if (focus) {
252 		text.setFillColor(colDYell);
253 		text.setOutlineColor(colDYell);
254 		frame.setOutlineColor(colDYell);
255 	} else {
256 		text.setFillColor(colWhite);
257 		text.setOutlineColor(colWhite);
258 		frame.setOutlineColor(colWhite);
259 	}
260 }
261 
Click(int x,int y)262 bool TTextField::Click(int x, int y) {
263 	if (TWidget::Click(x, y)) {
264 		cursorPos = 0;
265 		float first = text.findCharacterPos(cursorPos).x;
266 		for (;;) {
267 			float second = text.findCharacterPos(cursorPos + 1).x;
268 			if ((first + second) / 2.f >= x || cursorPos >= text.getString().getSize())
269 				break;
270 			cursorPos++;
271 			first = second;
272 		}
273 		cursorShape.setPosition(text.findCharacterPos(cursorPos).x, mouseRect.top + 9);
274 		return true;
275 	}
276 	return false;
277 }
278 
eraseFromText(sf::Text & text,std::size_t pos)279 static void eraseFromText(sf::Text& text, std::size_t pos) {
280 	sf::String str = text.getString();
281 	str.erase(pos, 1);
282 	text.setString(str);
283 }
Key(sf::Keyboard::Key key,bool released)284 void TTextField::Key(sf::Keyboard::Key key, bool released) {
285 	switch (key) {
286 		case sf::Keyboard::Delete:
287 			if (cursorPos < text.getString().getSize()) eraseFromText(text, cursorPos);
288 			break;
289 		case sf::Keyboard::BackSpace:
290 			if (cursorPos > 0) { eraseFromText(text, cursorPos-1); SetCursorPos(cursorPos - 1); }
291 			break;
292 		case sf::Keyboard::Right:
293 			if (cursorPos < text.getString().getSize()) SetCursorPos(cursorPos + 1);
294 			break;
295 		case sf::Keyboard::Left:
296 			if (cursorPos > 0) SetCursorPos(cursorPos - 1);
297 			break;
298 		case sf::Keyboard::Home:
299 			SetCursorPos(0);
300 			break;
301 		case sf::Keyboard::End:
302 			SetCursorPos(text.getString().getSize());
303 			break;
304 		default:
305 			break;
306 	}
307 }
308 
UpdateCursor(float timestep)309 void TTextField::UpdateCursor(float timestep) {
310 	time += timestep;
311 	if (time > CRSR_PERIODE) {
312 		time = 0;
313 		cursor = !cursor;
314 	}
315 }
316 
AddTextField(const sf::String & text,int x,int y,int width,int height)317 TTextField* AddTextField(const sf::String& text, int x, int y, int width, int height) {
318 	locked_LR = true;
319 	return static_cast<TTextField*>(AddWidget(new TTextField(x, y, width, height, text)));
320 }
321 
TCheckbox(int x,int y,int width,const sf::String & tag_)322 TCheckbox::TCheckbox(int x, int y, int width, const sf::String& tag_)
323 	: TWidget(x, y, 32 * Winsys.scale / 0.8f, 32 * Winsys.scale / 0.8f)
324 	, text(tag_, FT.getCurrentFont(), FT.GetSize())
325 	, back(Tex.GetSFTexture(CHECKBOX))
326 	, checkmark(Tex.GetSFTexture(CHECKMARK_SMALL))
327 	, checked(false) {
328 	text.setPosition(x, y);
329 	back.setPosition(x + width - 32, y);
330 	checkmark.setPosition(x + width - 32, y);
331 	mouseRect.left = x + width - 32;
332 	back.setScale(Winsys.scale / 0.8f, Winsys.scale / 0.8f);
333 	checkmark.setScale(Winsys.scale / 0.8f, Winsys.scale / 0.8f);
334 }
335 
SetPosition(int x,int y)336 void TCheckbox::SetPosition(int x, int y) {
337 	text.setPosition(x, y);
338 	back.setPosition(x, y);
339 	checkmark.setPosition(x, y);
340 }
341 
Focussed()342 void TCheckbox::Focussed() {
343 	if (focus) {
344 		text.setFillColor(colDYell);
345 		text.setOutlineColor(colDYell);
346 	} else {
347 		text.setFillColor(colWhite);
348 		text.setOutlineColor(colWhite);
349 	}
350 }
351 
Draw() const352 void TCheckbox::Draw() const {
353 	Winsys.draw(back);
354 	if (checked)
355 		Winsys.draw(checkmark);
356 	Winsys.draw(text);
357 }
358 
Click(int x,int y)359 bool TCheckbox::Click(int x, int y) {
360 	if (active && visible && Inside(x, y, mouseRect)) {
361 		checked = !checked;
362 		return true;
363 	}
364 	return false;
365 }
366 
Key(sf::Keyboard::Key key,bool released)367 void TCheckbox::Key(sf::Keyboard::Key key, bool released) {
368 	if (released) return;
369 
370 	if (key == sf::Keyboard::Space || key == sf::Keyboard::Return) {
371 		checked = !checked;
372 	}
373 }
374 
AddCheckbox(int x,int y,int width,const sf::String & tag)375 TCheckbox* AddCheckbox(int x, int y, int width, const sf::String& tag) {
376 	return static_cast<TCheckbox*>(AddWidget(new TCheckbox(x, y, width, tag)));
377 }
378 
TIconButton(int x,int y,const sf::Texture & texture,float size_,int max_,int value_)379 TIconButton::TIconButton(int x, int y, const sf::Texture& texture, float size_, int max_, int value_)
380 	: TWidget(x, y, 32, 32)
381 	, sprite(texture)
382 	, frame(sf::Vector2f(size_, size_))
383 	, size(size_)
384 	, maximum(max_)
385 	, value(value_) {
386 	sprite.setScale(size / (texture.getSize().x / 2.f), size / (texture.getSize().y / 2.f));
387 	sprite.setPosition(x, y);
388 	frame.setPosition(x, y);
389 	frame.setOutlineColor(colWhite);
390 	frame.setOutlineThickness(3.f);
391 	SetValue(value_);
392 }
393 
SetValue(int _value)394 void TIconButton::SetValue(int _value) {
395 	value = _value;
396 	if (value > maximum)
397 		value = 0;
398 	else if (value < 0)
399 		value = maximum;
400 
401 	sf::Vector2u texSize = sprite.getTexture()->getSize();
402 	switch (value) {
403 		case 0:
404 			sprite.setTextureRect(sf::IntRect(0, 0, texSize.x / 2, texSize.y / 2));
405 			break;
406 		case 1:
407 			sprite.setTextureRect(sf::IntRect(texSize.x / 2, 0, texSize.x / 2, texSize.y / 2));
408 			break;
409 		case 2:
410 			sprite.setTextureRect(sf::IntRect(0, texSize.y / 2, texSize.x / 2, texSize.y / 2));
411 			break;
412 		case 3:
413 			sprite.setTextureRect(sf::IntRect(texSize.x / 2, texSize.y / 2, texSize.x / 2, texSize.y / 2));
414 			break;
415 	}
416 }
417 
Draw() const418 void TIconButton::Draw() const {
419 	Winsys.draw(frame);
420 	Winsys.draw(sprite);
421 }
422 
Focussed()423 void TIconButton::Focussed() {
424 	if (focus)
425 		frame.setOutlineColor(colDYell);
426 	else
427 		frame.setOutlineColor(colWhite);
428 }
429 
Click(int x,int y)430 bool TIconButton::Click(int x, int y) {
431 	if (Inside(x, y, mouseRect)) {
432 		SetValue(value + 1);
433 		return true;
434 	}
435 	return false;
436 }
437 
Key(sf::Keyboard::Key key,bool released)438 void TIconButton::Key(sf::Keyboard::Key key, bool released) {
439 	if (released) return;
440 
441 	if (key == sf::Keyboard::Down) { // Arrow down/left
442 		SetValue(value - 1);
443 	} else if (key == sf::Keyboard::Up) { // Arrow up/right
444 		SetValue(value + 1);
445 	}
446 }
447 
AddIconButton(int x,int y,const sf::Texture & texture,float size,int maximum,int value)448 TIconButton* AddIconButton(int x, int y, const sf::Texture& texture, float size, int maximum, int value) {
449 	locked_UD = true;
450 	return static_cast<TIconButton*>(AddWidget(new TIconButton(x, y, texture, size, maximum, value)));
451 }
452 
TArrow(int x,int y,bool down_)453 TArrow::TArrow(int x, int y, bool down_)
454 	: TWidget(x, y, 32 * Winsys.scale / 0.8f, 16 * Winsys.scale / 0.8f)
455 	, sprite(Tex.GetSFTexture(LB_ARROWS))
456 	, down(down_) {
457 	sprite.setPosition(x, y);
458 	sprite.setScale(Winsys.scale / 0.8f, Winsys.scale / 0.8f);
459 
460 	SetTexture();
461 }
462 
Focussed()463 void TArrow::Focussed() {
464 	SetTexture();
465 }
466 
Activated()467 void TArrow::Activated() {
468 	SetTexture();
469 }
470 
SetTexture()471 void TArrow::SetTexture() {
472 	static const float textl[6] = { 0.5f, 0.f, 0.5f, 0.5f, 0.f, 0.5f };
473 	static const float texbr[6] = { 0.5f, 0.5f, 0.f, 0.75f, 0.75f, 0.25f };
474 
475 	int type = 0;
476 	if (active)
477 		type = 1;
478 	if (focus)
479 		type++;
480 	if (down)
481 		type += 3;
482 
483 	sf::Vector2u texSize = sprite.getTexture()->getSize();
484 	sprite.setTextureRect(sf::IntRect(textl[type] * texSize.x, texbr[type] * texSize.y, texSize.x / 2, texSize.y / 4));
485 }
486 
Draw() const487 void TArrow::Draw() const {
488 	Winsys.draw(sprite);
489 }
490 
AddArrow(int x,int y,bool down)491 TArrow* AddArrow(int x, int y, bool down) {
492 	return static_cast<TArrow*>(AddWidget(new TArrow(x, y, down)));
493 }
494 
495 
TUpDown(int x,int y,int min_,int max_,int value_,int distance,bool swapArrows_)496 TUpDown::TUpDown(int x, int y, int min_, int max_, int value_, int distance, bool swapArrows_)
497 	: TWidget(x, y, 32 * Winsys.scale / 0.8f, (32 + distance)*Winsys.scale / 0.8f)
498 	, up(x, y + (16 + distance)*Winsys.scale / 0.8f, true)
499 	, down(x, y, false)
500 	, higher(swapArrows_ ? up : down)
501 	, lower(swapArrows_ ? down : up)
502 	, value(value_)
503 	, minimum(min_)
504 	, maximum(max_)
505 	, swapArrows(swapArrows_) {
506 	lower.SetActive(value < maximum);
507 	higher.SetActive(value > minimum);
508 }
509 
Draw() const510 void TUpDown::Draw() const {
511 	up.Draw();
512 	down.Draw();
513 }
514 
Click(int x,int y)515 bool TUpDown::Click(int x, int y) {
516 	if (active && visible && lower.Click(x, y)) {
517 		value++;
518 		higher.SetActive(true);
519 		if (value == maximum)
520 			lower.SetActive(false);
521 		return true;
522 	}
523 	if (active && visible && higher.Click(x, y)) {
524 		lower.SetActive(true);
525 		value--;
526 		if (value == minimum)
527 			down.SetActive(false);
528 		return true;
529 	}
530 	return false;
531 }
532 
Key(sf::Keyboard::Key key,bool released)533 void TUpDown::Key(sf::Keyboard::Key key, bool released) {
534 	if (released) return;
535 
536 	if ((!swapArrows && key == sf::Keyboard::Up) || (swapArrows && key == sf::Keyboard::Down)) { // Arrow up
537 		if (value > minimum) {
538 			value--;
539 			lower.SetActive(true);
540 			if (value == minimum)
541 				higher.SetActive(false);
542 		}
543 	} else if ((!swapArrows && key == sf::Keyboard::Down) || (swapArrows && key == sf::Keyboard::Up)) { // Arrow down
544 		if (value < maximum) {
545 			value++;
546 			higher.SetActive(true);
547 			if (value == maximum)
548 				lower.SetActive(false);
549 		}
550 	}
551 }
552 
MouseMove(int x,int y)553 void TUpDown::MouseMove(int x, int y) {
554 	bool ofocus = focus;
555 	focus = active && visible && Inside(x, y, mouseRect);
556 	if (ofocus != focus)
557 		Focussed();
558 	up.MouseMove(x, y);
559 	down.MouseMove(x, y);
560 }
561 
SetValue(int value_)562 void TUpDown::SetValue(int value_) {
563 	value = clamp(minimum, value_, maximum);
564 	lower.SetActive(value < maximum);
565 	higher.SetActive(value > minimum);
566 }
SetMinimum(int min_)567 void TUpDown::SetMinimum(int min_) {
568 	minimum = min_;
569 	value = clamp(minimum, value, maximum);
570 	lower.SetActive(value < maximum);
571 	higher.SetActive(value > minimum);
572 }
SetMaximum(int max_)573 void TUpDown::SetMaximum(int max_) {
574 	maximum = max_;
575 	value = clamp(minimum, value, maximum);
576 	lower.SetActive(value < maximum);
577 	higher.SetActive(value > minimum);
578 }
579 
AddUpDown(int x,int y,int minimum,int maximum,int value,int distance,bool swapArrows)580 TUpDown* AddUpDown(int x, int y, int minimum, int maximum, int value, int distance, bool swapArrows) {
581 	locked_UD = true;
582 	return static_cast<TUpDown*>(AddWidget(new TUpDown(x, y, minimum, maximum, value, distance, swapArrows)));
583 }
584 
585 // ------------------ Elementary drawing ---------------------------------------------
586 
DrawFrameX(int x,int y,int w,int h,int line,const sf::Color & backcol,const sf::Color & framecol,float transp)587 void DrawFrameX(int x, int y, int w, int h, int line, const sf::Color& backcol, const sf::Color& framecol, float transp) {
588 	x += line;
589 	y += line;
590 	w -= line * 2;
591 	h -= line * 2;
592 	sf::RectangleShape shape(sf::Vector2f(w, h));
593 	shape.setPosition(x, y);
594 	shape.setOutlineThickness(line);
595 	shape.setFillColor(sf::Color(backcol.r, backcol.g, backcol.b, backcol.a * transp));
596 	shape.setOutlineColor(sf::Color(framecol.r, framecol.g, framecol.b, framecol.a * transp));
597 	Winsys.draw(shape);
598 }
599 
DrawBonusExt(int y,std::size_t numraces,std::size_t num)600 void DrawBonusExt(int y, std::size_t numraces, std::size_t num) {
601 	std::size_t maxtux = numraces * 3;
602 	if (num > maxtux) return;
603 
604 	static const sf::Color col2(115, 166, 217);
605 
606 	int lleft[3];
607 
608 	int framewidth = (int)numraces * 40 + 8;
609 	int totalwidth = framewidth * 3 + 8;
610 	int xleft = (Winsys.resolution.width - totalwidth) / 2;
611 	lleft[0] = xleft;
612 	lleft[1] = xleft + framewidth + 4;
613 	lleft[2] = xleft + framewidth + framewidth + 8;
614 
615 	DrawFrameX(lleft[0], y, framewidth, 40, 1, col2, colBlack, 1);
616 	DrawFrameX(lleft[1], y, framewidth, 40, 1, col2, colBlack, 1);
617 	DrawFrameX(lleft[2], y, framewidth, 40, 1, col2, colBlack, 1);
618 
619 	static sf::Sprite tuxbonus(Tex.GetSFTexture(TUXBONUS));
620 	sf::Vector2u size = tuxbonus.getTexture()->getSize();
621 	tuxbonus.setTextureRect(sf::IntRect(0, 0, size.x, size.y/2));
622 
623 	for (std::size_t i=0; i<maxtux; i++) {
624 		std::size_t majr = (i/numraces);
625 		std::size_t minr = i - majr * numraces;
626 		if (majr > 2) majr = 2;
627 		int x = lleft[majr] + (int)minr * 40 + 6;
628 
629 		if (i<num) {
630 			tuxbonus.setPosition(x, y + 4);
631 			Winsys.draw(tuxbonus);
632 		}
633 	}
634 }
635 
DrawGUIFrame()636 void DrawGUIFrame() {
637 	static sf::Sprite bottom_left(Tex.GetSFTexture(BOTTOM_LEFT));
638 	static sf::Sprite bottom_right(Tex.GetSFTexture(BOTTOM_RIGHT));
639 	static sf::Sprite top_left(Tex.GetSFTexture(TOP_LEFT));
640 	static sf::Sprite top_right(Tex.GetSFTexture(TOP_RIGHT));
641 
642 	bottom_left.setPosition(0, Winsys.resolution.height - bottom_left.getTexture()->getSize().y);
643 	bottom_right.setPosition(Winsys.resolution.width - bottom_right.getTexture()->getSize().x, Winsys.resolution.height - bottom_right.getTexture()->getSize().y);
644 	top_right.setPosition(Winsys.resolution.width - top_right.getTexture()->getSize().x, 0);
645 
646 	Winsys.draw(bottom_left);
647 	Winsys.draw(bottom_right);
648 	Winsys.draw(top_left);
649 	Winsys.draw(top_right);
650 }
651 
DrawGUIBackground(float scale)652 void DrawGUIBackground(float scale) {
653 	DrawGUIFrame();
654 
655 	static sf::Sprite logo(Tex.GetSFTexture(T_TITLE));
656 	scale *= 0.5f;
657 	logo.setScale(scale, scale);
658 	logo.setPosition((Winsys.resolution.width - logo.getTextureRect().width*scale)/2, 5);
659 	Winsys.draw(logo);
660 }
661 
DrawCursor()662 void DrawCursor() {
663 	static sf::Sprite s(Tex.GetSFTexture(MOUSECURSOR));
664 	static bool init = false;
665 	if (!init) {
666 		s.setScale((double) Winsys.resolution.width / 1400, (double) Winsys.resolution.width / 1400);
667 		init = true;
668 	}
669 	s.setPosition(cursor_pos.x, cursor_pos.y);
670 	Winsys.draw(s);
671 }
672 
673 
674 // ------------------ Main GUI functions ---------------------------------------------
675 
DrawGUI()676 void DrawGUI() {
677 	for (std::size_t i = 0; i < Widgets.size(); i++)
678 		if (Widgets[i]->GetVisible())
679 			Widgets[i]->Draw();
680 	if (param.ice_cursor)
681 		DrawCursor();
682 }
683 
ClickGUI(int x,int y)684 TWidget* ClickGUI(int x, int y) {
685 	TWidget* clicked = nullptr;
686 	for (std::size_t i = 0; i < Widgets.size(); i++) {
687 		if (Widgets[i]->Click(x, y)) {
688 			clicked = Widgets[i];
689 			lock_focussed = focussed;
690 		}
691 	}
692 	return clicked;
693 }
694 
MouseMoveGUI(int x,int y)695 TWidget* MouseMoveGUI(int x, int y) {
696 	if (x != 0 || y != 0) {
697 		focussed = -1;
698 		for (std::size_t i = 0; i < Widgets.size(); i++) {
699 			Widgets[i]->MouseMove(cursor_pos.x, cursor_pos.y);
700 			if (Widgets[i]->focussed())
701 				focussed = (int)i;
702 		}
703 	}
704 	if (focussed == -1) {
705 		focussed = lock_focussed;
706 		if (focussed != -1) {
707 			Widgets[focussed]->focus = true;
708 			Widgets[focussed]->Focussed();
709 		}
710 		return 0;
711 	}
712 
713 	return Widgets[focussed];
714 }
715 
KeyGUI(sf::Keyboard::Key key,bool released)716 TWidget* KeyGUI(sf::Keyboard::Key key, bool released) {
717 	if (!released) {
718 		switch (key) {
719 			case sf::Keyboard::Tab:
720 				if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::RShift))
721 					DecreaseFocus();
722 				else
723 					IncreaseFocus();
724 				break;
725 			case sf::Keyboard::Up:
726 				if (!locked_UD)
727 					DecreaseFocus();
728 				break;
729 			case sf::Keyboard::Left:
730 				if (!locked_LR)
731 					DecreaseFocus();
732 				break;
733 			case sf::Keyboard::Down:
734 				if (!locked_UD)
735 					IncreaseFocus();
736 				break;
737 			case sf::Keyboard::Right:
738 				if (!locked_LR)
739 					IncreaseFocus();
740 				break;
741 			default:
742 				break;
743 		}
744 	}
745 	if (focussed == -1)
746 		return 0;
747 	Widgets[focussed]->Key(key, released);
748 	return Widgets[focussed];
749 }
750 
TextEnterGUI(char text)751 TWidget* TextEnterGUI(char text) {
752 	if (focussed == -1)
753 		return 0;
754 	Widgets[focussed]->TextEnter(text);
755 	return Widgets[focussed];
756 }
757 
SetFocus(TWidget * widget)758 void SetFocus(TWidget* widget) {
759 	if (!widget)
760 		focussed = -1;
761 	else
762 		for (std::size_t i = 0; i < Widgets.size(); i++) {
763 			if (Widgets[i] == widget) {
764 				Widgets[i]->focus = true;
765 				Widgets[i]->Focussed();
766 				focussed = (int)i;
767 				break;
768 			} else if (Widgets[i]->focus) {
769 				Widgets[i]->focus = false;
770 				Widgets[i]->Focussed();
771 			}
772 		}
773 }
774 
IncreaseFocus()775 void IncreaseFocus() {
776 	if (focussed >= 0) {
777 		Widgets[focussed]->focus = false;
778 		Widgets[focussed]->Focussed();
779 	}
780 
781 	focussed++;
782 	if (focussed >= (int)Widgets.size())
783 		focussed = 0;
784 	int end = focussed;
785 	// Select only active widgets
786 	do {
787 		if (Widgets[focussed]->GetActive() && Widgets[focussed]->GetInteractive())
788 			break;
789 
790 		focussed++;
791 		if (focussed >= (int)Widgets.size())
792 			focussed = 0;
793 	} while (end != focussed);
794 
795 	if (focussed >= 0) {
796 		Widgets[focussed]->focus = true;
797 		Widgets[focussed]->Focussed();
798 	}
799 	lock_focussed = focussed;
800 }
DecreaseFocus()801 void DecreaseFocus() {
802 	if (focussed >= 0) {
803 		Widgets[focussed]->focus = false;
804 		Widgets[focussed]->Focussed();
805 	}
806 
807 	if (focussed > 0)
808 		focussed--;
809 	else
810 		focussed = (int)Widgets.size()-1;
811 	int end = focussed;
812 	// Select only active widgets
813 	do {
814 		if (Widgets[focussed]->GetActive() && Widgets[focussed]->GetInteractive())
815 			break;
816 
817 		if (focussed > 0)
818 			focussed--;
819 		else
820 			focussed = (int)Widgets.size()-1;
821 	} while (end != focussed);
822 
823 	if (focussed >= 0) {
824 		Widgets[focussed]->focus = true;
825 		Widgets[focussed]->Focussed();
826 	}
827 	lock_focussed = focussed;
828 }
829 
ResetGUI()830 void ResetGUI() {
831 	for (std::size_t i = 0; i < Widgets.size(); i++)
832 		delete Widgets[i];
833 	Widgets.clear();
834 	focussed = 0;
835 	lock_focussed = -1;
836 	locked_LR = locked_UD = false;
837 }
838 
839 // ---------------------------------------------------------------
840 
AutoYPosN(int percent)841 int AutoYPosN(int percent) {
842 	return Winsys.resolution.height * percent / 100;
843 }
844 
AutoAreaN(int top_perc,int bott_perc,unsigned int w)845 TArea AutoAreaN(int top_perc, int bott_perc, unsigned int w) {
846 	TArea res;
847 	res.top = AutoYPosN(top_perc);
848 	res.bottom = AutoYPosN(bott_perc);
849 	if (w > Winsys.resolution.width) w = Winsys.resolution.width;
850 	res.left = (Winsys.resolution.width - w) / 2;
851 	res.right = Winsys.resolution.width - res.left;
852 	return res;
853 }
854