OpenTTD Source 20260108-master-g8ba1860eaa
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
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 "sound_func.h"
40#include "timer/timer.h"
41#include "timer/timer_window.h"
42
43#include "table/strings.h"
44
45#include "safeguards.h"
46
54
56static Window *_mouseover_last_w = nullptr;
57static Window *_last_scroll_window = nullptr;
58
60WindowList _z_windows;
61
63/* static */ std::vector<Window *> Window::closed_windows;
64
68/* static */ void Window::DeleteClosedWindows()
69{
70 for (Window *w : Window::closed_windows) delete w;
72
73 /* Remove dead entries from the window list */
74 _z_windows.remove(nullptr);
75}
76
79
80/*
81 * Window that currently has focus. - The main purpose is to generate
82 * #FocusLost events, not to give next window in z-order focus when a
83 * window is closed.
84 */
85Window *_focused_window;
86
87Point _cursorpos_drag_start;
88
89int _scrollbar_start_pos;
90int _scrollbar_size;
91uint8_t _scroller_click_timeout = 0;
92
95
97
102std::vector<WindowDesc*> *_window_descs = nullptr;
103
105std::string _windows_file;
106
108WindowDesc::WindowDesc(WindowPosition def_pos, std::string_view ini_key, int16_t def_width_trad, int16_t def_height_trad,
109 WindowClass window_class, WindowClass parent_class, WindowDefaultFlags flags,
110 const std::span<const NWidgetPart> nwid_parts, HotkeyList *hotkeys,
111 const std::source_location location) :
112 source_location(location),
113 default_pos(def_pos),
114 cls(window_class),
115 parent_cls(parent_class),
116 ini_key(ini_key),
117 flags(flags),
118 nwid_parts(nwid_parts),
119 hotkeys(hotkeys),
120 default_width_trad(def_width_trad),
121 default_height_trad(def_height_trad)
122{
123 if (_window_descs == nullptr) _window_descs = new std::vector<WindowDesc*>();
124 _window_descs->push_back(this);
125}
126
127WindowDesc::~WindowDesc()
128{
129 _window_descs->erase(std::ranges::find(*_window_descs, this));
130}
131
138{
139 return this->pref_width != 0 ? this->pref_width : ScaleGUITrad(this->default_width_trad);
140}
141
148{
149 return this->pref_height != 0 ? this->pref_height : ScaleGUITrad(this->default_height_trad);
150}
151
156{
157 IniFile ini;
159 for (WindowDesc *wd : *_window_descs) {
160 if (wd->ini_key.empty()) continue;
161 IniLoadWindowSettings(ini, wd->ini_key, wd);
162 }
163}
164
168static bool DescSorter(WindowDesc * const &a, WindowDesc * const &b)
169{
170 return a->ini_key < b->ini_key;
171}
172
177{
178 /* Sort the stuff to get a nice ini file on first write */
179 std::sort(_window_descs->begin(), _window_descs->end(), DescSorter);
180
181 IniFile ini;
183 for (WindowDesc *wd : *_window_descs) {
184 if (wd->ini_key.empty()) continue;
185 IniSaveWindowSettings(ini, wd->ini_key, wd);
186 }
188}
189
194{
195 if (this->nested_root != nullptr && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != nullptr) {
197 } else {
198 /* There is no stickybox; clear the preference in case someone tried to be funny */
199 this->window_desc.pref_sticky = false;
200 }
201}
202
212int Window::GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height) const
213{
214 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
215 if (line_height < 0) line_height = wid->resize_y;
216 if (clickpos < wid->pos_y + padding) return INT_MAX;
217 return (clickpos - wid->pos_y - padding) / line_height;
218}
219
224{
225 for (auto &pair : this->widget_lookup) {
226 NWidgetBase *nwid = pair.second;
227 if (nwid->IsHighlighted()) {
228 nwid->SetHighlighted(TC_INVALID);
229 nwid->SetDirty(this);
230 }
231 }
232
234}
235
241void Window::SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
242{
243 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
244 if (nwid == nullptr) return;
245
246 nwid->SetHighlighted(highlighted_colour);
247 nwid->SetDirty(this);
248
249 if (highlighted_colour != TC_INVALID) {
250 /* If we set a highlight, the window has a highlight */
252 } else {
253 /* If we disable a highlight, check all widgets if anyone still has a highlight */
254 bool valid = false;
255 for (const auto &pair : this->widget_lookup) {
256 nwid = pair.second;
257 if (!nwid->IsHighlighted()) continue;
258
259 valid = true;
260 }
261 /* If nobody has a highlight, disable the flag on the window */
262 if (!valid) this->flags.Reset(WindowFlag::Highlighted);
263 }
264}
265
271bool Window::IsWidgetHighlighted(WidgetID widget_index) const
272{
273 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
274 if (nwid == nullptr) return false;
275
276 return nwid->IsHighlighted();
277}
278
286void Window::OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
287{
288 if (widget < 0) return;
289
290 /* Many dropdown selections depend on the position of the main toolbar,
291 * so if it doesn't exist (e.g. the end screen has appeared), just skip the instant close behaviour. */
292 if (instant_close && FindWindowById(WC_MAIN_TOOLBAR, 0) != nullptr) {
293 /* Send event for selected option if we're still
294 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
295 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
296 this->OnDropdownSelect(widget, index, click_result);
297 }
298 }
299
300 /* Raise the dropdown button */
301 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
302 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
304 } else {
305 this->RaiseWidget(widget);
306 }
307 this->SetWidgetDirty(widget);
308}
309
316{
317 return this->GetWidget<NWidgetScrollbar>(widnum);
318}
319
326{
327 return this->GetWidget<NWidgetScrollbar>(widnum);
328}
329
336{
337 auto query = this->querystrings.find(widnum);
338 return query != this->querystrings.end() ? query->second : nullptr;
339}
340
347{
348 auto query = this->querystrings.find(widnum);
349 return query != this->querystrings.end() ? query->second : nullptr;
350}
351
356{
357 for (auto &qs : this->querystrings) {
358 qs.second->text.UpdateSize();
359 }
360}
361
366/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
367{
368 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
369 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
370 }
371
372 return nullptr;
373}
374
379/* virtual */ Point Window::GetCaretPosition() const
380{
381 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
382 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
383 }
384
385 Point pt = {0, 0};
386 return pt;
387}
388
395/* virtual */ Rect Window::GetTextBoundingRect(size_t from, size_t to) const
396{
397 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
398 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
399 }
400
401 Rect r = {0, 0, 0, 0};
402 return r;
403}
404
410/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
411{
412 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
413 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
414 }
415
416 return -1;
417}
418
424{
425 if (_focused_window == w) return;
426
427 /* Don't focus a tooltip */
428 if (w != nullptr && w->window_class == WC_TOOLTIPS) return;
429
430 /* Invalidate focused widget */
431 if (_focused_window != nullptr) {
432 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
433 }
434
435 /* Remember which window was previously focused */
436 Window *old_focused = _focused_window;
437 _focused_window = w;
438
439 /* So we can inform it that it lost focus */
440 if (old_focused != nullptr) old_focused->OnFocusLost(false);
441 if (_focused_window != nullptr) _focused_window->OnFocus();
442}
443
450{
451 if (_focused_window == nullptr) return false;
452
453 /* The console does not have an edit box so a special case is needed. */
454 if (_focused_window->window_class == WC_CONSOLE) return true;
455
456 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
457}
458
464{
465 return _focused_window && _focused_window->window_class == WC_CONSOLE;
466}
467
472{
473 if (this->nested_focus != nullptr) {
475
476 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
477 this->nested_focus->SetDirty(this);
478 this->nested_focus = nullptr;
479 }
480}
481
488{
489 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
490 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
491
492 if (this->nested_focus != nullptr) {
493 /* Do nothing if widget_index is already focused. */
494 if (widget == this->nested_focus) return false;
495
496 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
497 this->nested_focus->SetDirty(this);
499 }
500
501 this->nested_focus = widget;
503 return true;
504}
505
506std::string Window::GetWidgetString([[maybe_unused]] WidgetID widget, StringID stringid) const
507{
508 if (stringid == STR_NULL) return {};
509 return GetString(stringid);
510}
511
516{
518}
519
524{
526}
527
532void Window::RaiseButtons(bool autoraise)
533{
534 for (auto &pair : this->widget_lookup) {
535 WidgetType type = pair.second->type;
536 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
537 if (wid != nullptr && ((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
538 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
539 wid->SetLowered(false);
540 wid->SetDirty(this);
541 }
542 }
543
544 /* Special widgets without widget index */
545 {
546 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
547 if (wid != nullptr) {
548 wid->SetLowered(false);
549 wid->SetDirty(this);
550 }
551 }
552}
553
558void Window::SetWidgetDirty(WidgetID widget_index) const
559{
560 /* Sometimes this function is called before the window is even fully initialized */
561 auto it = this->widget_lookup.find(widget_index);
562 if (it == std::end(this->widget_lookup)) return;
563
564 it->second->SetDirty(this);
565}
566
573{
574 if (hotkey < 0) return ES_NOT_HANDLED;
575
576 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
577 if (nw == nullptr || nw->IsDisabled()) return ES_NOT_HANDLED;
578
579 if (nw->type == WWT_EDITBOX) {
580 if (this->IsShaded()) return ES_NOT_HANDLED;
581
582 /* Focus editbox */
583 this->SetFocusedWidget(hotkey);
584 SetFocusedWindow(this);
585 } else {
586 /* Click button */
587 this->OnClick(Point(), hotkey, 1);
588 }
589 return ES_HANDLED;
590}
591
598{
599 /* Button click for this widget may already have been handled. */
600 if (this->IsWidgetLowered(widget) && this->timeout_timer == TIMEOUT_DURATION) return;
601
602 this->LowerWidget(widget);
603 this->SetTimeout();
604 this->SetWidgetDirty(widget);
605 SndClickBeep();
606}
607
608static void StartWindowDrag(Window *w);
609static void StartWindowSizing(Window *w, bool to_left);
610
618static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
619{
620 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
621 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
622
623 /* Allow dropdown close flag detection to work. */
625
626 bool focused_widget_changed = false;
627
628 /* If clicked on a window that previously did not have focus */
629 if (_focused_window != w) {
630 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
631 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
632 focused_widget_changed = true;
634 } else if (_focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU) {
635 /* The previously focused window was a dropdown menu, but the user clicked on another window that
636 * isn't focusable. Close the dropdown menu anyway. */
637 SetFocusedWindow(nullptr);
638 }
639 }
640
641 if (nw == nullptr) return; // exit if clicked outside of widgets
642
643 /* don't allow any interaction if the button has been disabled */
644 if (nw->IsDisabled()) return;
645
646 WidgetID widget_index = nw->GetIndex();
647
648 /* Clicked on a widget that is not disabled.
649 * So unless the clicked widget is the caption bar, change focus to this widget.
650 * Exception: In the OSK we always want the editbox to stay focused. */
651 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
652 /* focused_widget_changed is 'now' only true if the window this widget
653 * is in gained focus. In that case it must remain true, also if the
654 * local widget focus did not change. As such it's the logical-or of
655 * both changed states.
656 *
657 * If this is not preserved, then the OSK window would be opened when
658 * a user has the edit box focused and then click on another window and
659 * then back again on the edit box (to type some text).
660 */
661 focused_widget_changed |= w->SetFocusedWidget(widget_index);
662 }
663
664 /* Dropdown window of this widget was closed so don't process click this time. */
666
667 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
668
669 Point pt = { x, y };
670
671 switch (widget_type) {
672 case NWID_VSCROLLBAR:
673 case NWID_HSCROLLBAR:
674 ScrollbarClickHandler(w, nw, x, y);
675 break;
676
677 case WWT_EDITBOX: {
678 QueryString *query = w->GetQueryString(widget_index);
679 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
680 break;
681 }
682
683 case WWT_CLOSEBOX: // 'X'
684 w->Close();
685 return;
686
687 case WWT_CAPTION: // 'Title bar'
689 return;
690
691 case WWT_RESIZEBOX:
692 /* When the resize widget is on the left size of the window
693 * we assume that that button is used to resize to the left. */
694 StartWindowSizing(w, nw->pos_x < (w->width / 2));
695 nw->SetDirty(w);
696 return;
697
698 case WWT_DEFSIZEBOX: {
699 if (_ctrl_pressed) {
700 if (click_count > 1) {
701 w->window_desc.pref_width = 0;
703 } else {
706 }
707 } else {
708 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
709 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
710
711 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
712 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
713 /* dx and dy has to go by step.. calculate it.
714 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
715 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
716 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
717 ResizeWindow(w, dx, dy, false);
718 }
719
720 nw->SetLowered(true);
721 nw->SetDirty(w);
722 w->SetTimeout();
723 break;
724 }
725
726 case WWT_DEBUGBOX:
728 break;
729
730 case WWT_SHADEBOX:
731 nw->SetDirty(w);
732 w->SetShaded(!w->IsShaded());
733 return;
734
735 case WWT_STICKYBOX:
737 nw->SetDirty(w);
739 return;
740
741 default:
742 break;
743 }
744
745 /* Widget has no index, so the window is not interested in it. */
746 if (widget_index < 0) return;
747
748 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
749 if (w->IsWidgetHighlighted(widget_index)) {
750 w->SetWidgetHighlight(widget_index, TC_INVALID);
751 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
752 }
753
754 w->OnClick(pt, widget_index, click_count);
755}
756
763static void DispatchRightClickEvent(Window *w, int x, int y)
764{
765 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
766 if (wid == nullptr) return;
767
768 Point pt = { x, y };
769
770 /* No widget to handle, or the window is not interested in it. */
771 if (wid->GetIndex() >= 0) {
772 if (w->OnRightClick(pt, wid->GetIndex())) return;
773 }
774
775 /* Right-click close is enabled and there is a closebox. */
777 w->Close();
779 /* Right-click close is enabled, but excluding sticky windows. */
780 w->Close();
781 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
782 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_RIGHT_CLICK);
783 }
784}
785
792static void DispatchHoverEvent(Window *w, int x, int y)
793{
794 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
795
796 /* No widget to handle */
797 if (wid == nullptr) return;
798
799 Point pt = { x, y };
800
801 /* Show the tooltip if there is any */
802 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
803 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_HOVER);
804 return;
805 }
806
807 /* Widget has no index, so the window is not interested in it. */
808 if (wid->GetIndex() < 0) return;
809
810 w->OnHover(pt, wid->GetIndex());
811}
812
820static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
821{
822 if (nwid == nullptr) return;
823
824 /* Using wheel on caption/shade-box shades or unshades the window. */
825 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
826 w->SetShaded(wheel < 0);
827 return;
828 }
829
830 /* Wheeling a vertical scrollbar. */
831 if (nwid->type == NWID_VSCROLLBAR) {
832 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
833 if (sb->GetCount() > sb->GetCapacity()) {
834 if (sb->UpdatePosition(wheel)) {
835 w->OnScrollbarScroll(nwid->GetIndex());
836 w->SetDirty();
837 }
838 }
839 return;
840 }
841
842 /* Scroll the widget attached to the scrollbar. */
843 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
844 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
845 if (sb->UpdatePosition(wheel)) {
847 w->SetDirty();
848 }
849 }
850}
851
857static bool MayBeShown(const Window *w)
858{
859 /* If we're not modal, everything is okay. */
860 if (!HasModalProgress()) return true;
861
862 switch (w->window_class) {
863 case WC_MAIN_WINDOW:
864 case WC_MODAL_PROGRESS:
866 return true;
867
868 default:
869 return false;
870 }
871}
872
885static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
886{
888 ++it;
889 for (; !it.IsEnd(); ++it) {
890 const Window *v = *it;
891 if (MayBeShown(v) &&
892 right > v->left &&
893 bottom > v->top &&
894 left < v->left + v->width &&
895 top < v->top + v->height) {
896 /* v and rectangle intersect with each other */
897 int x;
898
899 if (left < (x = v->left)) {
900 DrawOverlappedWindow(w, left, top, x, bottom);
901 DrawOverlappedWindow(w, x, top, right, bottom);
902 return;
903 }
904
905 if (right > (x = v->left + v->width)) {
906 DrawOverlappedWindow(w, left, top, x, bottom);
907 DrawOverlappedWindow(w, x, top, right, bottom);
908 return;
909 }
910
911 if (top < (x = v->top)) {
912 DrawOverlappedWindow(w, left, top, right, x);
913 DrawOverlappedWindow(w, left, x, right, bottom);
914 return;
915 }
916
917 if (bottom > (x = v->top + v->height)) {
918 DrawOverlappedWindow(w, left, top, right, x);
919 DrawOverlappedWindow(w, left, x, right, bottom);
920 return;
921 }
922
923 return;
924 }
925 }
926
927 /* Setup blitter, and dispatch a repaint event to window *wz */
928 DrawPixelInfo *dp = _cur_dpi;
929 dp->width = right - left;
930 dp->height = bottom - top;
931 dp->left = left - w->left;
932 dp->top = top - w->top;
933 dp->pitch = _screen.pitch;
934 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
935 dp->zoom = ZoomLevel::Min;
936 w->OnPaint();
937}
938
947void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
948{
949 DrawPixelInfo bk;
950 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
951
952 for (Window *w : Window::IterateFromBack()) {
953 if (MayBeShown(w) &&
954 right > w->left &&
955 bottom > w->top &&
956 left < w->left + w->width &&
957 top < w->top + w->height) {
958 /* Window w intersects with the rectangle => needs repaint */
959 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));
960 }
961 }
962}
963
969{
970 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
971}
972
980void Window::ReInit(int rx, int ry, bool reposition)
981{
982 this->SetDirty(); // Mark whole current window as dirty.
983
984 /* Save current size. */
985 int window_width = this->width * _gui_scale / this->scale;
986 int window_height = this->height * _gui_scale / this->scale;
987 this->scale = _gui_scale;
988
989 this->OnInit();
990 /* Re-initialize window smallest size. */
991 this->nested_root->SetupSmallestSize(this);
992 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
993 this->width = this->nested_root->smallest_x;
994 this->height = this->nested_root->smallest_y;
995 this->resize.step_width = this->nested_root->resize_x;
996 this->resize.step_height = this->nested_root->resize_y;
997
998 /* Resize as close to the original size + requested resize as possible. */
999 window_width = std::max(window_width + rx, this->width);
1000 window_height = std::max(window_height + ry, this->height);
1001 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
1002 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
1003 /* dx and dy has to go by step.. calculate it.
1004 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1005 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1006 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1007
1008 if (reposition) {
1009 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1010 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1011 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1012 }
1013
1014 ResizeWindow(this, dx, dy, true, false);
1015 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1016}
1017
1023void Window::SetShaded(bool make_shaded)
1024{
1025 if (this->shade_select == nullptr) return;
1026
1027 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1028 if (this->shade_select->shown_plane != desired) {
1029 if (make_shaded) {
1030 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1031 this->unshaded_size.width = this->width;
1032 this->unshaded_size.height = this->height;
1033 this->shade_select->SetDisplayedPlane(desired);
1034 this->ReInit(0, -this->height);
1035 } else {
1036 this->shade_select->SetDisplayedPlane(desired);
1037 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1038 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1039 this->ReInit(dx, dy);
1040 }
1041 }
1042}
1043
1050{
1051 for (Window *v : Window::Iterate()) {
1052 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1053 }
1054
1055 return nullptr;
1056}
1057
1065{
1066 for (Window *v : Window::Iterate()) {
1067 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1068 }
1069
1070 return nullptr;
1071}
1072
1078{
1079 Window *child = this->FindChildWindow(wc);
1080 while (child != nullptr) {
1081 child->Close();
1082 child = this->FindChildWindow(wc);
1083 }
1084}
1085
1086
1093{
1094 Window *child = this->FindChildWindowById(wc, number);
1095 while (child != nullptr) {
1096 child->Close();
1097 child = this->FindChildWindowById(wc, number);
1098 }
1099}
1100
1104void Window::Close([[maybe_unused]] int data)
1105{
1106 /* Don't close twice. */
1107 if (*this->z_position == nullptr) return;
1108
1109 *this->z_position = nullptr;
1110
1111 if (_thd.window_class == this->window_class &&
1112 _thd.window_number == this->window_number) {
1114 }
1115
1116 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1117 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1118
1119 /* We can't scroll the window when it's closed. */
1120 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1121
1122 /* Make sure we don't try to access non-existing query strings. */
1123 this->querystrings.clear();
1124
1125 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1126 if (_focused_window == this) {
1127 this->OnFocusLost(true);
1128 _focused_window = nullptr;
1129 }
1130
1131 this->CloseChildWindows();
1132
1133 this->SetDirty();
1134
1135 Window::closed_windows.push_back(this);
1136}
1137
1142{
1143 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1144 assert(*this->z_position == nullptr);
1145}
1146
1154{
1155 for (Window *w : Window::Iterate()) {
1156 if (w->window_class == cls && w->window_number == number) return w;
1157 }
1158
1159 return nullptr;
1160}
1161
1169{
1170 for (Window *w : Window::Iterate()) {
1171 if (w->window_class == cls) return w;
1172 }
1173
1174 return nullptr;
1175}
1176
1183{
1185 assert(w != nullptr);
1186 return w;
1187}
1188
1195void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1196{
1197 Window *w = FindWindowById(cls, number);
1198 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1199 w->Close(data);
1200 }
1201}
1202
1208{
1209 /* Note: the container remains stable, even when deleting windows. */
1210 for (Window *w : Window::Iterate()) {
1211 if (w->window_class == cls) {
1212 w->Close(data);
1213 }
1214 }
1215}
1216
1224{
1225 /* Note: the container remains stable, even when deleting windows. */
1226 for (Window *w : Window::Iterate()) {
1227 if (w->owner == id) {
1228 w->Close();
1229 }
1230 }
1231
1232 /* Also delete the company specific windows that don't have a company-colour. */
1234}
1235
1243void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1244{
1245 for (Window *w : Window::Iterate()) {
1246 if (w->owner != old_owner) continue;
1247
1248 switch (w->window_class) {
1249 case WC_COMPANY_COLOUR:
1250 case WC_FINANCES:
1251 case WC_STATION_LIST:
1252 case WC_TRAINS_LIST:
1253 case WC_ROADVEH_LIST:
1254 case WC_SHIPS_LIST:
1255 case WC_AIRCRAFT_LIST:
1256 case WC_BUY_COMPANY:
1257 case WC_COMPANY:
1259 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().
1260 continue;
1261
1262 default:
1263 w->owner = new_owner;
1264 break;
1265 }
1266 }
1267}
1268
1269static void BringWindowToFront(Window *w, bool dirty = true);
1270
1279{
1280 Window *w = FindWindowById(cls, number);
1281
1282 if (w != nullptr) {
1283 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1284
1285 w->SetWhiteBorder();
1287 w->SetDirty();
1288 }
1289
1290 return w;
1291}
1292
1293static inline bool IsVitalWindow(const Window *w)
1294{
1295 switch (w->window_class) {
1296 case WC_MAIN_TOOLBAR:
1297 case WC_STATUS_BAR:
1298 case WC_NEWS_WINDOW:
1300 return true;
1301
1302 default:
1303 return false;
1304 }
1305}
1306
1316{
1317 assert(wc != WC_INVALID);
1318
1319 uint z_priority = 0;
1320
1321 switch (wc) {
1322 case WC_TOOLTIPS:
1323 ++z_priority;
1324 [[fallthrough]];
1325
1326 case WC_ERRMSG:
1328 ++z_priority;
1329 [[fallthrough]];
1330
1331 case WC_ENDSCREEN:
1332 ++z_priority;
1333 [[fallthrough]];
1334
1335 case WC_HIGHSCORE:
1336 ++z_priority;
1337 [[fallthrough]];
1338
1339 case WC_DROPDOWN_MENU:
1340 ++z_priority;
1341 [[fallthrough]];
1342
1343 case WC_MAIN_TOOLBAR:
1344 case WC_STATUS_BAR:
1345 ++z_priority;
1346 [[fallthrough]];
1347
1348 case WC_OSK:
1349 ++z_priority;
1350 [[fallthrough]];
1351
1352 case WC_QUERY_STRING:
1354 ++z_priority;
1355 [[fallthrough]];
1356
1358 case WC_MODAL_PROGRESS:
1360 case WC_SAVE_PRESET:
1361 ++z_priority;
1362 [[fallthrough]];
1363
1365 case WC_SAVELOAD:
1366 case WC_GAME_OPTIONS:
1367 case WC_CUSTOM_CURRENCY:
1368 case WC_NETWORK_WINDOW:
1369 case WC_GRF_PARAMETERS:
1370 case WC_SCRIPT_LIST:
1371 case WC_SCRIPT_SETTINGS:
1372 case WC_TEXTFILE:
1373 ++z_priority;
1374 [[fallthrough]];
1375
1376 case WC_CONSOLE:
1377 ++z_priority;
1378 [[fallthrough]];
1379
1380 case WC_NEWS_WINDOW:
1381 ++z_priority;
1382 [[fallthrough]];
1383
1384 default:
1385 ++z_priority;
1386 [[fallthrough]];
1387
1388 case WC_MAIN_WINDOW:
1389 return z_priority;
1390 }
1391}
1392
1399static void BringWindowToFront(Window *w, bool dirty)
1400{
1401 auto priority = GetWindowZPriority(w->window_class);
1402 WindowList::iterator dest = _z_windows.begin();
1403 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1404
1405 if (dest != w->z_position) {
1406 _z_windows.splice(dest, _z_windows, w->z_position);
1407 }
1408
1409 if (dirty) w->SetDirty();
1410}
1411
1420{
1421 /* Set up window properties; some of them are needed to set up smallest size below */
1422 this->window_class = this->window_desc.cls;
1423 this->SetWhiteBorder();
1425 this->owner = INVALID_OWNER;
1426 this->nested_focus = nullptr;
1427 this->window_number = window_number;
1428
1429 this->OnInit();
1430 /* Initialize smallest size. */
1431 this->nested_root->SetupSmallestSize(this);
1432 /* Initialize to smallest size. */
1433 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1434
1435 /* Further set up window properties,
1436 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1437 this->resize.step_width = this->nested_root->resize_x;
1438 this->resize.step_height = this->nested_root->resize_y;
1439
1440 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1441 * (so we don't interrupt typing) unless the new window has a text box. */
1442 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1443 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1444 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1445
1446 /* Insert the window into the correct location in the z-ordering. */
1447 BringWindowToFront(this, false);
1448}
1449
1457void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1458{
1459 this->left = x;
1460 this->top = y;
1461 this->width = sm_width;
1462 this->height = sm_height;
1463}
1464
1476void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1477{
1478 if (allow_resize) {
1479 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1480 def_height = std::max(def_height, this->height);
1481 /* Try to make windows smaller when our window is too small.
1482 * w->(width|height) is normally the same as min_(width|height),
1483 * but this way the GUIs can be made a little more dynamic;
1484 * one can use the same spec for multiple windows and those
1485 * can then determine the real minimum size of the window. */
1486 if (this->width != def_width || this->height != def_height) {
1487 /* Think about the overlapping toolbars when determining the minimum window size */
1488 int free_height = _screen.height;
1489 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1490 if (wt != nullptr) free_height -= wt->height;
1492 if (wt != nullptr) free_height -= wt->height;
1493
1494 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1495 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1496
1497 /* X and Y has to go by step.. calculate it.
1498 * The cast to int is necessary else x/y are implicitly cast to
1499 * unsigned int, which won't work. */
1500 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1501 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1502
1503 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1504 /* ResizeWindow() calls this->OnResize(). */
1505 } else {
1506 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1507 this->OnResize();
1508 }
1509 }
1510
1511 int nx = this->left;
1512 int ny = this->top;
1513
1514 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1515
1516 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1517 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1518 nx = std::max(nx, 0);
1519
1520 if (this->viewport != nullptr) {
1521 this->viewport->left += nx - this->left;
1522 this->viewport->top += ny - this->top;
1523 }
1524 this->left = nx;
1525 this->top = ny;
1526
1527 this->SetDirty();
1528}
1529
1542static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1543{
1544 int right = width + left;
1545 int bottom = height + top;
1546
1547 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1548
1549 /* Make sure it is not obscured by any window. */
1550 for (const Window *w : Window::Iterate()) {
1551 if (w->window_class == WC_MAIN_WINDOW) continue;
1552
1553 if (right > w->left &&
1554 w->left + w->width > left &&
1555 bottom > w->top &&
1556 w->top + w->height > top) {
1557 return false;
1558 }
1559 }
1560
1561 pos.x = left;
1562 pos.y = top;
1563 return true;
1564}
1565
1578static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1579{
1580 bool rtl = _current_text_dir == TD_RTL;
1581
1582 /* Left part of the rectangle may be at most 1/4 off-screen,
1583 * right part of the rectangle may be at most 1/2 off-screen
1584 */
1585 if (rtl) {
1586 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1587 } else {
1588 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1589 }
1590
1591 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1592 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1593
1594 /* Make sure it is not obscured by any window. */
1595 for (const Window *w : Window::Iterate()) {
1596 if (w->window_class == WC_MAIN_WINDOW) continue;
1597
1598 if (left + width > w->left &&
1599 w->left + w->width > left &&
1600 top + height > w->top &&
1601 w->top + w->height > top) {
1602 return false;
1603 }
1604 }
1605
1606 pos.x = left;
1607 pos.y = top;
1608 return true;
1609}
1610
1617static Point GetAutoPlacePosition(int width, int height)
1618{
1619 Point pt;
1620
1621 bool rtl = _current_text_dir == TD_RTL;
1622
1623 /* First attempt, try top-left of the screen */
1624 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1625 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1626 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1627
1628 /* Second attempt, try around all existing windows.
1629 * The new window must be entirely on-screen, and not overlap with an existing window.
1630 * Eight starting points are tried, two at each corner.
1631 */
1632 for (const Window *w : Window::Iterate()) {
1633 if (w->window_class == WC_MAIN_WINDOW) continue;
1634
1635 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1636 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1637 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1638 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1639 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1640 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1641 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1642 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1643 }
1644
1645 /* Third attempt, try around all existing windows.
1646 * The new window may be partly off-screen, and must not overlap with an existing window.
1647 * Only four starting points are tried.
1648 */
1649 for (const Window *w : Window::Iterate()) {
1650 if (w->window_class == WC_MAIN_WINDOW) continue;
1651
1652 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1653 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1654 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1655 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1656 }
1657
1658 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1659 * of the closebox
1660 */
1661 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1662 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1663 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1664
1665restart:
1666 for (const Window *w : Window::Iterate()) {
1667 if (w->left == left && w->top == top) {
1668 left += offset_x;
1669 top += offset_y;
1670 goto restart;
1671 }
1672 }
1673
1674 pt.x = left;
1675 pt.y = top;
1676 return pt;
1677}
1678
1686{
1687 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1688 assert(w != nullptr);
1689 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1690 return pt;
1691}
1692
1702{
1703 Point pt = GetToolbarAlignedWindowPosition(window_width);
1705 if (w != nullptr && w->top == pt.y && !_settings_client.gui.link_terraform_toolbar) {
1706 pt.x = w->left + (_current_text_dir == TD_RTL ? w->width : - window_width);
1707 }
1708 return pt;
1709}
1710
1728static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1729{
1730 Point pt;
1731 const Window *w;
1732
1733 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1734 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1735
1736 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1737 bool rtl = _current_text_dir == TD_RTL;
1738 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1739 pt.x = w->left + (rtl ? w->width - default_width : 0);
1740 pt.y = w->top + w->height;
1741 return pt;
1742 } else {
1743 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1744 * - Y position: closebox of parent + closebox of child + statusbar
1745 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1746 */
1747 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1748 if (w->top + 3 * indent_y < _screen.height) {
1749 pt.y = w->top + indent_y;
1750 int indent_close = NWidgetLeaf::closebox_dimension.width;
1751 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1752 if (_current_text_dir == TD_RTL) {
1753 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1754 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1755 } else {
1756 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1757 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1758 }
1759 }
1760 }
1761 }
1762
1763 switch (desc.default_pos) {
1764 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1765 return GetToolbarAlignedWindowPosition(default_width);
1766
1767 case WDP_AUTO: // Find a good automatic position for the window
1768 return GetAutoPlacePosition(default_width, default_height);
1769
1770 case WDP_CENTER: // Centre the window horizontally
1771 pt.x = (_screen.width - default_width) / 2;
1772 pt.y = (_screen.height - default_height) / 2;
1773 break;
1774
1775 case WDP_MANUAL:
1776 pt.x = 0;
1777 pt.y = 0;
1778 break;
1779
1780 default:
1781 NOT_REACHED();
1782 }
1783
1784 return pt;
1785}
1786
1787/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1788{
1789 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1790}
1791
1800{
1801 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1802 this->nested_root->FillWidgetLookup(this->widget_lookup);
1803}
1804
1810{
1811 this->InitializeData(window_number);
1812 this->ApplyDefaults();
1813 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1814 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1815 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1816}
1817
1823{
1824 this->CreateNestedTree();
1825 this->FinishInitNested(window_number);
1826}
1827
1832Window::Window(WindowDesc &desc) : window_desc(desc), scale(_gui_scale), mouse_capture_widget(INVALID_WIDGET)
1833{
1834 this->z_position = _z_windows.insert(_z_windows.end(), this);
1835}
1836
1845{
1846 for (Window *w : Window::IterateFromFront()) {
1847 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1848 return w;
1849 }
1850 }
1851
1852 return nullptr;
1853}
1854
1859{
1860 IConsoleClose();
1861
1862 _focused_window = nullptr;
1863 _mouseover_last_w = nullptr;
1864 _last_scroll_window = nullptr;
1865 _scrolling_viewport = false;
1866 _mouse_hovering = false;
1867
1869 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1870 NWidgetScrollbar::InvalidateDimensionCache();
1871
1873
1875}
1876
1881{
1883
1884 for (Window *w : Window::Iterate()) w->Close();
1885
1887
1888 assert(_z_windows.empty());
1889}
1890
1895{
1898 _thd.Reset();
1899}
1900
1901static void DecreaseWindowCounters()
1902{
1903 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1904
1905 for (Window *w : Window::Iterate()) {
1906 if (_scroller_click_timeout == 0) {
1907 /* Unclick scrollbar buttons if they are pressed. */
1908 for (auto &pair : w->widget_lookup) {
1909 NWidgetBase *nwid = pair.second;
1910 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1911 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1912 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1915 sb->SetDirty(w);
1916 }
1917 }
1918 }
1919 }
1920
1921 /* Handle editboxes */
1922 for (auto &pair : w->querystrings) {
1923 pair.second->HandleEditBox(w, pair.first);
1924 }
1925
1926 w->OnMouseLoop();
1927 }
1928
1929 for (Window *w : Window::Iterate()) {
1930 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1932
1933 w->OnTimeout();
1934 w->RaiseButtons(true);
1935 }
1936 }
1937}
1938
1939static void HandlePlacePresize()
1940{
1941 if (_special_mouse_mode != WSM_PRESIZE) return;
1942
1943 Window *w = _thd.GetCallbackWnd();
1944 if (w == nullptr) return;
1945
1946 Point pt = GetTileBelowCursor();
1947 if (pt.x == -1) {
1948 _thd.selend.x = -1;
1949 return;
1950 }
1951
1952 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1953}
1954
1960{
1962
1963 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1964
1965 Window *w = _thd.GetCallbackWnd();
1966 if (w != nullptr) {
1967 /* Send an event in client coordinates. */
1968 Point pt;
1969 pt.x = _cursor.pos.x - w->left;
1970 pt.y = _cursor.pos.y - w->top;
1971 if (_left_button_down) {
1972 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1973 } else {
1974 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1975 }
1976 }
1977
1978 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1979 return ES_HANDLED;
1980}
1981
1983static void HandleMouseOver()
1984{
1985 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1986
1987 /* We changed window, put an OnMouseOver event to the last window */
1988 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1989 /* Reset mouse-over coordinates of previous window */
1990 Point pt = { -1, -1 };
1992 }
1993
1994 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1996
1997 if (w != nullptr) {
1998 /* send an event in client coordinates. */
1999 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2000 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
2001 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
2002 }
2003}
2004
2010
2021static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
2022{
2023 if (v == nullptr) return;
2024
2025 const int min_visible = rect.Height();
2026
2027 int v_bottom = v->top + v->height - 1;
2028 int v_right = v->left + v->width - 1;
2029 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2030
2031 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2032 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2033
2034 /* Vertically, the rectangle is hidden behind v. */
2035 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2036 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2037 return;
2038 }
2039 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2040 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2041 return;
2042 }
2043
2044 /* Horizontally also hidden, force movement to a safe area. */
2045 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2046 *nx = v->left - min_visible - rect.left;
2047 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2048 *nx = v_right + min_visible - rect.right;
2049 } else {
2050 *ny = safe_y;
2051 }
2052}
2053
2061static void EnsureVisibleCaption(Window *w, int nx, int ny)
2062{
2063 /* Search for the title bar rectangle. */
2064 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2065 if (caption != nullptr) {
2066 const Rect caption_rect = caption->GetCurrentRect();
2067
2068 const int min_visible = caption_rect.Height();
2069
2070 /* Make sure the window doesn't leave the screen */
2071 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2072 ny = Clamp(ny, 0, _screen.height - min_visible);
2073
2074 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2075 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2076 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2077 }
2078
2079 if (w->viewport != nullptr) {
2080 w->viewport->left += nx - w->left;
2081 w->viewport->top += ny - w->top;
2082 }
2083
2084 w->left = nx;
2085 w->top = ny;
2086}
2087
2098void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2099{
2100 if (delta_x != 0 || delta_y != 0) {
2101 if (clamp_to_screen) {
2102 /* Determine the new right/bottom position. If that is outside of the bounds of
2103 * the resolution clamp it in such a manner that it stays within the bounds. */
2104 int new_right = w->left + w->width + delta_x;
2105 int new_bottom = w->top + w->height + delta_y;
2106 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2107 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2108 }
2109
2110 w->SetDirty();
2111
2112 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);
2113 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);
2114 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2115 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2116
2117 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);
2118 w->width = w->nested_root->current_x;
2119 w->height = w->nested_root->current_y;
2120 }
2121
2122 EnsureVisibleCaption(w, w->left, w->top);
2123
2124 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2125 if (schedule_resize) {
2126 w->ScheduleResize();
2127 } else {
2128 w->OnResize();
2129 }
2130 w->SetDirty();
2131}
2132
2139{
2141 return (w == nullptr) ? 0 : w->top + w->height;
2142}
2143
2150{
2152 return (w == nullptr) ? _screen.height : w->top;
2153}
2154
2155static bool _dragging_window;
2156
2162{
2163 /* Get out immediately if no window is being dragged at all. */
2164 if (!_dragging_window) return ES_NOT_HANDLED;
2165
2166 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2167 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2168
2169 /* Otherwise find the window... */
2170 for (Window *w : Window::Iterate()) {
2172 /* Stop the dragging if the left mouse button was released */
2173 if (!_left_button_down) {
2175 break;
2176 }
2177
2178 w->SetDirty();
2179
2180 int x = _cursor.pos.x + _drag_delta.x;
2181 int y = _cursor.pos.y + _drag_delta.y;
2182 int nx = x;
2183 int ny = y;
2184
2188 int delta;
2189
2190 for (const Window *v : Window::Iterate()) {
2191 if (v == w) continue; // Don't snap at yourself
2192
2193 if (y + w->height > v->top && y < v->top + v->height) {
2194 /* Your left border <-> other right border */
2195 delta = abs(v->left + v->width - x);
2196 if (delta <= hsnap) {
2197 nx = v->left + v->width;
2198 hsnap = delta;
2199 }
2200
2201 /* Your right border <-> other left border */
2202 delta = abs(v->left - x - w->width);
2203 if (delta <= hsnap) {
2204 nx = v->left - w->width;
2205 hsnap = delta;
2206 }
2207 }
2208
2209 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2210 /* Your left border <-> other left border */
2211 delta = abs(v->left - x);
2212 if (delta <= hsnap) {
2213 nx = v->left;
2214 hsnap = delta;
2215 }
2216
2217 /* Your right border <-> other right border */
2218 delta = abs(v->left + v->width - x - w->width);
2219 if (delta <= hsnap) {
2220 nx = v->left + v->width - w->width;
2221 hsnap = delta;
2222 }
2223 }
2224
2225 if (x + w->width > v->left && x < v->left + v->width) {
2226 /* Your top border <-> other bottom border */
2227 delta = abs(v->top + v->height - y);
2228 if (delta <= vsnap) {
2229 ny = v->top + v->height;
2230 vsnap = delta;
2231 }
2232
2233 /* Your bottom border <-> other top border */
2234 delta = abs(v->top - y - w->height);
2235 if (delta <= vsnap) {
2236 ny = v->top - w->height;
2237 vsnap = delta;
2238 }
2239 }
2240
2241 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2242 /* Your top border <-> other top border */
2243 delta = abs(v->top - y);
2244 if (delta <= vsnap) {
2245 ny = v->top;
2246 vsnap = delta;
2247 }
2248
2249 /* Your bottom border <-> other bottom border */
2250 delta = abs(v->top + v->height - y - w->height);
2251 if (delta <= vsnap) {
2252 ny = v->top + v->height - w->height;
2253 vsnap = delta;
2254 }
2255 }
2256 }
2257 }
2258
2259 EnsureVisibleCaption(w, nx, ny);
2260
2261 w->SetDirty();
2262 return ES_HANDLED;
2264 /* Stop the sizing if the left mouse button was released */
2265 if (!_left_button_down) {
2268 w->SetDirty();
2269 break;
2270 }
2271
2272 /* Compute difference in pixels between cursor position and reference point in the window.
2273 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2274 */
2275 int x, y = _cursor.pos.y - _drag_delta.y;
2277 x = _drag_delta.x - _cursor.pos.x;
2278 } else {
2279 x = _cursor.pos.x - _drag_delta.x;
2280 }
2281
2282 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2283 if (w->resize.step_width == 0) x = 0;
2284 if (w->resize.step_height == 0) y = 0;
2285
2286 /* Check the resize button won't go past the bottom of the screen */
2287 if (w->top + w->height + y > _screen.height) {
2288 y = _screen.height - w->height - w->top;
2289 }
2290
2291 /* X and Y has to go by step.. calculate it.
2292 * The cast to int is necessary else x/y are implicitly cast to
2293 * unsigned int, which won't work. */
2294 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2295 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2296
2297 /* Check that we don't go below the minimum set size */
2298 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2299 x = w->nested_root->smallest_x - w->width;
2300 }
2301 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2302 y = w->nested_root->smallest_y - w->height;
2303 }
2304
2305 /* Window already on size */
2306 if (x == 0 && y == 0) return ES_HANDLED;
2307
2308 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2309 _drag_delta.y += y;
2310 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2311 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2312 w->SetDirty();
2313 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2314 /* ResizeWindow() below ensures marking new position as dirty. */
2315 } else {
2316 _drag_delta.x += x;
2317 }
2318
2319 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2320 ResizeWindow(w, x, y);
2321 return ES_HANDLED;
2322 }
2323 }
2324
2325 _dragging_window = false;
2326 return ES_HANDLED;
2327}
2328
2334{
2337 _dragging_window = true;
2338
2339 _drag_delta.x = w->left - _cursor.pos.x;
2340 _drag_delta.y = w->top - _cursor.pos.y;
2341
2343}
2344
2350static void StartWindowSizing(Window *w, bool to_left)
2351{
2354 _dragging_window = true;
2355
2356 _drag_delta.x = _cursor.pos.x;
2357 _drag_delta.y = _cursor.pos.y;
2358
2360}
2361
2367{
2368 int i;
2370 bool rtl = false;
2371
2372 if (sb->type == NWID_HSCROLLBAR) {
2373 i = _cursor.pos.x - _cursorpos_drag_start.x;
2374 rtl = _current_text_dir == TD_RTL;
2375 } else {
2376 i = _cursor.pos.y - _cursorpos_drag_start.y;
2377 }
2378
2379 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2380 if (_scroller_click_timeout == 1) {
2381 _scroller_click_timeout = 3;
2382 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2384 w->SetDirty();
2385 }
2386 }
2387 return;
2388 }
2389
2390 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2391 int range = sb->GetCount() - sb->GetCapacity();
2392 if (range <= 0) return;
2393
2394 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2395 if (rtl) pos = range - pos;
2396 if (sb->SetPosition(pos)) {
2398 w->SetDirty();
2399 }
2400}
2401
2407{
2408 for (Window *w : Window::Iterate()) {
2409 if (w->mouse_capture_widget >= 0) {
2410 /* Abort if no button is clicked any more. */
2411 if (!_left_button_down) {
2414 return ES_HANDLED;
2415 }
2416
2417 /* Handle scrollbar internally, or dispatch click event */
2419 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2421 } else {
2422 /* If cursor hasn't moved, there is nothing to do. */
2423 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2424
2425 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2426 w->OnClick(pt, w->mouse_capture_widget, 0);
2427 }
2428 return ES_HANDLED;
2429 }
2430 }
2431
2432 return ES_NOT_HANDLED;
2433}
2434
2440{
2441 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2442
2444
2445 /* When we don't have a last scroll window we are starting to scroll.
2446 * When the last scroll window and this are not the same we went
2447 * outside of the window and should not left-mouse scroll anymore. */
2448 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2449
2451 _cursor.fix_at = false;
2452 _scrolling_viewport = false;
2453 _last_scroll_window = nullptr;
2454 return ES_NOT_HANDLED;
2455 }
2456
2457 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2458 /* If the main window is following a vehicle, then first let go of it! */
2459 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
2460 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2461 return ES_NOT_HANDLED;
2462 }
2463
2464 Point delta;
2465 if (scrollwheel_scrolling) {
2466 /* We are using scrollwheels for scrolling */
2467 /* Use the integer part for movement */
2468 delta.x = static_cast<int>(_cursor.h_wheel);
2469 delta.y = static_cast<int>(_cursor.v_wheel);
2470 /* Keep the fractional part so that subtle movement is accumulated */
2471 float temp;
2472 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2473 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2474 } else {
2476 delta.x = -_cursor.delta.x;
2477 delta.y = -_cursor.delta.y;
2478 } else {
2479 delta.x = _cursor.delta.x;
2480 delta.y = _cursor.delta.y;
2481 }
2482 }
2483
2484 /* Create a scroll-event and send it to the window */
2485 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2486
2487 _cursor.delta.x = 0;
2488 _cursor.delta.y = 0;
2489 _cursor.wheel_moved = false;
2490 return ES_HANDLED;
2491}
2492
2504{
2505 bool bring_to_front = false;
2506
2507 if (w->window_class == WC_MAIN_WINDOW ||
2508 IsVitalWindow(w) ||
2509 w->window_class == WC_TOOLTIPS ||
2511 return true;
2512 }
2513
2514 /* Use unshaded window size rather than current size for shaded windows. */
2515 int w_width = w->width;
2516 int w_height = w->height;
2517 if (w->IsShaded()) {
2518 w_width = w->unshaded_size.width;
2519 w_height = w->unshaded_size.height;
2520 }
2521
2523 ++it;
2524 for (; !it.IsEnd(); ++it) {
2525 Window *u = *it;
2526 /* A modal child will prevent the activation of the parent window */
2528 u->SetWhiteBorder();
2529 u->SetDirty();
2530 return false;
2531 }
2532
2533 if (u->window_class == WC_MAIN_WINDOW ||
2534 IsVitalWindow(u) ||
2535 u->window_class == WC_TOOLTIPS ||
2537 continue;
2538 }
2539
2540 /* Window sizes don't interfere, leave z-order alone */
2541 if (w->left + w_width <= u->left ||
2542 u->left + u->width <= w->left ||
2543 w->top + w_height <= u->top ||
2544 u->top + u->height <= w->top) {
2545 continue;
2546 }
2547
2548 bring_to_front = true;
2549 }
2550
2551 if (bring_to_front) BringWindowToFront(w);
2552 return true;
2553}
2554
2563EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2564{
2565 QueryString *query = this->GetQueryString(wid);
2566 if (query == nullptr) return ES_NOT_HANDLED;
2567
2568 int action = QueryString::ACTION_NOTHING;
2569
2570 switch (query->text.HandleKeyPress(key, keycode)) {
2571 case HKPR_EDITING:
2572 this->SetWidgetDirty(wid);
2573 this->OnEditboxChanged(wid);
2574 break;
2575
2576 case HKPR_CURSOR:
2577 this->SetWidgetDirty(wid);
2578 /* For the OSK also invalidate the parent window */
2579 if (this->window_class == WC_OSK) this->InvalidateData();
2580 break;
2581
2582 case HKPR_CONFIRM:
2583 if (this->window_class == WC_OSK) {
2584 this->OnClick(Point(), WID_OSK_OK, 1);
2585 } else if (query->ok_button >= 0) {
2586 this->OnClick(Point(), query->ok_button, 1);
2587 } else {
2588 action = query->ok_button;
2589 }
2590 break;
2591
2592 case HKPR_CANCEL:
2593 if (this->window_class == WC_OSK) {
2594 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2595 } else if (query->cancel_button >= 0) {
2596 this->OnClick(Point(), query->cancel_button, 1);
2597 } else {
2598 action = query->cancel_button;
2599 }
2600 break;
2601
2602 case HKPR_NOT_HANDLED:
2603 return ES_NOT_HANDLED;
2604
2605 default: break;
2606 }
2607
2608 switch (action) {
2610 this->UnfocusFocusedWidget();
2611 break;
2612
2614 if (query->text.GetText().empty()) {
2615 /* If already empty, unfocus instead */
2616 this->UnfocusFocusedWidget();
2617 } else {
2618 query->text.DeleteAll();
2619 this->SetWidgetDirty(wid);
2620 this->OnEditboxChanged(wid);
2621 }
2622 break;
2623
2624 default:
2625 break;
2626 }
2627
2628 return ES_HANDLED;
2629}
2630
2635void HandleToolbarHotkey(int hotkey)
2636{
2637 assert(HasModalProgress() || IsLocalCompany());
2638
2640 if (w != nullptr) {
2641 if (w->window_desc.hotkeys != nullptr) {
2642 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2643 }
2644 }
2645}
2646
2652void HandleKeypress(uint keycode, char32_t key)
2653{
2654 /* World generation is multithreaded and messes with companies.
2655 * But there is no company related window open anyway, so _current_company is not used. */
2656 assert(HasModalProgress() || IsLocalCompany());
2657
2658 /*
2659 * The Unicode standard defines an area called the private use area. Code points in this
2660 * area are reserved for private use and thus not portable between systems. For instance,
2661 * Apple defines code points for the arrow keys in this area, but these are only printable
2662 * on a system running OS X. We don't want these keys to show up in text fields and such,
2663 * and thus we have to clear the unicode character when we encounter such a key.
2664 */
2665 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2666
2667 /*
2668 * If both key and keycode is zero, we don't bother to process the event.
2669 */
2670 if (key == 0 && keycode == 0) return;
2671
2672 /* Check if the focused window has a focused editbox */
2673 if (EditBoxInGlobalFocus()) {
2674 /* All input will in this case go to the focused editbox */
2675 if (_focused_window->window_class == WC_CONSOLE) {
2676 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2677 } else {
2678 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2679 }
2680 }
2681
2682 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2683 for (Window *w : Window::IterateFromFront()) {
2684 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2685 if (w->window_desc.hotkeys != nullptr) {
2686 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2687 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2688 }
2689 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2690 }
2691
2693 /* When there is no toolbar w is null, check for that */
2694 if (w != nullptr) {
2695 if (w->window_desc.hotkeys != nullptr) {
2696 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2697 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2698 }
2699 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2700 }
2701
2702 HandleGlobalHotkeys(key, keycode);
2703}
2704
2709{
2710 /* Call the event, start with the uppermost window. */
2711 for (Window *w : Window::IterateFromFront()) {
2712 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2713 }
2714}
2715
2721/* virtual */ void Window::InsertTextString(WidgetID wid, std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2722{
2723 QueryString *query = this->GetQueryString(wid);
2724 if (query == nullptr) return;
2725
2726 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2727 this->SetWidgetDirty(wid);
2728 this->OnEditboxChanged(wid);
2729 }
2730}
2731
2738void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2739{
2740 if (!EditBoxInGlobalFocus()) return;
2741
2742 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2743}
2744
2749static void HandleAutoscroll()
2750{
2751 if (_game_mode == GM_MENU || HasModalProgress()) return;
2753 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2754
2755 int x = _cursor.pos.x;
2756 int y = _cursor.pos.y;
2757 Window *w = FindWindowFromPt(x, y);
2758 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2760
2761 Viewport *vp = IsPtInWindowViewport(w, x, y);
2762 if (vp == nullptr) return;
2763
2764 x -= vp->left;
2765 y -= vp->top;
2766
2767 /* here allows scrolling in both x and y axis */
2768 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2769 static const int SCROLLSPEED = 3;
2770 if (x - 15 < 0) {
2771 w->viewport->CancelFollow(*w);
2772 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2773 } else if (15 - (vp->width - x) > 0) {
2774 w->viewport->CancelFollow(*w);
2775 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2776 }
2777 if (y - 15 < 0) {
2778 w->viewport->CancelFollow(*w);
2779 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2780 } else if (15 - (vp->height - y) > 0) {
2781 w->viewport->CancelFollow(*w);
2782 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2783 }
2784}
2785
2786enum MouseClick : uint8_t {
2787 MC_NONE = 0,
2788 MC_LEFT,
2789 MC_RIGHT,
2790 MC_DOUBLE_LEFT,
2791 MC_HOVER,
2792};
2793
2794static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2795static constexpr int MAX_OFFSET_HOVER = 5;
2796
2798
2799const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2800
2801static void ScrollMainViewport(int x, int y)
2802{
2803 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2804 Window *w = GetMainWindow();
2805 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2806 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2807 }
2808}
2809
2819static const int8_t scrollamt[16][2] = {
2820 { 0, 0},
2821 {-2, 0},
2822 { 0, -2},
2823 {-2, -1},
2824 { 2, 0},
2825 { 0, 0},
2826 { 2, -1},
2827 { 0, -2},
2828 { 0, 2},
2829 {-2, 1},
2830 { 0, 0},
2831 {-2, 0},
2832 { 2, 1},
2833 { 0, 2},
2834 { 2, 0},
2835 { 0, 0},
2836};
2837
2838static void HandleKeyScrolling()
2839{
2840 /*
2841 * Check that any of the dirkeys is pressed and that the focused window
2842 * doesn't have an edit-box as focused widget.
2843 */
2844 if (_dirkeys && !EditBoxInGlobalFocus()) {
2845 int factor = _shift_pressed ? 50 : 10;
2846
2847 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2848 /* Key scrolling stops following a vehicle. */
2849 Window *main_window = GetMainWindow();
2850 main_window->viewport->CancelFollow(*main_window);
2851 }
2852
2853 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2854 }
2855}
2856
2857static void MouseLoop(MouseClick click, int mousewheel)
2858{
2859 /* World generation is multithreaded and messes with companies.
2860 * But there is no company related window open anyway, so _current_company is not used. */
2861 assert(HasModalProgress() || IsLocalCompany());
2862
2863 HandlePlacePresize();
2865
2866 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2867 if (HandleMouseDragDrop() == ES_HANDLED) return;
2868 if (HandleWindowDragging() == ES_HANDLED) return;
2869 if (HandleActiveWidget() == ES_HANDLED) return;
2870 if (HandleViewportScroll() == ES_HANDLED) return;
2871
2873
2874 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2875 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2876
2877 int x = _cursor.pos.x;
2878 int y = _cursor.pos.y;
2879 Window *w = FindWindowFromPt(x, y);
2880 if (w == nullptr) return;
2881
2882 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2883 Viewport *vp = IsPtInWindowViewport(w, x, y);
2884
2885 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2886 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2887
2888 if (mousewheel != 0) {
2889 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2890 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) {
2891 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2892 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2893 }
2894 }
2895
2896 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2897 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2898 }
2899
2900 if (vp != nullptr) {
2901 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2902 _scrolling_viewport = true;
2903 _cursor.fix_at = true;
2904 return;
2905 }
2906
2907 switch (click) {
2908 case MC_DOUBLE_LEFT:
2909 case MC_LEFT:
2910 if (HandleViewportClicked(*vp, x, y)) return;
2913 _scrolling_viewport = true;
2914 _cursor.fix_at = false;
2915 return;
2916 }
2917 break;
2918
2919 case MC_RIGHT:
2922 _scrolling_viewport = true;
2925 DispatchRightClickEvent(w, x - w->left, y - w->top);
2926 return;
2927 }
2928 break;
2929
2930 default:
2931 break;
2932 }
2933 }
2934
2935 switch (click) {
2936 case MC_LEFT:
2937 case MC_DOUBLE_LEFT:
2938 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2939 return;
2940
2941 default:
2942 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2943 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2944 * Simulate a right button click so we can get started. */
2945 [[fallthrough]];
2946
2947 case MC_RIGHT:
2948 DispatchRightClickEvent(w, x - w->left, y - w->top);
2949 return;
2950
2951 case MC_HOVER:
2952 DispatchHoverEvent(w, x - w->left, y - w->top);
2953 break;
2954 }
2955
2956 /* We're not doing anything with 2D scrolling, so reset the value. */
2957 _cursor.h_wheel = 0.0f;
2958 _cursor.v_wheel = 0.0f;
2959 _cursor.wheel_moved = false;
2960}
2961
2966{
2967 /* World generation is multithreaded and messes with companies.
2968 * But there is no company related window open anyway, so _current_company is not used. */
2969 assert(HasModalProgress() || IsLocalCompany());
2970
2971 static std::chrono::steady_clock::time_point double_click_time = {};
2972 static Point double_click_pos = {0, 0};
2973
2974 /* Mouse event? */
2975 MouseClick click = MC_NONE;
2977 click = MC_LEFT;
2978 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2979 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2980 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2981 click = MC_DOUBLE_LEFT;
2982 }
2983 double_click_time = std::chrono::steady_clock::now();
2984 double_click_pos = _cursor.pos;
2985 _left_button_clicked = true;
2986 } else if (_right_button_clicked) {
2987 _right_button_clicked = false;
2988 click = MC_RIGHT;
2989 }
2990
2991 int mousewheel = 0;
2992 if (_cursor.wheel) {
2993 mousewheel = _cursor.wheel;
2994 _cursor.wheel = 0;
2995 }
2996
2997 static std::chrono::steady_clock::time_point hover_time = {};
2998 static Point hover_pos = {0, 0};
2999
3001 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
3002 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
3003 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
3004 hover_pos = _cursor.pos;
3005 hover_time = std::chrono::steady_clock::now();
3006 _mouse_hovering = false;
3007 } else if (!_mouse_hovering) {
3008 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
3009 click = MC_HOVER;
3010 _mouse_hovering = true;
3011 hover_time = std::chrono::steady_clock::now();
3012 }
3013 }
3014 }
3015
3016 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
3017 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
3019 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
3021 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3023 } else {
3024 MouseLoop(click, mousewheel);
3025 }
3026
3027 /* We have moved the mouse the required distance,
3028 * no need to move it at any later time. */
3029 _cursor.delta.x = 0;
3030 _cursor.delta.y = 0;
3031}
3032
3036static void CheckSoftLimit()
3037{
3038 if (_settings_client.gui.window_soft_limit == 0) return;
3039
3040 for (;;) {
3041 uint deletable_count = 0;
3042 Window *last_deletable = nullptr;
3043 for (Window *w : Window::IterateFromFront()) {
3044 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3045
3046 last_deletable = w;
3047 deletable_count++;
3048 }
3049
3050 /* We've not reached the soft limit yet. */
3051 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3052
3053 assert(last_deletable != nullptr);
3054 last_deletable->Close();
3055 }
3056}
3057
3062{
3063 /* World generation is multithreaded and messes with companies.
3064 * But there is no company related window open anyway, so _current_company is not used. */
3065 assert(HasModalProgress() || IsLocalCompany());
3066
3068
3069 /* Process scheduled window deletion. */
3071
3072 /* HandleMouseEvents was already called for this tick */
3074}
3075
3076static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3077
3078bool CanContinueRealtimeTick()
3079{
3080 auto now = std::chrono::steady_clock::now();
3081 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3082}
3083
3088{
3089 _realtime_tick_start = std::chrono::steady_clock::now();
3090 for (Window *w : Window::Iterate()) {
3091 w->OnRealtimeTick(delta_ms);
3092 }
3093}
3094
3096static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3097 extern int _caret_timer;
3098 _caret_timer += 3;
3099 CursorTick();
3100
3101 HandleKeyScrolling();
3103 DecreaseWindowCounters();
3104});
3105
3109});
3110
3112static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3113 if (_network_dedicated) return;
3114
3115 for (Window *w : Window::Iterate()) {
3118 w->SetDirty();
3119 }
3120 }
3121});
3122
3127{
3128 static auto last_time = std::chrono::steady_clock::now();
3129 auto now = std::chrono::steady_clock::now();
3130 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3131
3132 if (delta_ms.count() == 0) return;
3133
3134 last_time = now;
3135
3138
3140
3142 CallWindowRealtimeTickEvent(delta_ms.count());
3143
3144 /* Process invalidations before anything else. */
3145 for (Window *w : Window::Iterate()) {
3149 }
3150
3151 /* Skip the actual drawing on dedicated servers without screen.
3152 * But still empty the invalidation queues above. */
3153 if (_network_dedicated) return;
3154
3156
3157 for (Window *w : Window::Iterate()) {
3158 /* Update viewport only if window is not shaded. */
3159 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3160 }
3162 /* Redraw mouse cursor in case it was hidden */
3163 DrawMouseCursor();
3164
3165 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3166 /* We are done with the last draw-frame, so we know what sprites we
3167 * clicked on. Reset the picker mode and invalidate the window. */
3170 }
3171}
3172
3179{
3180 for (const Window *w : Window::Iterate()) {
3181 if (w->window_class == cls && w->window_number == number) {
3182 w->SetDirty();
3183 return;
3184 }
3185 }
3186}
3187
3195{
3196 for (const Window *w : Window::Iterate()) {
3197 if (w->window_class == cls && w->window_number == number) {
3198 w->SetWidgetDirty(widget_index);
3199 return;
3200 }
3201 }
3202}
3203
3209{
3210 for (const Window *w : Window::Iterate()) {
3211 if (w->window_class == cls) w->SetDirty();
3212 }
3213}
3214
3219{
3220 this->scheduled_resize = true;
3221}
3222
3227{
3228 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3229 while (this->scheduled_resize) {
3230 this->scheduled_resize = false;
3231 this->OnResize();
3232 }
3233}
3234
3240void Window::InvalidateData(int data, bool gui_scope)
3241{
3242 this->SetDirty();
3243 if (!gui_scope) {
3244 /* Schedule GUI-scope invalidation for next redraw. */
3245 this->scheduled_invalidation_data.push_back(data);
3246 }
3247 this->OnInvalidateData(data, gui_scope);
3248}
3249
3254{
3255 for (int data : this->scheduled_invalidation_data) {
3256 if (this->window_class == WC_INVALID) break;
3257 this->OnInvalidateData(data, true);
3258 }
3259 this->scheduled_invalidation_data.clear();
3260}
3261
3266{
3267 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3268
3269 for (const auto &pair : this->widget_lookup) {
3270 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3271 }
3272}
3273
3300void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3301{
3302 for (Window *w : Window::Iterate()) {
3303 if (w->window_class == cls && w->window_number == number) {
3304 w->InvalidateData(data, gui_scope);
3305 return;
3306 }
3307 }
3308}
3309
3318void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3319{
3320 for (Window *w : Window::Iterate()) {
3321 if (w->window_class == cls) {
3322 w->InvalidateData(data, gui_scope);
3323 }
3324 }
3325}
3326
3331{
3332 for (Window *w : Window::Iterate()) {
3333 w->OnGameTick();
3334 }
3335}
3336
3344{
3345 /* Note: the container remains stable, even when deleting windows. */
3346 for (Window *w : Window::Iterate()) {
3348 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3349
3350 w->Close();
3351 }
3352 }
3353}
3354
3363{
3364 /* Note: the container remains stable, even when closing windows. */
3365 for (Window *w : Window::Iterate()) {
3367 w->Close();
3368 }
3369 }
3370}
3371
3376{
3378 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3379 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3380 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3381}
3382
3388{
3389 /* Note: the container remains stable, even when deleting windows. */
3390 for (Window *w : Window::Iterate()) {
3392 w->Close();
3393 }
3394 }
3395
3396 for (const Window *w : Window::Iterate()) w->SetDirty();
3397}
3398
3405
3406void ReInitWindow(Window *w, bool zoom_changed)
3407{
3408 if (w == nullptr) return;
3409 if (zoom_changed) {
3410 w->nested_root->AdjustPaddingForZoom();
3412 }
3413 w->ReInit();
3414}
3415
3417void ReInitAllWindows(bool zoom_changed)
3418{
3420 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3421 NWidgetScrollbar::InvalidateDimensionCache();
3422
3424
3425 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3426 * so EnsureVisibleCaption uses the updated size information. */
3427 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3428 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3429 for (Window *w : Window::Iterate()) {
3430 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3431 ReInitWindow(w, zoom_changed);
3432 }
3433
3436
3437 /* Make sure essential parts of all windows are visible */
3438 RelocateAllWindows(_screen.width, _screen.height);
3440}
3441
3449static int PositionWindow(Window *w, WindowClass clss, int setting)
3450{
3451 if (w == nullptr || w->window_class != clss) {
3452 w = FindWindowById(clss, 0);
3453 }
3454 if (w == nullptr) return 0;
3455
3456 int old_left = w->left;
3457 switch (setting) {
3458 case 1: w->left = (_screen.width - w->width) / 2; break;
3459 case 2: w->left = _screen.width - w->width; break;
3460 default: w->left = 0; break;
3461 }
3462 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3463 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3464 return w->left;
3465}
3466
3473{
3474 Debug(misc, 5, "Repositioning Main Toolbar...");
3476}
3477
3484{
3485 Debug(misc, 5, "Repositioning statusbar...");
3487}
3488
3495{
3496 Debug(misc, 5, "Repositioning news message...");
3498}
3499
3506{
3507 Debug(misc, 5, "Repositioning network chat window...");
3509}
3510
3511
3518{
3519 for (const Window *w : Window::Iterate()) {
3520 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3521 w->viewport->follow_vehicle = to_index;
3522 w->SetDirty();
3523 }
3524 }
3525}
3526
3527
3533void RelocateAllWindows(int neww, int newh)
3534{
3536
3537 /* Reposition toolbar then status bar before other all windows. */
3538 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3539 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3540 wt->left = PositionMainToolbar(wt);
3541 }
3542
3543 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3544 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3545 ws->top = newh - ws->height;
3546 ws->left = PositionStatusbar(ws);
3547 }
3548
3549 for (Window *w : Window::Iterate()) {
3550 int left, top;
3551 /* XXX - this probably needs something more sane. For example specifying
3552 * in a 'backup'-desc that the window should always be centered. */
3553 switch (w->window_class) {
3554 case WC_MAIN_WINDOW:
3555 case WC_BOOTSTRAP:
3556 case WC_HIGHSCORE:
3557 case WC_ENDSCREEN:
3558 ResizeWindow(w, neww, newh);
3559 continue;
3560
3561 case WC_MAIN_TOOLBAR:
3562 case WC_STATUS_BAR:
3563 continue;
3564
3565 case WC_NEWS_WINDOW:
3566 top = newh - w->height;
3567 left = PositionNewsMessage(w);
3568 break;
3569
3571 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3572
3573 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3574 left = PositionNetworkChatWindow(w);
3575 break;
3576
3577 case WC_CONSOLE:
3578 IConsoleResize(w);
3579 continue;
3580
3581 default: {
3582 if (w->flags.Test(WindowFlag::Centred)) {
3583 top = (newh - w->height) >> 1;
3584 left = (neww - w->width) >> 1;
3585 break;
3586 }
3587
3588 left = w->left;
3589 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3590 if (left < 0) left = 0;
3591
3592 top = w->top;
3593 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3594 break;
3595 }
3596 }
3597
3598 EnsureVisibleCaption(w, left, top);
3599 }
3600}
3601
3606void PickerWindowBase::Close([[maybe_unused]] int data)
3607{
3609 this->Window::Close();
3610}
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:136
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.
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:931
WidgetType type
Type of the widget / nested widget.
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
uint resize_y
Vertical resize step (0 means not resizable).
Base class for a 'real' widget.
bool IsDisabled() const
Return whether the widget is disabled.
NWidgetDisplayFlags disp_flags
Flags that affect display and interaction with the widget.
WidgetID GetScrollbarIndex() const
Get the WidgetID of this nested widget's scrollbar.
Definition widget.cpp:1260
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1233
void SetLowered(bool lowered)
Lower or raise the widget.
bool IsLowered() const
Return whether the widget is lowered.
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2642
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:1422
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:3606
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:30
Functions related to companies.
bool IsLocalCompany()
Is the current company the local company?
static constexpr Owner 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,...)
Output 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:87
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:249
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
Definition gfx_type.h:370
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:3393
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:968
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:1516
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1452
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1549
Hotkey related functions.
Types related to reading/writing '*.ini' files.
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:416
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, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:693
bool _networking
are we in networking mode?
Definition network.cpp:66
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:69
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.
Functions related to news.
void InitNewsItemStructs()
Initialize the news-items data structures.
Definition news_gui.cpp:714
@ 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, std::string_view grpname, WindowDesc *desc)
Load a WindowDesc from config.
Definition settings.cpp:873
void IniSaveWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Save a WindowDesc to config.
Definition settings.cpp:884
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
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.
void SndClickBeep()
Play a beep sound for a click event if enabled in settings.
Definition sound.cpp:253
Functions related to sound.
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.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ 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
T y
Y coordinate.
T x
X coordinate.
bool fix_at
mouse is moving, but cursor is not (used for scrolling)
Definition gfx_type.h:128
Point pos
logical mouse position
Definition gfx_type.h:125
bool in_window
mouse inside this window, determines drawing logic
Definition gfx_type.h:147
int wheel
mouse wheel movement
Definition gfx_type.h:127
Point delta
relative mouse movement in this tick
Definition gfx_type.h:126
Data about how and where to blit pixels.
Definition gfx_type.h:157
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?
bool link_terraform_toolbar
display terraform toolbar when displaying rail, road, water and airport toolbars
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:301
Ini file that supports both loading and saving.
Definition ini_type.h:86
bool SaveToDisk(const std::string &filename)
Save the Ini file's data to the disk.
Definition ini.cpp:42
void LoadFromDisk(std::string_view filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition ini_load.cpp:185
NewGrfDebugSpritePickerMode mode
Current state.
void * clicked_pixel
Clicked pixel (pointer to blitter buffer)
FlatSet< SpriteID > sprites
Sprites found.
static Titem * Get(auto 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:851
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:793
Rect GetBoundingRect(const Window *w, WidgetID wid, size_t from, size_t to) const
Get the bounding rectangle for a range of the query string.
Definition misc_gui.cpp:821
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:445
void DeleteAll()
Delete every character in the textbuffer.
Definition textbuf.cpp:112
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
bool InsertString(std::string_view str, bool marked, std::optional< size_t > caret=std::nullopt, std::optional< size_t > insert_location=std::nullopt, std::optional< size_t > replacement_end=std::nullopt)
Insert a string into the text buffer.
Definition textbuf.cpp:157
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.
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:137
static void SaveToConfig()
Save all WindowDesc settings to _windows_file.
Definition window.cpp:176
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:147
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
const WindowClass cls
Class of the window,.
Definition window_gui.h:179
const std::string_view ini_key
Key to store window defaults in openttd.cfg. An empty string 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:155
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
WindowDesc(WindowPosition default_pos, std::string_view 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:108
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:875
Data structure for an opened window.
Definition window_gui.h:274
virtual const struct Textbuf * GetFocusedTextbuf() const
Get the current input text buffer.
Definition window.cpp:366
void SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
Sets the highlighted status of a widget.
Definition window.cpp:241
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:980
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1104
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:793
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1809
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
virtual void ApplyDefaults()
Read default values from WindowDesc configuration an apply them to the window.
Definition window.cpp:193
uint8_t white_border_timer
Timer value of the WindowFlag::WhiteBorder for flags.
Definition window_gui.h:308
NWidgetStacked * shade_select
Selection widget (NWID_SELECTION) to use for shading the window. If nullptr, window cannot shade.
Definition window_gui.h:324
void InitializePositionSize(int x, int y, int min_width, int min_height)
Set the position and smallest size of the window.
Definition window.cpp:1457
Dimension unshaded_size
Last known unshaded size (only valid while shaded).
Definition window_gui.h:325
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3240
virtual EventState OnKeyPress(char32_t key, uint16_t keycode)
A key has been pressed.
Definition window_gui.h:651
Window * parent
Parent window.
Definition window_gui.h:329
AllWindows< false > IterateFromBack
Iterate all windows in Z order from back to front.
Definition window_gui.h:934
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition window.cpp:1141
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:470
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:558
uint8_t timeout_timer
Timer value of the WindowFlag::Timeout for flags.
Definition window_gui.h:307
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:319
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:506
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). INVALID_WIDGET if no widget has mouse ca...
Definition window_gui.h:327
virtual void OnGameTick()
Called once per (game) tick.
Definition window_gui.h:746
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition window_gui.h:868
virtual bool OnRightClick(Point pt, WidgetID widget)
A click with the right mouse button has been made on the window.
Definition window_gui.h:678
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:720
virtual void OnDropdownSelect(WidgetID widget, int index, int click_result)
A dropdown option associated to this window has been selected.
Definition window_gui.h:770
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition window.cpp:3253
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1077
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:471
virtual void OnMouseLoop()
Called for every mouse loop run, which is at least once per (game) tick.
Definition window_gui.h:741
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition window.cpp:1023
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:305
void ScheduleResize()
Mark this window as resized and in need of OnResize() event.
Definition window.cpp:3218
virtual void OnPaint()
The window must be repainted.
Definition window_gui.h:596
virtual void OnDragDrop(Point pt, WidgetID widget)
A dragged 'object' has been released.
Definition window_gui.h:707
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1799
WindowDesc & window_desc
Window description.
Definition window_gui.h:300
WindowClass window_class
Window class.
Definition window_gui.h:302
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
Definition window_gui.h:751
virtual void OnMouseWheel(int wheel, WidgetID widget)
The mouse wheel has been turned.
Definition window_gui.h:735
AllWindows< true > IterateFromFront
Iterate all windows in Z order from front to back.
Definition window_gui.h:935
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1092
void SetWhiteBorder()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:365
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:487
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:523
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:492
static std::vector< Window * > closed_windows
List of closed windows to delete.
Definition window_gui.h:276
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition window.cpp:532
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1787
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:317
virtual Point GetCaretPosition() const
Get the current caret position if an edit box has the focus.
Definition window.cpp:379
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1476
virtual void OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
A dropdown window associated to this window has been closed.
Definition window.cpp:286
virtual void InsertTextString(WidgetID wid, std::string_view str, bool marked, std::optional< size_t > caret, std::optional< size_t > insert_location, std::optional< size_t > replacement_end)
Insert a text string at the cursor position into the edit box widget.
Definition window.cpp:2721
int left
x position of left edge of the window
Definition window_gui.h:310
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:560
Window * FindChildWindow(WindowClass wc=WC_INVALID) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1049
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:356
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:320
virtual void OnEditboxChanged(WidgetID widget)
The text in an editbox has been edited.
Definition window_gui.h:778
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition window.cpp:355
virtual void OnScroll(Point delta)
Handle the request for (viewport) scrolling.
Definition window_gui.h:713
int top
y position of top edge of the window
Definition window_gui.h:311
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:669
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:335
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:323
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:410
std::vector< int > scheduled_invalidation_data
Data of scheduled OnInvalidateData() calls.
Definition window_gui.h:283
void InitializeData(WindowNumber window_number)
Initializes the data (except the position and initial size) of a new Window.
Definition window.cpp:1419
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:728
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1832
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:212
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:983
virtual bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition window_gui.h:693
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:461
virtual EventState OnCTRLStateChange()
The state of the control key has changed.
Definition window_gui.h:660
void ProcessScheduledResize()
Process scheduled OnResize() event.
Definition window.cpp:3226
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2563
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:700
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:597
virtual void OnResize()
Called after the window got resized.
Definition window_gui.h:763
Window * FindChildWindowById(WindowClass wc, WindowNumber number) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1064
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:515
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1822
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h:756
WindowFlags flags
Window flags.
Definition window_gui.h:301
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:315
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3265
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:572
static void DeleteClosedWindows()
Delete all closed windows.
Definition window.cpp:68
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:322
bool scheduled_resize
Set if window has been resized.
Definition window_gui.h:284
virtual Rect GetTextBoundingRect(size_t from, size_t to) const
Get the bounding rectangle for a text range if an edit box has the focus.
Definition window.cpp:395
bool IsWidgetHighlighted(WidgetID widget_index) const
Gets the highlighted status of a widget.
Definition window.cpp:271
void DisableAllWidgetHighlight()
Disable the highlighted status of all widgets.
Definition window.cpp:223
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:852
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:933
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:313
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:685
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
virtual void OnInit()
Notification that the nested widget tree gets initialized.
Definition window_gui.h:579
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
std::vector< WindowDesc * > * _window_descs
List of WindowDescs.
Definition window.cpp:102
@ 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.
static constexpr std::chrono::milliseconds TIMER_BLINK_INTERVAL
Interval used by blinking interface elements.
uint _toolbar_width
Width of the toolbar, shared by statusbar.
Stuff related to the (main) toolbar.
Base class for all vehicles.
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:408
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:80
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:274
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:250
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:35
@ 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:37
@ 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
@ DropdownClosed
Dropdown menu of the dropdown widget has closed.
@ ScrollbarDown
Down-button is lowered bit.
@ ScrollbarUp
Up-button is lowered bit.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ 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:3483
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:2021
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2155
static const 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.
void CloseConstructionWindows()
Close all windows that are used for construction of vehicle etc.
Definition window.cpp:3387
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:1195
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:1728
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1182
static Point _drag_delta
delta between mouse cursor and upper left corner of dragged window
Definition window.cpp:55
static bool MayBeShown(const Window *w)
Returns whether a window may be shown or not.
Definition window.cpp:857
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1223
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2708
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:885
bool _scrolling_viewport
A viewport is being scrolled with the mouse.
Definition window.cpp:93
void InputLoop()
Regular call from the global game loop.
Definition window.cpp:3061
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3126
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3472
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3343
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:947
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2098
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:1578
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3505
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2350
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:1617
PreventHideDirection
Direction for moving the window.
Definition window.cpp:2006
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:2008
@ PHD_UP
Above v is a safe position.
Definition window.cpp:2007
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2749
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:78
void HandleToolbarHotkey(int hotkey)
Handle Toolbar hotkey events - can come from a source like the MacBook Touch Bar.
Definition window.cpp:2635
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3449
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:423
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:1542
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2794
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1168
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3517
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:2439
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:820
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:618
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1844
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3375
static Window * _last_scroll_window
Window of the last scroll event.
Definition window.cpp:57
int GetMainViewTop()
Return the top of the main view available for general use.
Definition window.cpp:2138
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:1399
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3417
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:2061
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1207
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2965
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:1685
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2161
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3494
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2149
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:792
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:1243
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2652
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3208
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:2795
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:463
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1894
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1315
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:449
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:3300
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3400
static const IntervalTimer< TimerWindow > highlight_interval(TIMER_BLINK_INTERVAL, [](auto) { _window_highlight_colour=!_window_highlight_colour;})
Blink the window highlight colour constantly.
static bool DescSorter(WindowDesc *const &a, WindowDesc *const &b)
Sort WindowDesc by ini_key.
Definition window.cpp:168
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1278
Point AlignInitialConstructionToolbar(int window_width)
Compute the position of the construction toolbars.
Definition window.cpp:1701
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3533
std::string _windows_file
Config file to store WindowDesc.
Definition window.cpp:105
static void HandleScrollbarScrolling(Window *w)
Handle scrollbar scrolling with the mouse.
Definition window.cpp:2366
bool _mouse_hovering
The mouse is hovering over the same point.
Definition window.cpp:94
static void StartWindowDrag(Window *w)
Start window dragging.
Definition window.cpp:2333
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3330
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2406
std::vector< WindowDesc * > * _window_descs
List of all WindowDescs.
Definition window.cpp:102
static void DispatchRightClickEvent(Window *w, int x, int y)
Dispatch right mouse-button click in window.
Definition window.cpp:763
ViewportAutoscrolling
Values for _settings_client.gui.auto_scrolling.
Definition window.cpp:48
@ VA_MAIN_VIEWPORT_FULLSCREEN
Scroll main viewport at edge when using fullscreen.
Definition window.cpp:50
@ VA_MAIN_VIEWPORT
Scroll main viewport at edge.
Definition window.cpp:51
@ VA_EVERY_VIEWPORT
Scroll all viewports at their edges.
Definition window.cpp:52
@ VA_DISABLED
Do not autoscroll when mouse is at edge of viewport.
Definition window.cpp:49
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:2819
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2503
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:60
void CallWindowRealtimeTickEvent(uint delta_ms)
Dispatch OnRealtimeTick event over all windows.
Definition window.cpp:3087
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:96
static EventState HandleMouseDragDrop()
Handle dragging and dropping in mouse dragging mode (WSM_DRAGDROP).
Definition window.cpp:1959
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3362
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1983
static const 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 Window * _mouseover_last_w
Window of the last OnMouseOver event.
Definition window.cpp:56
static void CheckSoftLimit()
Check the soft limit of deletable (non vital, non sticky) windows.
Definition window.cpp:3036
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1153
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1880
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1858
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:3194
void HandleTextInput(std::string_view str, bool marked, std::optional< size_t > caret, std::optional< size_t > insert_location, std::optional< size_t > replacement_end)
Handle text input.
Definition window.cpp:2738
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3178
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:3318
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:423
@ 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.
static const int TIMEOUT_DURATION
The initial timeout value for WindowFlag::Timeout.
Definition window_gui.h:241
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:274
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:142
@ WDP_CENTER
Center the window.
Definition window_gui.h:145
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
@ WDP_ALIGN_TOOLBAR
Align toward the toolbar.
Definition window_gui.h:146
@ WDP_MANUAL
Manually align the window (so no automatic location finding)
Definition window_gui.h:143
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:60
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.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Definition window_type.h:23
WindowClass
Window classes.
Definition window_type.h:49
@ 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:69
@ 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:78
@ 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:50
@ 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:56
@ 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:63
@ 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 > ZoomLevel::Min) When shifting right,...
Definition zoom_func.h:22
@ Min
Minimum zoom level.