OpenTTD Source 20250205-master-gfd85ab1e2c
window.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "company_func.h"
12#include "gfx_func.h"
13#include "console_func.h"
14#include "console_gui.h"
15#include "viewport_func.h"
16#include "progress.h"
17#include "blitter/factory.hpp"
18#include "zoom_func.h"
19#include "vehicle_base.h"
20#include "depot_func.h"
21#include "window_func.h"
22#include "tilehighlight_func.h"
23#include "network/network.h"
24#include "querystring_gui.h"
25#include "strings_func.h"
26#include "settings_type.h"
27#include "settings_func.h"
28#include "ini_type.h"
29#include "newgrf_debug.h"
30#include "hotkeys.h"
31#include "toolbar_gui.h"
32#include "statusbar_gui.h"
33#include "error.h"
34#include "game/game.hpp"
36#include "framerate_type.h"
38#include "news_func.h"
39#include "timer/timer.h"
40#include "timer/timer_window.h"
41
42#include "safeguards.h"
43
51
53static Window *_mouseover_last_w = nullptr;
54static Window *_last_scroll_window = nullptr;
55
57WindowList _z_windows;
58
60/* static */ std::vector<Window *> Window::closed_windows;
61
65/* static */ void Window::DeleteClosedWindows()
66{
67 for (Window *w : Window::closed_windows) delete w;
69
70 /* Remove dead entries from the window list */
71 _z_windows.remove(nullptr);
72}
73
76
77/*
78 * Window that currently has focus. - The main purpose is to generate
79 * #FocusLost events, not to give next window in z-order focus when a
80 * window is closed.
81 */
82Window *_focused_window;
83
84Point _cursorpos_drag_start;
85
86int _scrollbar_start_pos;
87int _scrollbar_size;
88uint8_t _scroller_click_timeout = 0;
89
92
94
99std::vector<WindowDesc*> *_window_descs = nullptr;
100
102std::string _windows_file;
103
105WindowDesc::WindowDesc(WindowPosition def_pos, const char *ini_key, int16_t def_width_trad, int16_t def_height_trad,
106 WindowClass window_class, WindowClass parent_class, WindowDefaultFlags flags,
107 const std::span<const NWidgetPart> nwid_parts, HotkeyList *hotkeys,
108 const std::source_location location) :
109 source_location(location),
110 default_pos(def_pos),
111 cls(window_class),
112 parent_cls(parent_class),
113 ini_key(ini_key),
114 flags(flags),
115 nwid_parts(nwid_parts),
116 hotkeys(hotkeys),
117 default_width_trad(def_width_trad),
118 default_height_trad(def_height_trad)
119{
120 if (_window_descs == nullptr) _window_descs = new std::vector<WindowDesc*>();
121 _window_descs->push_back(this);
122}
123
124WindowDesc::~WindowDesc()
125{
126 _window_descs->erase(std::ranges::find(*_window_descs, this));
127}
128
135{
136 return this->pref_width != 0 ? this->pref_width : ScaleGUITrad(this->default_width_trad);
137}
138
145{
146 return this->pref_height != 0 ? this->pref_height : ScaleGUITrad(this->default_height_trad);
147}
148
153{
154 IniFile ini;
156 for (WindowDesc *wd : *_window_descs) {
157 if (wd->ini_key == nullptr) continue;
158 IniLoadWindowSettings(ini, wd->ini_key, wd);
159 }
160}
161
165static bool DescSorter(WindowDesc * const &a, WindowDesc * const &b)
166{
167 if (a->ini_key != nullptr && b->ini_key != nullptr) return strcmp(a->ini_key, b->ini_key) < 0;
168 return a->ini_key != nullptr;
169}
170
175{
176 /* Sort the stuff to get a nice ini file on first write */
177 std::sort(_window_descs->begin(), _window_descs->end(), DescSorter);
178
179 IniFile ini;
181 for (WindowDesc *wd : *_window_descs) {
182 if (wd->ini_key == nullptr) continue;
183 IniSaveWindowSettings(ini, wd->ini_key, wd);
184 }
186}
187
192{
193 if (this->nested_root != nullptr && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != nullptr) {
195 } else {
196 /* There is no stickybox; clear the preference in case someone tried to be funny */
197 this->window_desc.pref_sticky = false;
198 }
199}
200
210int Window::GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height) const
211{
212 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
213 if (line_height < 0) line_height = wid->resize_y;
214 if (clickpos < wid->pos_y + padding) return INT_MAX;
215 return (clickpos - wid->pos_y - padding) / line_height;
216}
217
222{
223 for (auto &pair : this->widget_lookup) {
224 NWidgetBase *nwid = pair.second;
225 if (nwid->IsHighlighted()) {
226 nwid->SetHighlighted(TC_INVALID);
227 nwid->SetDirty(this);
228 }
229 }
230
232}
233
239void Window::SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
240{
241 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
242 if (nwid == nullptr) return;
243
244 nwid->SetHighlighted(highlighted_colour);
245 nwid->SetDirty(this);
246
247 if (highlighted_colour != TC_INVALID) {
248 /* If we set a highlight, the window has a highlight */
250 } else {
251 /* If we disable a highlight, check all widgets if anyone still has a highlight */
252 bool valid = false;
253 for (const auto &pair : this->widget_lookup) {
254 nwid = pair.second;
255 if (!nwid->IsHighlighted()) continue;
256
257 valid = true;
258 }
259 /* If nobody has a highlight, disable the flag on the window */
261 }
262}
263
269bool Window::IsWidgetHighlighted(WidgetID widget_index) const
270{
271 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
272 if (nwid == nullptr) return false;
273
274 return nwid->IsHighlighted();
275}
276
284void Window::OnDropdownClose(Point pt, WidgetID widget, int index, bool instant_close)
285{
286 if (widget < 0) return;
287
288 if (instant_close) {
289 /* Send event for selected option if we're still
290 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
291 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
292 this->OnDropdownSelect(widget, index);
293 }
294 }
295
296 /* Raise the dropdown button */
297 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
298 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
299 nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
300 } else {
301 this->RaiseWidget(widget);
302 }
303 this->SetWidgetDirty(widget);
304}
305
312{
313 return this->GetWidget<NWidgetScrollbar>(widnum);
314}
315
322{
323 return this->GetWidget<NWidgetScrollbar>(widnum);
324}
325
332{
333 auto query = this->querystrings.find(widnum);
334 return query != this->querystrings.end() ? query->second : nullptr;
335}
336
343{
344 auto query = this->querystrings.find(widnum);
345 return query != this->querystrings.end() ? query->second : nullptr;
346}
347
352{
353 for (auto &qs : this->querystrings) {
354 qs.second->text.UpdateSize();
355 }
356}
357
362/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
363{
364 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
365 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
366 }
367
368 return nullptr;
369}
370
375/* virtual */ Point Window::GetCaretPosition() const
376{
377 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
378 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
379 }
380
381 Point pt = {0, 0};
382 return pt;
383}
384
391/* virtual */ Rect Window::GetTextBoundingRect(const char *from, const char *to) const
392{
393 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
394 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
395 }
396
397 Rect r = {0, 0, 0, 0};
398 return r;
399}
400
406/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
407{
408 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
409 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
410 }
411
412 return -1;
413}
414
420{
421 if (_focused_window == w) return;
422
423 /* Don't focus a tooltip */
424 if (w != nullptr && w->window_class == WC_TOOLTIPS) return;
425
426 /* Invalidate focused widget */
427 if (_focused_window != nullptr) {
428 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
429 }
430
431 /* Remember which window was previously focused */
432 Window *old_focused = _focused_window;
433 _focused_window = w;
434
435 /* So we can inform it that it lost focus */
436 if (old_focused != nullptr) old_focused->OnFocusLost(false);
437 if (_focused_window != nullptr) _focused_window->OnFocus();
438}
439
446{
447 if (_focused_window == nullptr) return false;
448
449 /* The console does not have an edit box so a special case is needed. */
450 if (_focused_window->window_class == WC_CONSOLE) return true;
451
452 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
453}
454
460{
461 return _focused_window && _focused_window->window_class == WC_CONSOLE;
462}
463
468{
469 if (this->nested_focus != nullptr) {
471
472 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
473 this->nested_focus->SetDirty(this);
474 this->nested_focus = nullptr;
475 }
476}
477
484{
485 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
486 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
487
488 if (this->nested_focus != nullptr) {
489 /* Do nothing if widget_index is already focused. */
490 if (widget == this->nested_focus) return false;
491
492 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
493 this->nested_focus->SetDirty(this);
495 }
496
497 this->nested_focus = widget;
499 return true;
500}
501
506{
508}
509
514{
516}
517
522void Window::RaiseButtons(bool autoraise)
523{
524 for (auto &pair : this->widget_lookup) {
525 WidgetType type = pair.second->type;
526 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
527 if (((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
528 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
529 wid->SetLowered(false);
530 wid->SetDirty(this);
531 }
532 }
533
534 /* Special widgets without widget index */
535 {
536 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
537 if (wid != nullptr) {
538 wid->SetLowered(false);
539 wid->SetDirty(this);
540 }
541 }
542}
543
548void Window::SetWidgetDirty(WidgetID widget_index) const
549{
550 /* Sometimes this function is called before the window is even fully initialized */
551 auto it = this->widget_lookup.find(widget_index);
552 if (it == std::end(this->widget_lookup)) return;
553
554 it->second->SetDirty(this);
555}
556
563{
564 if (hotkey < 0) return ES_NOT_HANDLED;
565
566 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
567 if (nw == nullptr || nw->IsDisabled()) return ES_NOT_HANDLED;
568
569 if (nw->type == WWT_EDITBOX) {
570 if (this->IsShaded()) return ES_NOT_HANDLED;
571
572 /* Focus editbox */
573 this->SetFocusedWidget(hotkey);
574 SetFocusedWindow(this);
575 } else {
576 /* Click button */
577 this->OnClick(Point(), hotkey, 1);
578 }
579 return ES_HANDLED;
580}
581
588{
589 this->LowerWidget(widget);
590 this->SetTimeout();
591 this->SetWidgetDirty(widget);
592}
593
594static void StartWindowDrag(Window *w);
595static void StartWindowSizing(Window *w, bool to_left);
596
604static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
605{
606 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
607 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
608
609 /* Allow dropdown close flag detection to work. */
610 if (nw != nullptr) ClrBit(nw->disp_flags, NDB_DROPDOWN_CLOSED);
611
612 bool focused_widget_changed = false;
613 /* If clicked on a window that previously did not have focus */
614 if (_focused_window != w && // We already have focus, right?
615 !w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && // Don't lose focus to toolbars
616 widget_type != WWT_CLOSEBOX) { // Don't change focused window if 'X' (close button) was clicked
617 focused_widget_changed = true;
619 }
620
621 if (nw == nullptr) return; // exit if clicked outside of widgets
622
623 /* don't allow any interaction if the button has been disabled */
624 if (nw->IsDisabled()) return;
625
626 WidgetID widget_index = nw->GetIndex();
627
628 /* Clicked on a widget that is not disabled.
629 * So unless the clicked widget is the caption bar, change focus to this widget.
630 * Exception: In the OSK we always want the editbox to stay focused. */
631 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
632 /* focused_widget_changed is 'now' only true if the window this widget
633 * is in gained focus. In that case it must remain true, also if the
634 * local widget focus did not change. As such it's the logical-or of
635 * both changed states.
636 *
637 * If this is not preserved, then the OSK window would be opened when
638 * a user has the edit box focused and then click on another window and
639 * then back again on the edit box (to type some text).
640 */
641 focused_widget_changed |= w->SetFocusedWidget(widget_index);
642 }
643
644 /* Dropdown window of this widget was closed so don't process click this time. */
645 if (HasBit(nw->disp_flags, NDB_DROPDOWN_CLOSED)) return;
646
647 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
648
649 Point pt = { x, y };
650
651 switch (widget_type) {
652 case NWID_VSCROLLBAR:
653 case NWID_HSCROLLBAR:
654 ScrollbarClickHandler(w, nw, x, y);
655 break;
656
657 case WWT_EDITBOX: {
658 QueryString *query = w->GetQueryString(widget_index);
659 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
660 break;
661 }
662
663 case WWT_CLOSEBOX: // 'X'
664 w->Close();
665 return;
666
667 case WWT_CAPTION: // 'Title bar'
669 return;
670
671 case WWT_RESIZEBOX:
672 /* When the resize widget is on the left size of the window
673 * we assume that that button is used to resize to the left. */
674 StartWindowSizing(w, nw->pos_x < (w->width / 2));
675 nw->SetDirty(w);
676 return;
677
678 case WWT_DEFSIZEBOX: {
679 if (_ctrl_pressed) {
682 } else {
683 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
684 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
685
686 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
687 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
688 /* dx and dy has to go by step.. calculate it.
689 * The cast to int is necessary else dx/dy are implicitly casted to unsigned int, which won't work. */
690 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
691 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
692 ResizeWindow(w, dx, dy, false);
693 }
694
695 nw->SetLowered(true);
696 nw->SetDirty(w);
697 w->SetTimeout();
698 break;
699 }
700
701 case WWT_DEBUGBOX:
703 break;
704
705 case WWT_SHADEBOX:
706 nw->SetDirty(w);
707 w->SetShaded(!w->IsShaded());
708 return;
709
710 case WWT_STICKYBOX:
712 nw->SetDirty(w);
714 return;
715
716 default:
717 break;
718 }
719
720 /* Widget has no index, so the window is not interested in it. */
721 if (widget_index < 0) return;
722
723 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
724 if (w->IsWidgetHighlighted(widget_index)) {
725 w->SetWidgetHighlight(widget_index, TC_INVALID);
726 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
727 }
728
729 w->OnClick(pt, widget_index, click_count);
730}
731
738static void DispatchRightClickEvent(Window *w, int x, int y)
739{
740 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
741 if (wid == nullptr) return;
742
743 Point pt = { x, y };
744
745 /* No widget to handle, or the window is not interested in it. */
746 if (wid->GetIndex() >= 0) {
747 if (w->OnRightClick(pt, wid->GetIndex())) return;
748 }
749
750 /* Right-click close is enabled and there is a closebox. */
752 w->Close();
754 /* Right-click close is enabled, but excluding sticky windows. */
755 w->Close();
756 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
757 GuiShowTooltips(w, wid->GetToolTip(), TCC_RIGHT_CLICK);
758 }
759}
760
767static void DispatchHoverEvent(Window *w, int x, int y)
768{
769 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
770
771 /* No widget to handle */
772 if (wid == nullptr) return;
773
774 Point pt = { x, y };
775
776 /* Show the tooltip if there is any */
777 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
778 GuiShowTooltips(w, wid->GetToolTip(), TCC_HOVER);
779 return;
780 }
781
782 /* Widget has no index, so the window is not interested in it. */
783 if (wid->GetIndex() < 0) return;
784
785 w->OnHover(pt, wid->GetIndex());
786}
787
795static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
796{
797 if (nwid == nullptr) return;
798
799 /* Using wheel on caption/shade-box shades or unshades the window. */
800 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
801 w->SetShaded(wheel < 0);
802 return;
803 }
804
805 /* Wheeling a vertical scrollbar. */
806 if (nwid->type == NWID_VSCROLLBAR) {
807 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
808 if (sb->GetCount() > sb->GetCapacity()) {
809 if (sb->UpdatePosition(wheel)) w->SetDirty();
810 }
811 return;
812 }
813
814 /* Scroll the widget attached to the scrollbar. */
815 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
816 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
817 if (sb->UpdatePosition(wheel)) w->SetDirty();
818 }
819}
820
826static bool MayBeShown(const Window *w)
827{
828 /* If we're not modal, everything is okay. */
829 if (!HasModalProgress()) return true;
830
831 switch (w->window_class) {
832 case WC_MAIN_WINDOW:
833 case WC_MODAL_PROGRESS:
835 return true;
836
837 default:
838 return false;
839 }
840}
841
854static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
855{
857 ++it;
858 for (; !it.IsEnd(); ++it) {
859 const Window *v = *it;
860 if (MayBeShown(v) &&
861 right > v->left &&
862 bottom > v->top &&
863 left < v->left + v->width &&
864 top < v->top + v->height) {
865 /* v and rectangle intersect with each other */
866 int x;
867
868 if (left < (x = v->left)) {
869 DrawOverlappedWindow(w, left, top, x, bottom);
870 DrawOverlappedWindow(w, x, top, right, bottom);
871 return;
872 }
873
874 if (right > (x = v->left + v->width)) {
875 DrawOverlappedWindow(w, left, top, x, bottom);
876 DrawOverlappedWindow(w, x, top, right, bottom);
877 return;
878 }
879
880 if (top < (x = v->top)) {
881 DrawOverlappedWindow(w, left, top, right, x);
882 DrawOverlappedWindow(w, left, x, right, bottom);
883 return;
884 }
885
886 if (bottom > (x = v->top + v->height)) {
887 DrawOverlappedWindow(w, left, top, right, x);
888 DrawOverlappedWindow(w, left, x, right, bottom);
889 return;
890 }
891
892 return;
893 }
894 }
895
896 /* Setup blitter, and dispatch a repaint event to window *wz */
897 DrawPixelInfo *dp = _cur_dpi;
898 dp->width = right - left;
899 dp->height = bottom - top;
900 dp->left = left - w->left;
901 dp->top = top - w->top;
902 dp->pitch = _screen.pitch;
903 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
904 dp->zoom = ZOOM_LVL_MIN;
905 w->OnPaint();
906}
907
916void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
917{
918 DrawPixelInfo bk;
919 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
920
921 for (Window *w : Window::IterateFromBack()) {
922 if (MayBeShown(w) &&
923 right > w->left &&
924 bottom > w->top &&
925 left < w->left + w->width &&
926 top < w->top + w->height) {
927 /* Window w intersects with the rectangle => needs repaint */
928 DrawOverlappedWindow(w, std::max(left, w->left), std::max(top, w->top), std::min(right, w->left + w->width), std::min(bottom, w->top + w->height));
929 }
930 }
931}
932
938{
939 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
940}
941
949void Window::ReInit(int rx, int ry, bool reposition)
950{
951 this->SetDirty(); // Mark whole current window as dirty.
952
953 /* Save current size. */
954 int window_width = this->width * _gui_scale / this->scale;
955 int window_height = this->height * _gui_scale / this->scale;
956 this->scale = _gui_scale;
957
958 this->OnInit();
959 /* Re-initialize window smallest size. */
960 this->nested_root->SetupSmallestSize(this);
961 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
962 this->width = this->nested_root->smallest_x;
963 this->height = this->nested_root->smallest_y;
964 this->resize.step_width = this->nested_root->resize_x;
965 this->resize.step_height = this->nested_root->resize_y;
966
967 /* Resize as close to the original size + requested resize as possible. */
968 window_width = std::max(window_width + rx, this->width);
969 window_height = std::max(window_height + ry, this->height);
970 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
971 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
972 /* dx and dy has to go by step.. calculate it.
973 * The cast to int is necessary else dx/dy are implicitly casted to unsigned int, which won't work. */
974 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
975 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
976
977 if (reposition) {
978 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
979 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
980 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight());
981 }
982
983 ResizeWindow(this, dx, dy, true, false);
984 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
985}
986
992void Window::SetShaded(bool make_shaded)
993{
994 if (this->shade_select == nullptr) return;
995
996 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
997 if (this->shade_select->shown_plane != desired) {
998 if (make_shaded) {
999 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1000 this->unshaded_size.width = this->width;
1001 this->unshaded_size.height = this->height;
1002 this->shade_select->SetDisplayedPlane(desired);
1003 this->ReInit(0, -this->height);
1004 } else {
1005 this->shade_select->SetDisplayedPlane(desired);
1006 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1007 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1008 this->ReInit(dx, dy);
1009 }
1010 }
1011}
1012
1020{
1021 for (Window *v : Window::Iterate()) {
1022 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1023 }
1024
1025 return nullptr;
1026}
1027
1033{
1034 Window *child = this->FindChildWindow(wc);
1035 while (child != nullptr) {
1036 child->Close();
1037 child = this->FindChildWindow(wc);
1038 }
1039}
1040
1044void Window::Close([[maybe_unused]] int data)
1045{
1046 /* Don't close twice. */
1047 if (*this->z_position == nullptr) return;
1048
1049 *this->z_position = nullptr;
1050
1051 if (_thd.window_class == this->window_class &&
1052 _thd.window_number == this->window_number) {
1054 }
1055
1056 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1057 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1058
1059 /* We can't scroll the window when it's closed. */
1060 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1061
1062 /* Make sure we don't try to access non-existing query strings. */
1063 this->querystrings.clear();
1064
1065 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1066 if (_focused_window == this) {
1067 this->OnFocusLost(true);
1068 _focused_window = nullptr;
1069 }
1070
1071 this->CloseChildWindows();
1072
1073 this->SetDirty();
1074
1075 Window::closed_windows.push_back(this);
1076}
1077
1082{
1083 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1084 assert(*this->z_position == nullptr);
1085
1086 if (this->viewport != nullptr) DeleteWindowViewport(this);
1087}
1088
1096{
1097 for (Window *w : Window::Iterate()) {
1098 if (w->window_class == cls && w->window_number == number) return w;
1099 }
1100
1101 return nullptr;
1102}
1103
1111{
1112 for (Window *w : Window::Iterate()) {
1113 if (w->window_class == cls) return w;
1114 }
1115
1116 return nullptr;
1117}
1118
1125{
1127 assert(w != nullptr);
1128 return w;
1129}
1130
1137void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1138{
1139 Window *w = FindWindowById(cls, number);
1140 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1141 w->Close(data);
1142 }
1143}
1144
1150{
1151 /* Note: the container remains stable, even when deleting windows. */
1152 for (Window *w : Window::Iterate()) {
1153 if (w->window_class == cls) {
1154 w->Close(data);
1155 }
1156 }
1157}
1158
1166{
1167 /* Note: the container remains stable, even when deleting windows. */
1168 for (Window *w : Window::Iterate()) {
1169 if (w->owner == id) {
1170 w->Close();
1171 }
1172 }
1173
1174 /* Also delete the company specific windows that don't have a company-colour. */
1176}
1177
1185void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1186{
1187 for (Window *w : Window::Iterate()) {
1188 if (w->owner != old_owner) continue;
1189
1190 switch (w->window_class) {
1191 case WC_COMPANY_COLOUR:
1192 case WC_FINANCES:
1193 case WC_STATION_LIST:
1194 case WC_TRAINS_LIST:
1195 case WC_ROADVEH_LIST:
1196 case WC_SHIPS_LIST:
1197 case WC_AIRCRAFT_LIST:
1198 case WC_BUY_COMPANY:
1199 case WC_COMPANY:
1201 case WC_VEHICLE_ORDERS: // Changing owner would also require changing WindowDesc, which is not possible; however keeping the old one crashes because of missing widgets etc.. See ShowOrdersWindow().
1202 continue;
1203
1204 default:
1205 w->owner = new_owner;
1206 break;
1207 }
1208 }
1209}
1210
1211static void BringWindowToFront(Window *w, bool dirty = true);
1212
1221{
1222 Window *w = FindWindowById(cls, number);
1223
1224 if (w != nullptr) {
1225 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1226
1227 w->SetWhiteBorder();
1229 w->SetDirty();
1230 }
1231
1232 return w;
1233}
1234
1235static inline bool IsVitalWindow(const Window *w)
1236{
1237 switch (w->window_class) {
1238 case WC_MAIN_TOOLBAR:
1239 case WC_STATUS_BAR:
1240 case WC_NEWS_WINDOW:
1242 return true;
1243
1244 default:
1245 return false;
1246 }
1247}
1248
1258{
1259 assert(wc != WC_INVALID);
1260
1261 uint z_priority = 0;
1262
1263 switch (wc) {
1264 case WC_TOOLTIPS:
1265 ++z_priority;
1266 [[fallthrough]];
1267
1268 case WC_ERRMSG:
1270 ++z_priority;
1271 [[fallthrough]];
1272
1273 case WC_ENDSCREEN:
1274 ++z_priority;
1275 [[fallthrough]];
1276
1277 case WC_HIGHSCORE:
1278 ++z_priority;
1279 [[fallthrough]];
1280
1281 case WC_DROPDOWN_MENU:
1282 ++z_priority;
1283 [[fallthrough]];
1284
1285 case WC_MAIN_TOOLBAR:
1286 case WC_STATUS_BAR:
1287 ++z_priority;
1288 [[fallthrough]];
1289
1290 case WC_OSK:
1291 ++z_priority;
1292 [[fallthrough]];
1293
1294 case WC_QUERY_STRING:
1296 ++z_priority;
1297 [[fallthrough]];
1298
1300 case WC_MODAL_PROGRESS:
1302 case WC_SAVE_PRESET:
1303 ++z_priority;
1304 [[fallthrough]];
1305
1307 case WC_SAVELOAD:
1308 case WC_GAME_OPTIONS:
1309 case WC_CUSTOM_CURRENCY:
1310 case WC_NETWORK_WINDOW:
1311 case WC_GRF_PARAMETERS:
1312 case WC_SCRIPT_LIST:
1313 case WC_SCRIPT_SETTINGS:
1314 case WC_TEXTFILE:
1315 ++z_priority;
1316 [[fallthrough]];
1317
1318 case WC_CONSOLE:
1319 ++z_priority;
1320 [[fallthrough]];
1321
1322 case WC_NEWS_WINDOW:
1323 ++z_priority;
1324 [[fallthrough]];
1325
1326 default:
1327 ++z_priority;
1328 [[fallthrough]];
1329
1330 case WC_MAIN_WINDOW:
1331 return z_priority;
1332 }
1333}
1334
1341static void BringWindowToFront(Window *w, bool dirty)
1342{
1343 auto priority = GetWindowZPriority(w->window_class);
1344 WindowList::iterator dest = _z_windows.begin();
1345 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1346
1347 if (dest != w->z_position) {
1348 _z_windows.splice(dest, _z_windows, w->z_position);
1349 }
1350
1351 if (dirty) w->SetDirty();
1352}
1353
1362{
1363 /* Set up window properties; some of them are needed to set up smallest size below */
1364 this->window_class = this->window_desc.cls;
1365 this->SetWhiteBorder();
1367 this->owner = INVALID_OWNER;
1368 this->nested_focus = nullptr;
1369 this->window_number = window_number;
1370
1371 this->OnInit();
1372 /* Initialize smallest size. */
1373 this->nested_root->SetupSmallestSize(this);
1374 /* Initialize to smallest size. */
1375 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1376
1377 /* Further set up window properties,
1378 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1379 this->resize.step_width = this->nested_root->resize_x;
1380 this->resize.step_height = this->nested_root->resize_y;
1381
1382 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1383 * (so we don't interrupt typing) unless the new window has a text box. */
1384 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1385 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1386 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1387
1388 /* Insert the window into the correct location in the z-ordering. */
1389 BringWindowToFront(this, false);
1390}
1391
1399void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1400{
1401 this->left = x;
1402 this->top = y;
1403 this->width = sm_width;
1404 this->height = sm_height;
1405}
1406
1417void Window::FindWindowPlacementAndResize(int def_width, int def_height)
1418{
1419 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1420 def_height = std::max(def_height, this->height);
1421 /* Try to make windows smaller when our window is too small.
1422 * w->(width|height) is normally the same as min_(width|height),
1423 * but this way the GUIs can be made a little more dynamic;
1424 * one can use the same spec for multiple windows and those
1425 * can then determine the real minimum size of the window. */
1426 if (this->width != def_width || this->height != def_height) {
1427 /* Think about the overlapping toolbars when determining the minimum window size */
1428 int free_height = _screen.height;
1430 if (wt != nullptr) free_height -= wt->height;
1432 if (wt != nullptr) free_height -= wt->height;
1433
1434 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1435 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1436
1437 /* X and Y has to go by step.. calculate it.
1438 * The cast to int is necessary else x/y are implicitly casted to
1439 * unsigned int, which won't work. */
1440 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1441 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1442
1443 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1444 /* ResizeWindow() calls this->OnResize(). */
1445 } else {
1446 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1447 this->OnResize();
1448 }
1449
1450 int nx = this->left;
1451 int ny = this->top;
1452
1453 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1454
1456 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1457 nx = std::max(nx, 0);
1458
1459 if (this->viewport != nullptr) {
1460 this->viewport->left += nx - this->left;
1461 this->viewport->top += ny - this->top;
1462 }
1463 this->left = nx;
1464 this->top = ny;
1465
1466 this->SetDirty();
1467}
1468
1481static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1482{
1483 int right = width + left;
1484 int bottom = height + top;
1485
1486 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1487
1488 /* Make sure it is not obscured by any window. */
1489 for (const Window *w : Window::Iterate()) {
1490 if (w->window_class == WC_MAIN_WINDOW) continue;
1491
1492 if (right > w->left &&
1493 w->left + w->width > left &&
1494 bottom > w->top &&
1495 w->top + w->height > top) {
1496 return false;
1497 }
1498 }
1499
1500 pos.x = left;
1501 pos.y = top;
1502 return true;
1503}
1504
1517static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1518{
1519 bool rtl = _current_text_dir == TD_RTL;
1520
1521 /* Left part of the rectangle may be at most 1/4 off-screen,
1522 * right part of the rectangle may be at most 1/2 off-screen
1523 */
1524 if (rtl) {
1525 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1526 } else {
1527 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1528 }
1529
1530 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1531 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1532
1533 /* Make sure it is not obscured by any window. */
1534 for (const Window *w : Window::Iterate()) {
1535 if (w->window_class == WC_MAIN_WINDOW) continue;
1536
1537 if (left + width > w->left &&
1538 w->left + w->width > left &&
1539 top + height > w->top &&
1540 w->top + w->height > top) {
1541 return false;
1542 }
1543 }
1544
1545 pos.x = left;
1546 pos.y = top;
1547 return true;
1548}
1549
1556static Point GetAutoPlacePosition(int width, int height)
1557{
1558 Point pt;
1559
1560 bool rtl = _current_text_dir == TD_RTL;
1561
1562 /* First attempt, try top-left of the screen */
1563 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1564 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1565 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1566
1567 /* Second attempt, try around all existing windows.
1568 * The new window must be entirely on-screen, and not overlap with an existing window.
1569 * Eight starting points are tried, two at each corner.
1570 */
1571 for (const Window *w : Window::Iterate()) {
1572 if (w->window_class == WC_MAIN_WINDOW) continue;
1573
1574 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1575 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1576 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1577 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1578 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1579 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1580 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1581 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1582 }
1583
1584 /* Third attempt, try around all existing windows.
1585 * The new window may be partly off-screen, and must not overlap with an existing window.
1586 * Only four starting points are tried.
1587 */
1588 for (const Window *w : Window::Iterate()) {
1589 if (w->window_class == WC_MAIN_WINDOW) continue;
1590
1591 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1592 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1593 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1594 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1595 }
1596
1597 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1598 * of the closebox
1599 */
1600 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1601 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1602 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1603
1604restart:
1605 for (const Window *w : Window::Iterate()) {
1606 if (w->left == left && w->top == top) {
1607 left += offset_x;
1608 top += offset_y;
1609 goto restart;
1610 }
1611 }
1612
1613 pt.x = left;
1614 pt.y = top;
1615 return pt;
1616}
1617
1625{
1626 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1627 assert(w != nullptr);
1628 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1629 return pt;
1630}
1631
1649static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1650{
1651 Point pt;
1652 const Window *w;
1653
1654 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1655 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1656
1657 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1658 bool rtl = _current_text_dir == TD_RTL;
1659 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1660 pt.x = w->left + (rtl ? w->width - default_width : 0);
1661 pt.y = w->top + w->height;
1662 return pt;
1663 } else {
1664 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1665 * - Y position: closebox of parent + closebox of child + statusbar
1666 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1667 */
1668 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1669 if (w->top + 3 * indent_y < _screen.height) {
1670 pt.y = w->top + indent_y;
1671 int indent_close = NWidgetLeaf::closebox_dimension.width;
1672 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1673 if (_current_text_dir == TD_RTL) {
1674 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1675 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1676 } else {
1677 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1678 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1679 }
1680 }
1681 }
1682 }
1683
1684 switch (desc.default_pos) {
1685 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1686 return GetToolbarAlignedWindowPosition(default_width);
1687
1688 case WDP_AUTO: // Find a good automatic position for the window
1689 return GetAutoPlacePosition(default_width, default_height);
1690
1691 case WDP_CENTER: // Centre the window horizontally
1692 pt.x = (_screen.width - default_width) / 2;
1693 pt.y = (_screen.height - default_height) / 2;
1694 break;
1695
1696 case WDP_MANUAL:
1697 pt.x = 0;
1698 pt.y = 0;
1699 break;
1700
1701 default:
1702 NOT_REACHED();
1703 }
1704
1705 return pt;
1706}
1707
1708/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1709{
1711}
1712
1721{
1722 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1723 this->nested_root->FillWidgetLookup(this->widget_lookup);
1724}
1725
1731{
1732 this->InitializeData(window_number);
1733 this->ApplyDefaults();
1734 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1735 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1736 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight());
1737}
1738
1744{
1745 this->CreateNestedTree();
1746 this->FinishInitNested(window_number);
1747}
1748
1753Window::Window(WindowDesc &desc) : window_desc(desc), scale(_gui_scale), mouse_capture_widget(-1)
1754{
1755 this->z_position = _z_windows.insert(_z_windows.end(), this);
1756}
1757
1766{
1767 for (Window *w : Window::IterateFromFront()) {
1768 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1769 return w;
1770 }
1771 }
1772
1773 return nullptr;
1774}
1775
1780{
1781 IConsoleClose();
1782
1783 _focused_window = nullptr;
1784 _mouseover_last_w = nullptr;
1785 _last_scroll_window = nullptr;
1786 _scrolling_viewport = false;
1787 _mouse_hovering = false;
1788
1790 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1791 NWidgetScrollbar::InvalidateDimensionCache();
1792
1794
1796}
1797
1802{
1804
1805 for (Window *w : Window::Iterate()) w->Close();
1806
1808
1809 assert(_z_windows.empty());
1810}
1811
1816{
1819 _thd.Reset();
1820}
1821
1822static void DecreaseWindowCounters()
1823{
1824 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1825
1826 for (Window *w : Window::Iterate()) {
1827 if (_scroller_click_timeout == 0) {
1828 /* Unclick scrollbar buttons if they are pressed. */
1829 for (auto &pair : w->widget_lookup) {
1830 NWidgetBase *nwid = pair.second;
1831 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1832 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1835 w->mouse_capture_widget = -1;
1836 sb->SetDirty(w);
1837 }
1838 }
1839 }
1840 }
1841
1842 /* Handle editboxes */
1843 for (auto &pair : w->querystrings) {
1844 pair.second->HandleEditBox(w, pair.first);
1845 }
1846
1847 w->OnMouseLoop();
1848 }
1849
1850 for (Window *w : Window::Iterate()) {
1851 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1853
1854 w->OnTimeout();
1855 w->RaiseButtons(true);
1856 }
1857 }
1858}
1859
1860static void HandlePlacePresize()
1861{
1862 if (_special_mouse_mode != WSM_PRESIZE) return;
1863
1864 Window *w = _thd.GetCallbackWnd();
1865 if (w == nullptr) return;
1866
1867 Point pt = GetTileBelowCursor();
1868 if (pt.x == -1) {
1869 _thd.selend.x = -1;
1870 return;
1871 }
1872
1873 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1874}
1875
1881{
1883
1884 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1885
1886 Window *w = _thd.GetCallbackWnd();
1887 if (w != nullptr) {
1888 /* Send an event in client coordinates. */
1889 Point pt;
1890 pt.x = _cursor.pos.x - w->left;
1891 pt.y = _cursor.pos.y - w->top;
1892 if (_left_button_down) {
1893 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1894 } else {
1895 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1896 }
1897 }
1898
1899 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1900 return ES_HANDLED;
1901}
1902
1904static void HandleMouseOver()
1905{
1906 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1907
1908 /* We changed window, put an OnMouseOver event to the last window */
1909 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1910 /* Reset mouse-over coordinates of previous window */
1911 Point pt = { -1, -1 };
1913 }
1914
1915 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1917
1918 if (w != nullptr) {
1919 /* send an event in client coordinates. */
1920 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
1921 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
1922 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
1923 }
1924}
1925
1931
1942static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
1943{
1944 if (v == nullptr) return;
1945
1946 const int min_visible = rect.Height();
1947
1948 int v_bottom = v->top + v->height - 1;
1949 int v_right = v->left + v->width - 1;
1950 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
1951
1952 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
1953 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
1954
1955 /* Vertically, the rectangle is hidden behind v. */
1956 if (*nx + rect.left + min_visible < v->left) { // At left of v.
1957 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
1958 return;
1959 }
1960 if (*nx + rect.right - min_visible > v_right) { // At right of v.
1961 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
1962 return;
1963 }
1964
1965 /* Horizontally also hidden, force movement to a safe area. */
1966 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
1967 *nx = v->left - min_visible - rect.left;
1968 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
1969 *nx = v_right + min_visible - rect.right;
1970 } else {
1971 *ny = safe_y;
1972 }
1973}
1974
1982static void EnsureVisibleCaption(Window *w, int nx, int ny)
1983{
1984 /* Search for the title bar rectangle. */
1985 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
1986 if (caption != nullptr) {
1987 const Rect caption_rect = caption->GetCurrentRect();
1988
1989 const int min_visible = caption_rect.Height();
1990
1991 /* Make sure the window doesn't leave the screen */
1992 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
1993 ny = Clamp(ny, 0, _screen.height - min_visible);
1994
1995 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
1996 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
1997 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
1998 }
1999
2000 if (w->viewport != nullptr) {
2001 w->viewport->left += nx - w->left;
2002 w->viewport->top += ny - w->top;
2003 }
2004
2005 w->left = nx;
2006 w->top = ny;
2007}
2008
2019void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2020{
2021 if (delta_x != 0 || delta_y != 0) {
2022 if (clamp_to_screen) {
2023 /* Determine the new right/bottom position. If that is outside of the bounds of
2024 * the resolution clamp it in such a manner that it stays within the bounds. */
2025 int new_right = w->left + w->width + delta_x;
2026 int new_bottom = w->top + w->height + delta_y;
2027 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2028 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2029 }
2030
2031 w->SetDirty();
2032
2033 uint new_xinc = std::max(0, (w->nested_root->resize_x == 0) ? 0 : (int)(w->nested_root->current_x - w->nested_root->smallest_x) + delta_x);
2034 uint new_yinc = std::max(0, (w->nested_root->resize_y == 0) ? 0 : (int)(w->nested_root->current_y - w->nested_root->smallest_y) + delta_y);
2035 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2036 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2037
2038 w->nested_root->AssignSizePosition(ST_RESIZE, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, _current_text_dir == TD_RTL);
2039 w->width = w->nested_root->current_x;
2040 w->height = w->nested_root->current_y;
2041 }
2042
2043 EnsureVisibleCaption(w, w->left, w->top);
2044
2045 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2046 if (schedule_resize) {
2047 w->ScheduleResize();
2048 } else {
2049 w->OnResize();
2050 }
2051 w->SetDirty();
2052}
2053
2060{
2062 return (w == nullptr) ? 0 : w->top + w->height;
2063}
2064
2071{
2073 return (w == nullptr) ? _screen.height : w->top;
2074}
2075
2076static bool _dragging_window;
2077
2083{
2084 /* Get out immediately if no window is being dragged at all. */
2085 if (!_dragging_window) return ES_NOT_HANDLED;
2086
2087 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2088 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2089
2090 /* Otherwise find the window... */
2091 for (Window *w : Window::Iterate()) {
2093 /* Stop the dragging if the left mouse button was released */
2094 if (!_left_button_down) {
2096 break;
2097 }
2098
2099 w->SetDirty();
2100
2101 int x = _cursor.pos.x + _drag_delta.x;
2102 int y = _cursor.pos.y + _drag_delta.y;
2103 int nx = x;
2104 int ny = y;
2105
2109 int delta;
2110
2111 for (const Window *v : Window::Iterate()) {
2112 if (v == w) continue; // Don't snap at yourself
2113
2114 if (y + w->height > v->top && y < v->top + v->height) {
2115 /* Your left border <-> other right border */
2116 delta = abs(v->left + v->width - x);
2117 if (delta <= hsnap) {
2118 nx = v->left + v->width;
2119 hsnap = delta;
2120 }
2121
2122 /* Your right border <-> other left border */
2123 delta = abs(v->left - x - w->width);
2124 if (delta <= hsnap) {
2125 nx = v->left - w->width;
2126 hsnap = delta;
2127 }
2128 }
2129
2130 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2131 /* Your left border <-> other left border */
2132 delta = abs(v->left - x);
2133 if (delta <= hsnap) {
2134 nx = v->left;
2135 hsnap = delta;
2136 }
2137
2138 /* Your right border <-> other right border */
2139 delta = abs(v->left + v->width - x - w->width);
2140 if (delta <= hsnap) {
2141 nx = v->left + v->width - w->width;
2142 hsnap = delta;
2143 }
2144 }
2145
2146 if (x + w->width > v->left && x < v->left + v->width) {
2147 /* Your top border <-> other bottom border */
2148 delta = abs(v->top + v->height - y);
2149 if (delta <= vsnap) {
2150 ny = v->top + v->height;
2151 vsnap = delta;
2152 }
2153
2154 /* Your bottom border <-> other top border */
2155 delta = abs(v->top - y - w->height);
2156 if (delta <= vsnap) {
2157 ny = v->top - w->height;
2158 vsnap = delta;
2159 }
2160 }
2161
2162 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2163 /* Your top border <-> other top border */
2164 delta = abs(v->top - y);
2165 if (delta <= vsnap) {
2166 ny = v->top;
2167 vsnap = delta;
2168 }
2169
2170 /* Your bottom border <-> other bottom border */
2171 delta = abs(v->top + v->height - y - w->height);
2172 if (delta <= vsnap) {
2173 ny = v->top + v->height - w->height;
2174 vsnap = delta;
2175 }
2176 }
2177 }
2178 }
2179
2180 EnsureVisibleCaption(w, nx, ny);
2181
2182 w->SetDirty();
2183 return ES_HANDLED;
2185 /* Stop the sizing if the left mouse button was released */
2186 if (!_left_button_down) {
2189 w->SetDirty();
2190 break;
2191 }
2192
2193 /* Compute difference in pixels between cursor position and reference point in the window.
2194 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2195 */
2196 int x, y = _cursor.pos.y - _drag_delta.y;
2198 x = _drag_delta.x - _cursor.pos.x;
2199 } else {
2200 x = _cursor.pos.x - _drag_delta.x;
2201 }
2202
2203 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2204 if (w->resize.step_width == 0) x = 0;
2205 if (w->resize.step_height == 0) y = 0;
2206
2207 /* Check the resize button won't go past the bottom of the screen */
2208 if (w->top + w->height + y > _screen.height) {
2209 y = _screen.height - w->height - w->top;
2210 }
2211
2212 /* X and Y has to go by step.. calculate it.
2213 * The cast to int is necessary else x/y are implicitly casted to
2214 * unsigned int, which won't work. */
2215 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2216 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2217
2218 /* Check that we don't go below the minimum set size */
2219 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2220 x = w->nested_root->smallest_x - w->width;
2221 }
2222 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2223 y = w->nested_root->smallest_y - w->height;
2224 }
2225
2226 /* Window already on size */
2227 if (x == 0 && y == 0) return ES_HANDLED;
2228
2229 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2230 _drag_delta.y += y;
2231 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2232 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2233 w->SetDirty();
2234 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2235 /* ResizeWindow() below ensures marking new position as dirty. */
2236 } else {
2237 _drag_delta.x += x;
2238 }
2239
2240 /* ResizeWindow sets both pre- and after-size to dirty for redrawal */
2241 ResizeWindow(w, x, y);
2242 return ES_HANDLED;
2243 }
2244 }
2245
2246 _dragging_window = false;
2247 return ES_HANDLED;
2248}
2249
2255{
2258 _dragging_window = true;
2259
2260 _drag_delta.x = w->left - _cursor.pos.x;
2261 _drag_delta.y = w->top - _cursor.pos.y;
2262
2264}
2265
2271static void StartWindowSizing(Window *w, bool to_left)
2272{
2275 _dragging_window = true;
2276
2277 _drag_delta.x = _cursor.pos.x;
2278 _drag_delta.y = _cursor.pos.y;
2279
2281}
2282
2288{
2289 int i;
2291 bool rtl = false;
2292
2293 if (sb->type == NWID_HSCROLLBAR) {
2294 i = _cursor.pos.x - _cursorpos_drag_start.x;
2295 rtl = _current_text_dir == TD_RTL;
2296 } else {
2297 i = _cursor.pos.y - _cursorpos_drag_start.y;
2298 }
2299
2300 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
2301 if (_scroller_click_timeout == 1) {
2302 _scroller_click_timeout = 3;
2303 if (sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1)) w->SetDirty();
2304 }
2305 return;
2306 }
2307
2308 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2309 int range = sb->GetCount() - sb->GetCapacity();
2310 if (range <= 0) return;
2311
2312 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, _scrollbar_size);
2313 if (rtl) pos = range - pos;
2314 if (sb->SetPosition(pos)) w->SetDirty();
2315}
2316
2322{
2323 for (Window *w : Window::Iterate()) {
2324 if (w->mouse_capture_widget >= 0) {
2325 /* Abort if no button is clicked any more. */
2326 if (!_left_button_down) {
2328 w->mouse_capture_widget = -1;
2329 return ES_HANDLED;
2330 }
2331
2332 /* Handle scrollbar internally, or dispatch click event */
2334 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2336 } else {
2337 /* If cursor hasn't moved, there is nothing to do. */
2338 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2339
2340 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2341 w->OnClick(pt, w->mouse_capture_widget, 0);
2342 }
2343 return ES_HANDLED;
2344 }
2345 }
2346
2347 return ES_NOT_HANDLED;
2348}
2349
2355{
2356 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2357
2359
2360 /* When we don't have a last scroll window we are starting to scroll.
2361 * When the last scroll window and this are not the same we went
2362 * outside of the window and should not left-mouse scroll anymore. */
2363 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2364
2366 _cursor.fix_at = false;
2367 _scrolling_viewport = false;
2368 _last_scroll_window = nullptr;
2369 return ES_NOT_HANDLED;
2370 }
2371
2373 /* If the main window is following a vehicle, then first let go of it! */
2375 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2376 return ES_NOT_HANDLED;
2377 }
2378
2379 Point delta;
2380 if (scrollwheel_scrolling) {
2381 /* We are using scrollwheels for scrolling */
2382 /* Use the integer part for movement */
2383 delta.x = static_cast<int>(_cursor.h_wheel);
2384 delta.y = static_cast<int>(_cursor.v_wheel);
2385 /* Keep the fractional part so that subtle movement is accumulated */
2386 float temp;
2387 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2388 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2389 } else {
2391 delta.x = -_cursor.delta.x;
2392 delta.y = -_cursor.delta.y;
2393 } else {
2394 delta.x = _cursor.delta.x;
2395 delta.y = _cursor.delta.y;
2396 }
2397 }
2398
2399 /* Create a scroll-event and send it to the window */
2400 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2401
2402 _cursor.delta.x = 0;
2403 _cursor.delta.y = 0;
2404 _cursor.wheel_moved = false;
2405 return ES_HANDLED;
2406}
2407
2419{
2420 bool bring_to_front = false;
2421
2422 if (w->window_class == WC_MAIN_WINDOW ||
2423 IsVitalWindow(w) ||
2424 w->window_class == WC_TOOLTIPS ||
2426 return true;
2427 }
2428
2429 /* Use unshaded window size rather than current size for shaded windows. */
2430 int w_width = w->width;
2431 int w_height = w->height;
2432 if (w->IsShaded()) {
2433 w_width = w->unshaded_size.width;
2434 w_height = w->unshaded_size.height;
2435 }
2436
2438 ++it;
2439 for (; !it.IsEnd(); ++it) {
2440 Window *u = *it;
2441 /* A modal child will prevent the activation of the parent window */
2443 u->SetWhiteBorder();
2444 u->SetDirty();
2445 return false;
2446 }
2447
2448 if (u->window_class == WC_MAIN_WINDOW ||
2449 IsVitalWindow(u) ||
2450 u->window_class == WC_TOOLTIPS ||
2452 continue;
2453 }
2454
2455 /* Window sizes don't interfere, leave z-order alone */
2456 if (w->left + w_width <= u->left ||
2457 u->left + u->width <= w->left ||
2458 w->top + w_height <= u->top ||
2459 u->top + u->height <= w->top) {
2460 continue;
2461 }
2462
2463 bring_to_front = true;
2464 }
2465
2466 if (bring_to_front) BringWindowToFront(w);
2467 return true;
2468}
2469
2478EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2479{
2480 QueryString *query = this->GetQueryString(wid);
2481 if (query == nullptr) return ES_NOT_HANDLED;
2482
2483 int action = QueryString::ACTION_NOTHING;
2484
2485 switch (query->text.HandleKeyPress(key, keycode)) {
2486 case HKPR_EDITING:
2487 this->SetWidgetDirty(wid);
2488 this->OnEditboxChanged(wid);
2489 break;
2490
2491 case HKPR_CURSOR:
2492 this->SetWidgetDirty(wid);
2493 /* For the OSK also invalidate the parent window */
2494 if (this->window_class == WC_OSK) this->InvalidateData();
2495 break;
2496
2497 case HKPR_CONFIRM:
2498 if (this->window_class == WC_OSK) {
2499 this->OnClick(Point(), WID_OSK_OK, 1);
2500 } else if (query->ok_button >= 0) {
2501 this->OnClick(Point(), query->ok_button, 1);
2502 } else {
2503 action = query->ok_button;
2504 }
2505 break;
2506
2507 case HKPR_CANCEL:
2508 if (this->window_class == WC_OSK) {
2509 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2510 } else if (query->cancel_button >= 0) {
2511 this->OnClick(Point(), query->cancel_button, 1);
2512 } else {
2513 action = query->cancel_button;
2514 }
2515 break;
2516
2517 case HKPR_NOT_HANDLED:
2518 return ES_NOT_HANDLED;
2519
2520 default: break;
2521 }
2522
2523 switch (action) {
2525 this->UnfocusFocusedWidget();
2526 break;
2527
2529 if (StrEmpty(query->text.GetText())) {
2530 /* If already empty, unfocus instead */
2531 this->UnfocusFocusedWidget();
2532 } else {
2533 query->text.DeleteAll();
2534 this->SetWidgetDirty(wid);
2535 this->OnEditboxChanged(wid);
2536 }
2537 break;
2538
2539 default:
2540 break;
2541 }
2542
2543 return ES_HANDLED;
2544}
2545
2550void HandleToolbarHotkey(int hotkey)
2551{
2552 assert(HasModalProgress() || IsLocalCompany());
2553
2555 if (w != nullptr) {
2556 if (w->window_desc.hotkeys != nullptr) {
2557 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2558 }
2559 }
2560}
2561
2567void HandleKeypress(uint keycode, char32_t key)
2568{
2569 /* World generation is multithreaded and messes with companies.
2570 * But there is no company related window open anyway, so _current_company is not used. */
2571 assert(HasModalProgress() || IsLocalCompany());
2572
2573 /*
2574 * The Unicode standard defines an area called the private use area. Code points in this
2575 * area are reserved for private use and thus not portable between systems. For instance,
2576 * Apple defines code points for the arrow keys in this area, but these are only printable
2577 * on a system running OS X. We don't want these keys to show up in text fields and such,
2578 * and thus we have to clear the unicode character when we encounter such a key.
2579 */
2580 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2581
2582 /*
2583 * If both key and keycode is zero, we don't bother to process the event.
2584 */
2585 if (key == 0 && keycode == 0) return;
2586
2587 /* Check if the focused window has a focused editbox */
2588 if (EditBoxInGlobalFocus()) {
2589 /* All input will in this case go to the focused editbox */
2590 if (_focused_window->window_class == WC_CONSOLE) {
2591 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2592 } else {
2593 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2594 }
2595 }
2596
2597 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2598 for (Window *w : Window::IterateFromFront()) {
2599 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2600 if (w->window_desc.hotkeys != nullptr) {
2601 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2602 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2603 }
2604 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2605 }
2606
2608 /* When there is no toolbar w is null, check for that */
2609 if (w != nullptr) {
2610 if (w->window_desc.hotkeys != nullptr) {
2611 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2612 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2613 }
2614 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2615 }
2616
2617 HandleGlobalHotkeys(key, keycode);
2618}
2619
2624{
2625 /* Call the event, start with the uppermost window. */
2626 for (Window *w : Window::IterateFromFront()) {
2627 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2628 }
2629}
2630
2636/* virtual */ void Window::InsertTextString(WidgetID wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
2637{
2638 QueryString *query = this->GetQueryString(wid);
2639 if (query == nullptr) return;
2640
2641 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2642 this->SetWidgetDirty(wid);
2643 this->OnEditboxChanged(wid);
2644 }
2645}
2646
2653void HandleTextInput(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
2654{
2655 if (!EditBoxInGlobalFocus()) return;
2656
2657 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2658}
2659
2667
2672static void HandleAutoscroll()
2673{
2674 if (_game_mode == GM_MENU || HasModalProgress()) return;
2676 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2677
2678 int x = _cursor.pos.x;
2679 int y = _cursor.pos.y;
2680 Window *w = FindWindowFromPt(x, y);
2681 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2683
2684 Viewport *vp = IsPtInWindowViewport(w, x, y);
2685 if (vp == nullptr) return;
2686
2687 x -= vp->left;
2688 y -= vp->top;
2689
2690 /* here allows scrolling in both x and y axis */
2691 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2692 static const int SCROLLSPEED = 3;
2693 if (x - 15 < 0) {
2695 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2696 } else if (15 - (vp->width - x) > 0) {
2698 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2699 }
2700 if (y - 15 < 0) {
2702 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2703 } else if (15 - (vp->height - y) > 0) {
2705 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2706 }
2707}
2708
2709enum MouseClick : uint8_t {
2710 MC_NONE = 0,
2711 MC_LEFT,
2712 MC_RIGHT,
2713 MC_DOUBLE_LEFT,
2714 MC_HOVER,
2715};
2716
2717static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2718static constexpr int MAX_OFFSET_HOVER = 5;
2719
2721
2722const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2723
2724static void ScrollMainViewport(int x, int y)
2725{
2726 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2727 Window *w = GetMainWindow();
2730 }
2731}
2732
2742static const int8_t scrollamt[16][2] = {
2743 { 0, 0},
2744 {-2, 0},
2745 { 0, -2},
2746 {-2, -1},
2747 { 2, 0},
2748 { 0, 0},
2749 { 2, -1},
2750 { 0, -2},
2751 { 0, 2},
2752 {-2, 1},
2753 { 0, 0},
2754 {-2, 0},
2755 { 2, 1},
2756 { 0, 2},
2757 { 2, 0},
2758 { 0, 0},
2759};
2760
2761static void HandleKeyScrolling()
2762{
2763 /*
2764 * Check that any of the dirkeys is pressed and that the focused window
2765 * doesn't have an edit-box as focused widget.
2766 */
2767 if (_dirkeys && !EditBoxInGlobalFocus()) {
2768 int factor = _shift_pressed ? 50 : 10;
2769
2770 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2771 /* Key scrolling stops following a vehicle. */
2773 }
2774
2775 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2776 }
2777}
2778
2779static void MouseLoop(MouseClick click, int mousewheel)
2780{
2781 /* World generation is multithreaded and messes with companies.
2782 * But there is no company related window open anyway, so _current_company is not used. */
2783 assert(HasModalProgress() || IsLocalCompany());
2784
2785 HandlePlacePresize();
2787
2788 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2789 if (HandleMouseDragDrop() == ES_HANDLED) return;
2790 if (HandleWindowDragging() == ES_HANDLED) return;
2791 if (HandleActiveWidget() == ES_HANDLED) return;
2792 if (HandleViewportScroll() == ES_HANDLED) return;
2793
2795
2796 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2797 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2798
2799 int x = _cursor.pos.x;
2800 int y = _cursor.pos.y;
2801 Window *w = FindWindowFromPt(x, y);
2802 if (w == nullptr) return;
2803
2804 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2805 Viewport *vp = IsPtInWindowViewport(w, x, y);
2806
2807 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2808 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2809
2810 if (mousewheel != 0) {
2811 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2812 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) w->OnMouseWheel(mousewheel);
2813
2814 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2815 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2816 }
2817
2818 if (vp != nullptr) {
2819 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2820 _scrolling_viewport = true;
2821 _cursor.fix_at = true;
2822 return;
2823 }
2824
2825 switch (click) {
2826 case MC_DOUBLE_LEFT:
2827 case MC_LEFT:
2828 if (HandleViewportClicked(vp, x, y)) return;
2831 _scrolling_viewport = true;
2832 _cursor.fix_at = false;
2833 return;
2834 }
2835 break;
2836
2837 case MC_RIGHT:
2840 _scrolling_viewport = true;
2843 DispatchRightClickEvent(w, x - w->left, y - w->top);
2844 return;
2845 }
2846 break;
2847
2848 default:
2849 break;
2850 }
2851 }
2852
2853 switch (click) {
2854 case MC_LEFT:
2855 case MC_DOUBLE_LEFT:
2856 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2857 return;
2858
2859 default:
2860 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2861 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2862 * Simulate a right button click so we can get started. */
2863 [[fallthrough]];
2864
2865 case MC_RIGHT:
2866 DispatchRightClickEvent(w, x - w->left, y - w->top);
2867 return;
2868
2869 case MC_HOVER:
2870 DispatchHoverEvent(w, x - w->left, y - w->top);
2871 break;
2872 }
2873
2874 /* We're not doing anything with 2D scrolling, so reset the value. */
2875 _cursor.h_wheel = 0.0f;
2876 _cursor.v_wheel = 0.0f;
2877 _cursor.wheel_moved = false;
2878}
2879
2884{
2885 /* World generation is multithreaded and messes with companies.
2886 * But there is no company related window open anyway, so _current_company is not used. */
2887 assert(HasModalProgress() || IsLocalCompany());
2888
2889 /* Handle sprite picker before any GUI interaction */
2890 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _input_events_this_tick == 0) {
2891 /* We are done with the last draw-frame, so we know what sprites we
2892 * clicked on. Reset the picker mode and invalidate the window. */
2895 }
2896
2897 static std::chrono::steady_clock::time_point double_click_time = {};
2898 static Point double_click_pos = {0, 0};
2899
2900 /* Mouse event? */
2901 MouseClick click = MC_NONE;
2903 click = MC_LEFT;
2904 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2905 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2906 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2907 click = MC_DOUBLE_LEFT;
2908 }
2909 double_click_time = std::chrono::steady_clock::now();
2910 double_click_pos = _cursor.pos;
2911 _left_button_clicked = true;
2913 } else if (_right_button_clicked) {
2914 _right_button_clicked = false;
2915 click = MC_RIGHT;
2917 }
2918
2919 int mousewheel = 0;
2920 if (_cursor.wheel) {
2921 mousewheel = _cursor.wheel;
2922 _cursor.wheel = 0;
2924 }
2925
2926 static std::chrono::steady_clock::time_point hover_time = {};
2927 static Point hover_pos = {0, 0};
2928
2930 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
2931 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
2932 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
2933 hover_pos = _cursor.pos;
2934 hover_time = std::chrono::steady_clock::now();
2935 _mouse_hovering = false;
2936 } else if (!_mouse_hovering) {
2937 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
2938 click = MC_HOVER;
2940 _mouse_hovering = true;
2941 hover_time = std::chrono::steady_clock::now();
2942 }
2943 }
2944 }
2945
2946 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
2947 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
2949 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
2951 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
2953 } else {
2954 MouseLoop(click, mousewheel);
2955 }
2956
2957 /* We have moved the mouse the required distance,
2958 * no need to move it at any later time. */
2959 _cursor.delta.x = 0;
2960 _cursor.delta.y = 0;
2961}
2962
2966static void CheckSoftLimit()
2967{
2968 if (_settings_client.gui.window_soft_limit == 0) return;
2969
2970 for (;;) {
2971 uint deletable_count = 0;
2972 Window *last_deletable = nullptr;
2973 for (Window *w : Window::IterateFromFront()) {
2974 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
2975
2976 last_deletable = w;
2977 deletable_count++;
2978 }
2979
2980 /* We've not reached the soft limit yet. */
2981 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
2982
2983 assert(last_deletable != nullptr);
2984 last_deletable->Close();
2985 }
2986}
2987
2992{
2993 /* World generation is multithreaded and messes with companies.
2994 * But there is no company related window open anyway, so _current_company is not used. */
2995 assert(HasModalProgress() || IsLocalCompany());
2996
2998
2999 /* Process scheduled window deletion. */
3001
3002 if (_input_events_this_tick != 0) {
3003 /* The input loop is called only once per GameLoop() - so we can clear the counter here */
3005 /* there were some inputs this tick, don't scroll ??? */
3006 return;
3007 }
3008
3009 /* HandleMouseEvents was already called for this tick */
3011}
3012
3017{
3018 for (Window *w : Window::Iterate()) {
3019 w->OnRealtimeTick(delta_ms);
3020 }
3021}
3022
3024static IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3025 extern int _caret_timer;
3026 _caret_timer += 3;
3027 CursorTick();
3028
3029 HandleKeyScrolling();
3031 DecreaseWindowCounters();
3032});
3033
3035static IntervalTimer<TimerWindow> highlight_interval(std::chrono::milliseconds(450), [](auto) {
3037});
3038
3040static IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3041 if (_network_dedicated) return;
3042
3043 for (Window *w : Window::Iterate()) {
3046 w->SetDirty();
3047 }
3048 }
3049});
3050
3055{
3056 static auto last_time = std::chrono::steady_clock::now();
3057 auto now = std::chrono::steady_clock::now();
3058 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3059
3060 if (delta_ms.count() == 0) return;
3061
3062 last_time = now;
3063
3066
3068
3070 CallWindowRealtimeTickEvent(delta_ms.count());
3071
3072 /* Process invalidations before anything else. */
3073 for (Window *w : Window::Iterate()) {
3077 }
3078
3079 /* Skip the actual drawing on dedicated servers without screen.
3080 * But still empty the invalidation queues above. */
3081 if (_network_dedicated) return;
3082
3084
3085 for (Window *w : Window::Iterate()) {
3086 /* Update viewport only if window is not shaded. */
3087 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3088 }
3090 /* Redraw mouse cursor in case it was hidden */
3091 DrawMouseCursor();
3092}
3093
3100{
3101 for (const Window *w : Window::Iterate()) {
3102 if (w->window_class == cls && w->window_number == number) w->SetDirty();
3103 }
3104}
3105
3113{
3114 for (const Window *w : Window::Iterate()) {
3115 if (w->window_class == cls && w->window_number == number) {
3116 w->SetWidgetDirty(widget_index);
3117 }
3118 }
3119}
3120
3126{
3127 for (const Window *w : Window::Iterate()) {
3128 if (w->window_class == cls) w->SetDirty();
3129 }
3130}
3131
3136{
3137 this->scheduled_resize = true;
3138}
3139
3144{
3145 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3146 while (this->scheduled_resize) {
3147 this->scheduled_resize = false;
3148 this->OnResize();
3149 }
3150}
3151
3157void Window::InvalidateData(int data, bool gui_scope)
3158{
3159 this->SetDirty();
3160 if (!gui_scope) {
3161 /* Schedule GUI-scope invalidation for next redraw. */
3162 this->scheduled_invalidation_data.push_back(data);
3163 }
3164 this->OnInvalidateData(data, gui_scope);
3165}
3166
3171{
3172 for (int data : this->scheduled_invalidation_data) {
3173 if (this->window_class == WC_INVALID) break;
3174 this->OnInvalidateData(data, true);
3175 }
3176 this->scheduled_invalidation_data.clear();
3177}
3178
3183{
3184 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3185
3186 for (const auto &pair : this->widget_lookup) {
3187 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3188 }
3189}
3190
3217void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3218{
3219 for (Window *w : Window::Iterate()) {
3220 if (w->window_class == cls && w->window_number == number) {
3221 w->InvalidateData(data, gui_scope);
3222 }
3223 }
3224}
3225
3234void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3235{
3236 for (Window *w : Window::Iterate()) {
3237 if (w->window_class == cls) {
3238 w->InvalidateData(data, gui_scope);
3239 }
3240 }
3241}
3242
3247{
3248 for (Window *w : Window::Iterate()) {
3249 w->OnGameTick();
3250 }
3251}
3252
3260{
3261 /* Note: the container remains stable, even when deleting windows. */
3262 for (Window *w : Window::Iterate()) {
3264 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3265
3266 w->Close();
3267 }
3268 }
3269}
3270
3279{
3280 /* Note: the container remains stable, even when closing windows. */
3281 for (Window *w : Window::Iterate()) {
3283 w->Close();
3284 }
3285 }
3286}
3287
3292{
3294 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3295 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3296 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3297}
3298
3304{
3305 /* Note: the container remains stable, even when deleting windows. */
3306 for (Window *w : Window::Iterate()) {
3308 w->Close();
3309 }
3310 }
3311
3312 for (const Window *w : Window::Iterate()) w->SetDirty();
3313}
3314
3321
3322void ReInitWindow(Window *w, bool zoom_changed)
3323{
3324 if (w == nullptr) return;
3325 if (zoom_changed) {
3326 w->nested_root->AdjustPaddingForZoom();
3328 }
3329 w->ReInit();
3330}
3331
3333void ReInitAllWindows(bool zoom_changed)
3334{
3336 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3337 NWidgetScrollbar::InvalidateDimensionCache();
3338
3340
3341 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3342 * so EnsureVisibleCaption uses the updated size information. */
3343 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3344 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3345 for (Window *w : Window::Iterate()) {
3346 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3347 ReInitWindow(w, zoom_changed);
3348 }
3349
3352
3353 /* Make sure essential parts of all windows are visible */
3354 RelocateAllWindows(_screen.width, _screen.height);
3356}
3357
3365static int PositionWindow(Window *w, WindowClass clss, int setting)
3366{
3367 if (w == nullptr || w->window_class != clss) {
3368 w = FindWindowById(clss, 0);
3369 }
3370 if (w == nullptr) return 0;
3371
3372 int old_left = w->left;
3373 switch (setting) {
3374 case 1: w->left = (_screen.width - w->width) / 2; break;
3375 case 2: w->left = _screen.width - w->width; break;
3376 default: w->left = 0; break;
3377 }
3378 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3379 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3380 return w->left;
3381}
3382
3389{
3390 Debug(misc, 5, "Repositioning Main Toolbar...");
3392}
3393
3400{
3401 Debug(misc, 5, "Repositioning statusbar...");
3403}
3404
3411{
3412 Debug(misc, 5, "Repositioning news message...");
3414}
3415
3422{
3423 Debug(misc, 5, "Repositioning network chat window...");
3425}
3426
3427
3434{
3435 for (const Window *w : Window::Iterate()) {
3436 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3437 w->viewport->follow_vehicle = to_index;
3438 w->SetDirty();
3439 }
3440 }
3441}
3442
3443
3449void RelocateAllWindows(int neww, int newh)
3450{
3452
3453 /* Reposition toolbar then status bar before other all windows. */
3454 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3455 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3456 wt->left = PositionMainToolbar(wt);
3457 }
3458
3459 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3460 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3461 ws->top = newh - ws->height;
3462 ws->left = PositionStatusbar(ws);
3463 }
3464
3465 for (Window *w : Window::Iterate()) {
3466 int left, top;
3467 /* XXX - this probably needs something more sane. For example specifying
3468 * in a 'backup'-desc that the window should always be centered. */
3469 switch (w->window_class) {
3470 case WC_MAIN_WINDOW:
3471 case WC_BOOTSTRAP:
3472 case WC_HIGHSCORE:
3473 case WC_ENDSCREEN:
3474 ResizeWindow(w, neww, newh);
3475 continue;
3476
3477 case WC_MAIN_TOOLBAR:
3478 case WC_STATUS_BAR:
3479 continue;
3480
3481 case WC_NEWS_WINDOW:
3482 top = newh - w->height;
3483 left = PositionNewsMessage(w);
3484 break;
3485
3487 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3488
3489 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3490 left = PositionNetworkChatWindow(w);
3491 break;
3492
3493 case WC_CONSOLE:
3494 IConsoleResize(w);
3495 continue;
3496
3497 default: {
3498 if (w->flags.Test(WindowFlag::Centred)) {
3499 top = (newh - w->height) >> 1;
3500 left = (neww - w->width) >> 1;
3501 break;
3502 }
3503
3504 left = w->left;
3505 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3506 if (left < 0) left = 0;
3507
3508 top = w->top;
3509 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3510 break;
3511 }
3512 }
3513
3514 EnsureVisibleCaption(w, left, top);
3515 }
3516}
3517
3522void PickerWindowBase::Close([[maybe_unused]] int data)
3523{
3525 this->Window::Close();
3526}
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:138
How all blitters should look like.
Definition base.hpp:29
virtual void * MoveTo(void *video, int x, int y)=0
Move the destination pointer the requested amount x and y, keeping in mind any pitch and bpp of the r...
Enum-as-bit-set wrapper.
constexpr EnumBitSet & Set(Tenum value)
Set the enum value.
constexpr EnumBitSet & Flip(Tenum value)
Flip the enum value.
constexpr EnumBitSet & Reset(Tenum value)
Reset the enum value to not set.
constexpr bool Test(Tenum value) const
Test if the enum value is set.
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Baseclass for nested widgets.
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:903
WidgetType type
Type of the widget / nested widget.
int pos_x
Horizontal position of top-left corner of the widget in the window.
Base class for a 'real' widget.
bool IsDisabled() const
Return whether the widget is disabled.
NWidgetDisplay disp_flags
Flags that affect display and interaction with the widget.
WidgetID GetIndex() const
Get the WidgetID of this nested widget.
Definition widget.cpp:1235
WidgetID GetScrollbarIndex() const
Get the WidgetID of this nested widget's scrollbar.
Definition widget.cpp:1244
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1208
void SetLowered(bool lowered)
Lower or raise the widget.
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2676
static Dimension resizebox_dimension
Cached size of a resizebox widget.
static Dimension closebox_dimension
Cached size of a closebox widget.
Nested widget to display and control a scrollbar in a window.
int shown_plane
Plane being displayed (for NWID_SELECTION only).
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1418
static void Reset(PerformanceElement elem)
Store the previous accumulator value and reset for a new cycle of accumulating measurements.
RAII class for measuring simple elements of performance.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:3522
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
bool UpdatePosition(int difference, ScrollbarStepping unit=SS_SMALL)
Updates the position of the first visible element by the given amount.
bool SetPosition(size_type position)
Sets the position of the first visible element.
size_type GetCount() const
Gets the number of elements in the list.
static bool Elapsed(TElapsed value)
Called when time for this timer elapsed.
virtual void EditBoxLostFocus()
An edit box lost the input focus.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
virtual void EditBoxGainedFocus()
An edit box gained the input focus.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
Functions related to companies.
bool IsLocalCompany()
Is the current company the local company?
Owner
Enum for all companies/owners.
@ INVALID_OWNER
An invalid owner.
Console functions used outside of the console code.
void IConsoleClose()
Close the in-game console.
void IConsoleResize(Window *w)
Change the size of the in-game console window after the screen size changed, or the window state chan...
GUI related functions in the console.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition debug.h:37
Functions related to depots.
void InitDepotWindowBlockSizes()
Set the size of the blocks in the window so we can be sure that they are big enough for the vehicle s...
Functions related to errors.
void UnshowCriticalError()
Unshow the critical error.
void ShowFirstError()
Show the first error of the queue.
Factory to 'query' all available blitters.
@ NO_DIRECTORY
A path without any base directory.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
void ProcessPendingPerformanceMeasurements()
This drains the PFE_SOUND measurement data queue into _pf_data.
Types for recording game performance data.
@ PFE_DRAWING
Speed of drawing world and GUI.
@ PFE_DRAWWORLD
Time spent drawing world viewports in GUI.
Base functions for all Games.
bool _shift_pressed
Is Shift pressed?
Definition gfx.cpp:39
bool _left_button_down
Is left mouse button pressed?
Definition gfx.cpp:41
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
uint8_t _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
Definition gfx.cpp:34
bool _left_button_clicked
Is left mouse button clicked?
Definition gfx.cpp:42
bool _right_button_clicked
Is right mouse button clicked?
Definition gfx.cpp:44
bool _right_button_down
Is right mouse button pressed?
Definition gfx.cpp:43
int _gui_scale
GUI scale, 100 is 100%.
Definition gfx.cpp:63
Functions related to the gfx engine.
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:243
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:294
std::unique_ptr< NWidgetBase > MakeWindowNWidgetTree(std::span< const NWidgetPart > nwid_parts, NWidgetStacked **shade_select)
Make a nested widget tree for a window from a parts array.
Definition widget.cpp:3400
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:937
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
Definition gfx.cpp:1486
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1397
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1529
Hotkey related functions.
Types related to reading/writing '*.ini' files.
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:404
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
constexpr uint Ceil(uint a, uint b)
Computes ceil(a / b) * b for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
void GuiShowTooltips(Window *parent, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
Shows a tooltip.
Definition misc_gui.cpp:740
bool _networking
are we in networking mode?
Definition network.cpp:65
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:68
Basic functions/variables used all over the place.
void NetworkDrawChatMessage()
Draw the chat message-box.
void NetworkReInitChatBoxSize()
Initialize all font-dependent chat box sizes.
void NetworkUndrawChatMessage()
Hide the chatbox.
Network functions used by other parts of OpenTTD.
Functions/types related to NewGRF debugging.
NewGrfDebugSpritePicker _newgrf_debug_sprite_picker
The sprite picker.
uint8_t valid
Bits indicating what variable is valid (for each bit, 0 is invalid, 1 is valid).
Functions related to news.
void InitNewsItemStructs()
Initialize the news-items data structures.
Definition news_gui.cpp:719
@ WID_OSK_CANCEL
Cancel key.
Definition osk_widget.h:17
@ WID_OSK_OK
Ok key.
Definition osk_widget.h:18
Functions related to modal progress.
bool HasModalProgress()
Check if we are currently in a modal progress state.
Definition progress.h:17
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
void IniLoadWindowSettings(IniFile &ini, const char *grpname, void *desc)
Load a WindowDesc from config.
Definition settings.cpp:882
void IniSaveWindowSettings(IniFile &ini, const char *grpname, void *desc)
Save a WindowDesc to config.
Definition settings.cpp:893
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
Functions related to setting/changing the settings.
Types related to global configuration settings.
@ VSM_VIEWPORT_RMB_FIXED
Viewport moves with mouse movement on holding right mouse button, cursor position is fixed.
@ VSM_MAP_RMB_FIXED
Map moves with mouse movement on holding right mouse button, cursor position is fixed.
@ VSM_MAP_LMB
Map moves with mouse movement on holding left mouse button, cursor moves.
@ SWS_SCROLL_MAP
Scroll wheel scrolls the map.
bool ScrollMainWindowTo(int x, int y, int z, bool instant)
Scrolls the main window to given coordinates.
Functions, definitions and such used only by the GUI.
@ SBI_NEWS_DELETED
abort current news display (active news were deleted)
Definition of base types and functions in a cross-platform compatible way.
bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition string_func.h:57
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
GUISettings gui
settings related to the GUI
bool fix_at
mouse is moving, but cursor is not (used for scrolling)
Definition gfx_type.h:127
Point pos
logical mouse position
Definition gfx_type.h:124
bool in_window
mouse inside this window, determines drawing logic
Definition gfx_type.h:146
int wheel
mouse wheel movement
Definition gfx_type.h:126
Point delta
relative mouse movement in this tick
Definition gfx_type.h:125
Data about how and where to blit pixels.
Definition gfx_type.h:156
uint8_t window_snap_radius
windows snap at each other if closer than this
uint16_t hover_delay_ms
time required to activate a hover event, in milliseconds
uint8_t auto_scrolling
scroll when moving mouse to the edge (see ViewportAutoscrolling)
uint8_t scroll_mode
viewport scroll mode
RightClickClose right_click_wnd_close
close window with right click
uint8_t window_soft_limit
soft limit of maximum number of non-stickied non-vital windows (0 = no limit)
uint8_t toolbar_pos
position of toolbars, 0=left, 1=center, 2=right
uint8_t statusbar_pos
position of statusbar, 0=left, 1=center, 2=right
uint8_t scrollwheel_scrolling
scrolling using the scroll wheel?
List of hotkeys for a window.
Definition hotkeys.h:37
int CheckMatch(uint16_t keycode, bool global_only=false) const
Check if a keycode is bound to something.
Definition hotkeys.cpp:309
Ini file that supports both loading and saving.
Definition ini_type.h:88
bool SaveToDisk(const std::string &filename)
Save the Ini file's data to the disk.
Definition ini.cpp:42
void LoadFromDisk(const std::string &filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition ini_load.cpp:187
NewGrfDebugSpritePickerMode mode
Current state.
void * clicked_pixel
Clicked pixel (pointer to blitter buffer)
std::vector< SpriteID > sprites
Sprites found.
Coordinates of a point in 2D.
static Titem * Get(size_t index)
Returns Titem with given index.
Data stored about a string that can be modified in the GUI.
int ok_button
Widget button of parent window to simulate when pressing OK in OSK.
static const int ACTION_DESELECT
Deselect editbox.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
ptrdiff_t GetCharAtPosition(const Window *w, WidgetID wid, const Point &pt) const
Get the character that is rendered at a position.
Definition misc_gui.cpp:898
Rect GetBoundingRect(const Window *w, WidgetID wid, const char *from, const char *to) const
Get the bounding rectangle for a range of the query string.
Definition misc_gui.cpp:868
static const int ACTION_NOTHING
Nothing.
static const int ACTION_CLEAR
Clear editbox.
Point GetCaretPosition(const Window *w, WidgetID wid) const
Get the current caret position.
Definition misc_gui.cpp:840
Specification of a rectangle with absolute coordinates of all edges.
int Height() const
Get height of Rect.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:213
uint step_width
Step-size of width resize changes.
Definition window_gui.h:212
Helper/buffer for input fields.
void UpdateSize()
Update Textbuf type with its actual physical character and screenlength Get the count of characters i...
Definition textbuf.cpp:463
void DeleteAll()
Delete every character in the textbuffer.
Definition textbuf.cpp:114
bool InsertString(const char *str, bool marked, const char *caret=nullptr, const char *insert_location=nullptr, const char *replacement_end=nullptr)
Insert a string into the text buffer.
Definition textbuf.cpp:160
const char * GetText() const
Get the current text.
Definition textbuf.cpp:290
Window * GetCallbackWnd()
Get the window that started the current highlighting.
WindowClass window_class
The WindowClass of the window that is responsible for the selection mode.
void Reset()
Reset tile highlighting.
Point selend
The location where the drag currently ends.
WindowNumber window_number
The WindowNumber of the window that is responsible for the selection mode.
Vehicle data structure.
int32_t z_pos
z coordinate.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
int32_t dest_scrollpos_y
Current destination y coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition window_gui.h:256
int32_t dest_scrollpos_x
Current destination x coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition window_gui.h:255
VehicleID follow_vehicle
VehicleID to follow if following a vehicle, INVALID_VEHICLE otherwise.
Definition window_gui.h:252
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
High level window description.
Definition window_gui.h:168
int16_t GetDefaultWidth() const
Determine default width of window.
Definition window.cpp:134
static void SaveToConfig()
Save all WindowDesc settings to _windows_file.
Definition window.cpp:174
int16_t pref_width
User-preferred width of the window. Zero if unset.
Definition window_gui.h:187
const WindowPosition default_pos
Preferred position of the window.
Definition window_gui.h:178
bool pref_sticky
Preferred stickyness.
Definition window_gui.h:186
int16_t pref_height
User-preferred height of the window. Zero if unset.
Definition window_gui.h:188
int16_t GetDefaultHeight() const
Determine default height of window.
Definition window.cpp:144
const int16_t default_height_trad
Preferred initial height of the window (pixels at 1x zoom).
Definition window_gui.h:198
const int16_t default_width_trad
Preferred initial width of the window (pixels at 1x zoom).
Definition window_gui.h:197
WindowDesc(WindowPosition default_pos, const char *ini_key, int16_t def_width_trad, int16_t def_height_trad, WindowClass window_class, WindowClass parent_class, WindowDefaultFlags flags, const std::span< const NWidgetPart > nwid_parts, HotkeyList *hotkeys=nullptr, const std::source_location location=std::source_location::current())
Window description constructor.
Definition window.cpp:105
const WindowClass cls
Class of the window,.
Definition window_gui.h:179
const char * ini_key
Key to store window defaults in openttd.cfg. nullptr if nothing shall be stored.
Definition window_gui.h:181
const WindowDefaultFlags flags
Flags.
Definition window_gui.h:182
static void LoadFromConfig()
Load all WindowDesc settings from _windows_file.
Definition window.cpp:152
const WindowClass parent_cls
Class of the parent window.
Definition window_gui.h:180
const HotkeyList * hotkeys
Hotkeys for the window.
Definition window_gui.h:184
const std::span< const NWidgetPart > nwid_parts
Span of nested widget parts describing the window.
Definition window_gui.h:183
Number to differentiate different windows of the same class.
Iterator to iterate all valid Windows.
Definition window_gui.h:861
Data structure for an opened window.
Definition window_gui.h:272
virtual const struct Textbuf * GetFocusedTextbuf() const
Get the current input text buffer.
Definition window.cpp:362
void SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
Sets the highlighted status of a widget.
Definition window.cpp:239
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:949
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1044
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:779
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1730
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:319
virtual void ApplyDefaults()
Read default values from WindowDesc configuration an apply them to the window.
Definition window.cpp:191
uint8_t white_border_timer
Timer value of the WindowFlag::WhiteBorder for flags.
Definition window_gui.h:306
NWidgetStacked * shade_select
Selection widget (NWID_SELECTION) to use for shading the window. If nullptr, window cannot shade.
Definition window_gui.h:322
void InitializePositionSize(int x, int y, int min_width, int min_height)
Set the position and smallest size of the window.
Definition window.cpp:1399
Dimension unshaded_size
Last known unshaded size (only valid while shaded).
Definition window_gui.h:323
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3157
virtual EventState OnKeyPress(char32_t key, uint16_t keycode)
A key has been pressed.
Definition window_gui.h:645
Window * parent
Parent window.
Definition window_gui.h:327
AllWindows< false > IterateFromBack
Iterate all windows in Z order from back to front.
Definition window_gui.h:921
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition window.cpp:1081
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:468
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:548
uint8_t timeout_timer
Timer value of the WindowFlag::Timeout for flags.
Definition window_gui.h:305
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
Definition window_gui.h:325
virtual void OnGameTick()
Called once per (game) tick.
Definition window_gui.h:732
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition window_gui.h:854
virtual bool OnRightClick(Point pt, WidgetID widget)
A click with the right mouse button has been made on the window.
Definition window_gui.h:672
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition window.cpp:3170
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1032
ResizeInfo resize
Resize information.
Definition window_gui.h:313
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:467
virtual void OnMouseLoop()
Called for every mouse loop run, which is at least once per (game) tick.
Definition window_gui.h:727
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition window.cpp:992
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:303
void ScheduleResize()
Mark this window as resized and in need of OnResize() event.
Definition window.cpp:3135
virtual void OnPaint()
The window must be repainted.
Definition window_gui.h:592
virtual void OnDragDrop(Point pt, WidgetID widget)
A dragged 'object' has been released.
Definition window_gui.h:701
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1720
WindowDesc & window_desc
Window description.
Definition window_gui.h:298
WindowClass window_class
Window class.
Definition window_gui.h:300
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
Definition window_gui.h:737
AllWindows< true > IterateFromFront
Iterate all windows in Z order from front to back.
Definition window_gui.h:922
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:317
void SetWhiteBorder()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:363
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:483
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:513
virtual void OnDropdownClose(Point pt, WidgetID widget, int index, bool instant_close)
A dropdown window associated to this window has been closed.
Definition window.cpp:284
static std::vector< Window * > closed_windows
List of closed windows to delete.
Definition window_gui.h:274
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition window.cpp:522
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1708
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:315
virtual void OnDropdownSelect(WidgetID widget, int index)
A dropdown option associated to this window has been selected.
Definition window_gui.h:756
virtual void OnMouseWheel(int wheel)
The mouse wheel has been turned.
Definition window_gui.h:721
virtual Point GetCaretPosition() const
Get the current caret position if an edit box has the focus.
Definition window.cpp:375
int left
x position of left edge of the window
Definition window_gui.h:308
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:556
Window * FindChildWindow(WindowClass wc=WC_INVALID) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1019
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:354
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:318
virtual void OnEditboxChanged(WidgetID widget)
The text in an editbox has been edited.
Definition window_gui.h:764
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition window.cpp:351
virtual void OnScroll(Point delta)
Handle the request for (viewport) scrolling.
Definition window_gui.h:707
int top
y position of top edge of the window
Definition window_gui.h:309
virtual void OnClick(Point pt, WidgetID widget, int click_count)
A click with the left mouse button has been made on the window.
Definition window_gui.h:663
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:331
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:321
virtual ptrdiff_t GetTextCharacterAtPosition(const Point &pt) const
Get the character that is rendered at a position by the focused edit box.
Definition window.cpp:406
std::vector< int > scheduled_invalidation_data
Data of scheduled OnInvalidateData() calls.
Definition window_gui.h:281
void InitializeData(WindowNumber window_number)
Initializes the data (except the position and initial size) of a new Window.
Definition window.cpp:1361
virtual void OnMouseOver(Point pt, WidgetID widget)
The mouse is currently moving over the window or has just moved outside of the window.
Definition window_gui.h:715
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1753
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:210
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:970
virtual bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition window_gui.h:687
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:459
virtual EventState OnCTRLStateChange()
The state of the control key has changed.
Definition window_gui.h:654
void ProcessScheduledResize()
Process scheduled OnResize() event.
Definition window.cpp:3143
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2478
virtual void InsertTextString(WidgetID wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
Insert a text string at the cursor position into the edit box widget.
Definition window.cpp:2636
virtual void OnMouseDrag(Point pt, WidgetID widget)
An 'object' is being dragged at the provided position, highlight the target if possible.
Definition window_gui.h:694
void HandleButtonClick(WidgetID widget)
Do all things to make a button look clicked and mark it to be unclicked in a few ticks.
Definition window.cpp:587
virtual void OnResize()
Called after the window got resized.
Definition window_gui.h:749
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:505
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1743
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h:742
WindowFlags flags
Window flags.
Definition window_gui.h:299
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:311
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3182
virtual Rect GetTextBoundingRect(const char *from, const char *to) const
Get the bounding rectangle for a text range if an edit box has the focus.
Definition window.cpp:391
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:562
static void DeleteClosedWindows()
Delete all closed windows.
Definition window.cpp:65
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:320
bool scheduled_resize
Set if window has been resized.
Definition window_gui.h:282
bool IsWidgetHighlighted(WidgetID widget_index) const
Gets the highlighted status of a widget.
Definition window.cpp:269
void DisableAllWidgetHighlight()
Disable the highlighted status of all widgets.
Definition window.cpp:221
virtual void OnPlacePresize(Point pt, TileIndex tile)
The user moves over the map when a tile highlight mode has been set when the special mouse mode has b...
Definition window_gui.h:838
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:920
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:311
virtual void OnHover(Point pt, WidgetID widget)
The mouse is hovering over a widget in the window, perform an action for it.
Definition window_gui.h:679
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:310
virtual void OnInit()
Notification that the nested widget tree gets initialized.
Definition window_gui.h:575
virtual void FindWindowPlacementAndResize(int def_width, int def_height)
Resize window towards the default size.
Definition window.cpp:1417
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:301
std::vector< WindowDesc * > * _window_descs
List of WindowDescs.
Definition window.cpp:99
@ HKPR_NOT_HANDLED
Key does not affect editboxes.
@ HKPR_CANCEL
Escape key pressed.
@ HKPR_EDITING
Textbuf content changed.
@ HKPR_CONFIRM
Return or enter key pressed.
@ HKPR_CURSOR
Non-text change, e.g. cursor position.
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
void UpdateTileSelection()
Updates tile highlighting for all cases.
Definition of Interval and OneShot timers.
Definition of the Window system.
uint _toolbar_width
Width of the toolbar, shared by statusbar.
Stuff related to the (main) toolbar.
Base class for all vehicles.
uint32_t VehicleID
The type all our vehicle IDs have.
static const VehicleID INVALID_VEHICLE
Constant representing a non-existing vehicle.
Base of all video drivers.
Viewport * IsPtInWindowViewport(const Window *w, int x, int y)
Is a xy position inside the viewport of the window?
Definition viewport.cpp:411
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
Update the viewport position being displayed.
Functions related to (drawing on) viewports.
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:66
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
Returns the index for the widget located at the given position relative to the window.
Definition widget.cpp:268
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:244
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:35
@ ND_SCROLLBAR_UP
Bit value of the 'scrollbar up' flag.
@ NDB_SCROLLBAR_UP
Up-button is lowered bit.
@ ND_SCROLLBAR_BTN
Bit value of the 'scrollbar up' or 'scrollbar down' flag.
@ NDB_DROPDOWN_CLOSED
Dropdown menu of the dropdown widget has closed.
@ ND_SCROLLBAR_DOWN
Bit value of the 'scrollbar down' flag.
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:37
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:74
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:57
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:60
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:39
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:63
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:75
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:56
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition widget_type.h:54
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ ST_RESIZE
Resize the nested widget tree.
@ ST_SMALLEST
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
int PositionStatusbar(Window *w)
(Re)position statusbar window at the screen.
Definition window.cpp:3399
static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
Do not allow hiding of the rectangle with base coordinates nx and ny behind window v.
Definition window.cpp:1942
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2076
void CloseConstructionWindows()
Close all windows that are used for construction of vehicle etc.
Definition window.cpp:3303
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1137
static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
Compute the position of the top-left corner of a new window that is opened.
Definition window.cpp:1649
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1124
static Point _drag_delta
delta between mouse cursor and upper left corner of dragged window
Definition window.cpp:52
static bool MayBeShown(const Window *w)
Returns whether a window may be shown or not.
Definition window.cpp:826
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1165
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2623
static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
Generate repaint events for the visible part of window w within the rectangle.
Definition window.cpp:854
bool _scrolling_viewport
A viewport is being scrolled with the mouse.
Definition window.cpp:90
void InputLoop()
Regular call from the global game loop.
Definition window.cpp:2991
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3054
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3388
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3259
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
From a rectangle that needs redrawing, find the windows that intersect with the rectangle.
Definition window.cpp:916
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2019
static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
Decide whether a given rectangle is a good place to open a mostly visible new window.
Definition window.cpp:1517
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3421
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2271
static Point GetAutoPlacePosition(int width, int height)
Find a good place for opening a new window of a given width and height.
Definition window.cpp:1556
PreventHideDirection
Direction for moving the window.
Definition window.cpp:1927
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:1929
@ PHD_UP
Above v is a safe position.
Definition window.cpp:1928
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2672
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:75
void HandleToolbarHotkey(int hotkey)
Handle Toolbar hotkey events - can come from a source like the MacBook Touch Bar.
Definition window.cpp:2550
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3365
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:419
static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
Decide whether a given rectangle is a good place to open a completely visible new window.
Definition window.cpp:1481
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2717
static int _input_events_this_tick
Local counter that is incremented each time an mouse input event is detected.
Definition window.cpp:2666
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1110
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3433
const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500)
Time between 2 left clicks before it becoming a double click.
static EventState HandleViewportScroll()
Handle viewport scrolling with the mouse.
Definition window.cpp:2354
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:795
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:604
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1765
static IntervalTimer< TimerWindow > highlight_interval(std::chrono::milliseconds(450), [](auto) { _window_highlight_colour=!_window_highlight_colour;})
Blink the window highlight colour constantly.
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3291
static Window * _last_scroll_window
Window of the last scroll event.
Definition window.cpp:54
int GetMainViewTop()
Return the top of the main view available for general use.
Definition window.cpp:2059
static void BringWindowToFront(Window *w, bool dirty=true)
On clicking on a window, make it the frontmost window of all windows with an equal or lower z-priorit...
Definition window.cpp:1341
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3333
static void EnsureVisibleCaption(Window *w, int nx, int ny)
Make sure at least a part of the caption bar is still visible by moving the window if necessary.
Definition window.cpp:1982
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1149
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2883
Point GetToolbarAlignedWindowPosition(int window_width)
Computer the position of the top-left corner of a window to be opened right under the toolbar.
Definition window.cpp:1624
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2082
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3410
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2070
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:767
void ChangeWindowOwner(Owner old_owner, Owner new_owner)
Change the owner of all the windows one company can take over from another company in the case of a c...
Definition window.cpp:1185
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2567
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3125
void HandleTextInput(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
Handle text input.
Definition window.cpp:2653
EventState VpHandlePlaceSizingDrag()
Handle the mouse while dragging for placement/resizing.
static constexpr int MAX_OFFSET_HOVER
Maximum mouse movement before stopping a hover event.
Definition window.cpp:2718
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:459
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1815
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1257
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:445
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3217
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3316
static IntervalTimer< TimerWindow > window_interval(std::chrono::milliseconds(30), [](auto) { extern int _caret_timer;_caret_timer+=3;CursorTick();HandleKeyScrolling();HandleAutoscroll();DecreaseWindowCounters();})
Update various of window-related information on a regular interval.
static bool DescSorter(WindowDesc *const &a, WindowDesc *const &b)
Sort WindowDesc by ini_key.
Definition window.cpp:165
static IntervalTimer< TimerWindow > white_border_interval(std::chrono::milliseconds(30), [](auto) { if(_network_dedicated) return;for(Window *w :Window::Iterate()) { if(w->flags.Test(WindowFlag::WhiteBorder) &&--w->white_border_timer==0) { w->flags.Reset(WindowFlag::WhiteBorder);w->SetDirty();} } })
Blink all windows marked with a white border.
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1220
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3449
std::string _windows_file
Config file to store WindowDesc.
Definition window.cpp:102
static void HandleScrollbarScrolling(Window *w)
Handle scrollbar scrolling with the mouse.
Definition window.cpp:2287
bool _mouse_hovering
The mouse is hovering over the same point.
Definition window.cpp:91
static void StartWindowDrag(Window *w)
Start window dragging.
Definition window.cpp:2254
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3246
static EventState HandleActiveWidget()
Handle active widget (mouse draggin on widget) with the mouse.
Definition window.cpp:2321
std::vector< WindowDesc * > * _window_descs
List of all WindowDescs.
Definition window.cpp:99
static void DispatchRightClickEvent(Window *w, int x, int y)
Dispatch right mouse-button click in window.
Definition window.cpp:738
ViewportAutoscrolling
Values for _settings_client.gui.auto_scrolling.
Definition window.cpp:45
@ VA_MAIN_VIEWPORT_FULLSCREEN
Scroll main viewport at edge when using fullscreen.
Definition window.cpp:47
@ VA_MAIN_VIEWPORT
Scroll main viewport at edge.
Definition window.cpp:48
@ VA_EVERY_VIEWPORT
Scroll all viewports at their edges.
Definition window.cpp:49
@ VA_DISABLED
Do not autoscroll when mouse is at edge of viewport.
Definition window.cpp:46
static const int8_t scrollamt[16][2]
Describes all the different arrow key combinations the game allows when it is in scrolling mode.
Definition window.cpp:2742
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2418
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:57
void CallWindowRealtimeTickEvent(uint delta_ms)
Dispatch OnRealtimeTick event over all windows.
Definition window.cpp:3016
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:93
static EventState HandleMouseDragDrop()
Handle dragging and dropping in mouse dragging mode (WSM_DRAGDROP).
Definition window.cpp:1880
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3278
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1904
static Window * _mouseover_last_w
Window of the last OnMouseOver event.
Definition window.cpp:53
static void CheckSoftLimit()
Check the soft limit of deletable (non vital, non sticky) windows.
Definition window.cpp:2966
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1095
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1801
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1779
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting)
Definition window.cpp:3112
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3099
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3234
Window functions not directly related to making/drawing windows.
@ Construction
This window is used for construction; close it whenever changing company.
@ NoClose
This window can't be interactively closed.
@ NoFocus
This window won't get focus/make any other window lose focus when click.
@ Modal
The window is a modal child of some other window, meaning the parent is 'inactive'.
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:419
@ SizingLeft
Window is being resized towards the left.
@ DisableVpScroll
Window does not do autoscroll,.
@ Highlighted
Window has a widget that has a highlight.
@ Centred
Window is centered and shall stay centered after ReInit.
@ Dragging
Window is being dragged.
@ SizingRight
Window is being resized towards the right.
@ WhiteBorder
Window white border counter bit mask.
@ Timeout
Window timeout counter.
@ Sticky
Window is made sticky by user.
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
Returns the index for the widget located at the given position relative to the window.
Definition widget.cpp:268
SpecialMouseMode
Mouse modes.
@ WSM_DRAGDROP
Drag&drop an object.
@ WSM_PRESIZE
Presizing mode (docks, tunnels).
WindowPosition
How do we the window to be placed?
Definition window_gui.h:143
@ WDP_CENTER
Center the window.
Definition window_gui.h:146
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:145
@ WDP_ALIGN_TOOLBAR
Align toward the toolbar.
Definition window_gui.h:147
@ WDP_MANUAL
Manually align the window (so no automatic location finding)
Definition window_gui.h:144
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:57
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
WindowClass
Window classes.
Definition window_type.h:46
@ WC_INVALID
Invalid window.
@ WC_OSK
On Screen Keyboard; Window numbers:
@ WC_CONSOLE
Console; Window numbers:
@ WC_NEWS_WINDOW
News window; Window numbers:
@ WC_HIGHSCORE
Highscore; Window numbers:
@ WC_BUY_COMPANY
Buyout company (merger); Window numbers:
@ WC_SPRITE_ALIGNER
Sprite aligner (debug); Window numbers:
@ WC_COMPANY_INFRASTRUCTURE
Company infrastructure overview; Window numbers:
@ WC_ENDSCREEN
Endscreen; Window numbers:
@ WC_COMPANY_COLOUR
Company colour selection; Window numbers:
@ WC_STATION_LIST
Station list; Window numbers:
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition window_type.h:66
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_SEND_NETWORK_MSG
Chatbox; Window numbers:
@ WC_ERRMSG
Error message; Window numbers:
@ WC_BUILD_TOOLBAR
Build toolbar; Window numbers:
Definition window_type.h:75
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_SCRIPT_SETTINGS
Script settings; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:47
@ WC_SCEN_LAND_GEN
Landscape generation (in Scenario Editor); Window numbers:
@ WC_SCRIPT_LIST
Scripts list; Window numbers:
@ WC_SAVE_PRESET
Save preset; Window numbers:
@ WC_SHIPS_LIST
Ships list; Window numbers:
@ WC_MESSAGE_HISTORY
News history list; Window numbers:
@ WC_CONFIRM_POPUP_QUERY
Popup with confirm question; Window numbers:
@ WC_GENERATE_LANDSCAPE
Generate landscape (newgame); Window numbers:
@ WC_CUSTOM_CURRENCY
Custom currency; Window numbers:
@ WC_MAIN_WINDOW
Main window; Window numbers:
Definition window_type.h:53
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers:
@ WC_NETWORK_WINDOW
Network window; Window numbers:
@ WC_FINANCES
Finances of a company; Window numbers:
@ WC_TEXTFILE
textfile; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_QUERY_STRING
Query string window; Window numbers:
@ WC_SMALLMAP
Small map; Window numbers:
@ WC_BOOTSTRAP
Bootstrap; Window numbers:
@ WC_SAVELOAD
Saveload window; Window numbers:
@ WC_GRF_PARAMETERS
NewGRF parameters; Window numbers:
@ WC_MODAL_PROGRESS
Progress report of landscape generation; Window numbers:
@ WC_MAIN_TOOLBAR
Main toolbar (the long bar at the top); Window numbers:
Definition window_type.h:60
@ WC_TOOLTIPS
Tooltip window; Window numbers:
@ WC_COMPANY
Company view; Window numbers:
@ WC_NETWORK_STATUS_WINDOW
Network status window; Window numbers:
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_MIN) When shifting right,...
Definition zoom_func.h:22
@ ZOOM_LVL_MIN
Minimum zoom level.
Definition zoom_type.h:41