OpenTTD Source 20250818-master-g1850ad1aa2
window.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "company_func.h"
12#include "gfx_func.h"
13#include "console_func.h"
14#include "console_gui.h"
15#include "viewport_func.h"
16#include "progress.h"
17#include "blitter/factory.hpp"
18#include "zoom_func.h"
19#include "vehicle_base.h"
20#include "depot_func.h"
21#include "window_func.h"
22#include "tilehighlight_func.h"
23#include "network/network.h"
24#include "querystring_gui.h"
25#include "strings_func.h"
26#include "settings_type.h"
27#include "settings_func.h"
28#include "ini_type.h"
29#include "newgrf_debug.h"
30#include "hotkeys.h"
31#include "toolbar_gui.h"
32#include "statusbar_gui.h"
33#include "error.h"
34#include "game/game.hpp"
36#include "framerate_type.h"
38#include "news_func.h"
39#include "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 if (instant_close) {
291 /* Send event for selected option if we're still
292 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
293 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
294 this->OnDropdownSelect(widget, index, click_result);
295 }
296 }
297
298 /* Raise the dropdown button */
299 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
300 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
302 } else {
303 this->RaiseWidget(widget);
304 }
305 this->SetWidgetDirty(widget);
306}
307
314{
315 return this->GetWidget<NWidgetScrollbar>(widnum);
316}
317
324{
325 return this->GetWidget<NWidgetScrollbar>(widnum);
326}
327
334{
335 auto query = this->querystrings.find(widnum);
336 return query != this->querystrings.end() ? query->second : nullptr;
337}
338
345{
346 auto query = this->querystrings.find(widnum);
347 return query != this->querystrings.end() ? query->second : nullptr;
348}
349
354{
355 for (auto &qs : this->querystrings) {
356 qs.second->text.UpdateSize();
357 }
358}
359
364/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
365{
366 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
367 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
368 }
369
370 return nullptr;
371}
372
377/* virtual */ Point Window::GetCaretPosition() const
378{
379 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
380 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
381 }
382
383 Point pt = {0, 0};
384 return pt;
385}
386
393/* virtual */ Rect Window::GetTextBoundingRect(size_t from, size_t to) const
394{
395 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
396 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
397 }
398
399 Rect r = {0, 0, 0, 0};
400 return r;
401}
402
408/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
409{
410 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
411 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
412 }
413
414 return -1;
415}
416
422{
423 if (_focused_window == w) return;
424
425 /* Don't focus a tooltip */
426 if (w != nullptr && w->window_class == WC_TOOLTIPS) return;
427
428 /* Invalidate focused widget */
429 if (_focused_window != nullptr) {
430 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
431 }
432
433 /* Remember which window was previously focused */
434 Window *old_focused = _focused_window;
435 _focused_window = w;
436
437 /* So we can inform it that it lost focus */
438 if (old_focused != nullptr) old_focused->OnFocusLost(false);
439 if (_focused_window != nullptr) _focused_window->OnFocus();
440}
441
448{
449 if (_focused_window == nullptr) return false;
450
451 /* The console does not have an edit box so a special case is needed. */
452 if (_focused_window->window_class == WC_CONSOLE) return true;
453
454 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
455}
456
462{
463 return _focused_window && _focused_window->window_class == WC_CONSOLE;
464}
465
470{
471 if (this->nested_focus != nullptr) {
473
474 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
475 this->nested_focus->SetDirty(this);
476 this->nested_focus = nullptr;
477 }
478}
479
486{
487 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
488 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
489
490 if (this->nested_focus != nullptr) {
491 /* Do nothing if widget_index is already focused. */
492 if (widget == this->nested_focus) return false;
493
494 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
495 this->nested_focus->SetDirty(this);
497 }
498
499 this->nested_focus = widget;
501 return true;
502}
503
504std::string Window::GetWidgetString([[maybe_unused]] WidgetID widget, StringID stringid) const
505{
506 if (stringid == STR_NULL) return {};
507 return GetString(stringid);
508}
509
514{
516}
517
522{
524}
525
530void Window::RaiseButtons(bool autoraise)
531{
532 for (auto &pair : this->widget_lookup) {
533 WidgetType type = pair.second->type;
534 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
535 if (wid != nullptr && ((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
536 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
537 wid->SetLowered(false);
538 wid->SetDirty(this);
539 }
540 }
541
542 /* Special widgets without widget index */
543 {
544 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
545 if (wid != nullptr) {
546 wid->SetLowered(false);
547 wid->SetDirty(this);
548 }
549 }
550}
551
556void Window::SetWidgetDirty(WidgetID widget_index) const
557{
558 /* Sometimes this function is called before the window is even fully initialized */
559 auto it = this->widget_lookup.find(widget_index);
560 if (it == std::end(this->widget_lookup)) return;
561
562 it->second->SetDirty(this);
563}
564
571{
572 if (hotkey < 0) return ES_NOT_HANDLED;
573
574 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
575 if (nw == nullptr || nw->IsDisabled()) return ES_NOT_HANDLED;
576
577 if (nw->type == WWT_EDITBOX) {
578 if (this->IsShaded()) return ES_NOT_HANDLED;
579
580 /* Focus editbox */
581 this->SetFocusedWidget(hotkey);
582 SetFocusedWindow(this);
583 } else {
584 /* Click button */
585 this->OnClick(Point(), hotkey, 1);
586 }
587 return ES_HANDLED;
588}
589
596{
597 this->LowerWidget(widget);
598 this->SetTimeout();
599 this->SetWidgetDirty(widget);
600 SndClickBeep();
601}
602
603static void StartWindowDrag(Window *w);
604static void StartWindowSizing(Window *w, bool to_left);
605
613static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
614{
615 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
616 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
617
618 /* Allow dropdown close flag detection to work. */
620
621 bool focused_widget_changed = false;
622 /* If clicked on a window that previously did not have focus */
623 if (_focused_window != w && // We already have focus, right?
624 !w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && // Don't lose focus to toolbars
625 widget_type != WWT_CLOSEBOX) { // Don't change focused window if 'X' (close button) was clicked
626 focused_widget_changed = true;
628 }
629
630 if (nw == nullptr) return; // exit if clicked outside of widgets
631
632 /* don't allow any interaction if the button has been disabled */
633 if (nw->IsDisabled()) return;
634
635 WidgetID widget_index = nw->GetIndex();
636
637 /* Clicked on a widget that is not disabled.
638 * So unless the clicked widget is the caption bar, change focus to this widget.
639 * Exception: In the OSK we always want the editbox to stay focused. */
640 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
641 /* focused_widget_changed is 'now' only true if the window this widget
642 * is in gained focus. In that case it must remain true, also if the
643 * local widget focus did not change. As such it's the logical-or of
644 * both changed states.
645 *
646 * If this is not preserved, then the OSK window would be opened when
647 * a user has the edit box focused and then click on another window and
648 * then back again on the edit box (to type some text).
649 */
650 focused_widget_changed |= w->SetFocusedWidget(widget_index);
651 }
652
653 /* Dropdown window of this widget was closed so don't process click this time. */
655
656 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
657
658 Point pt = { x, y };
659
660 switch (widget_type) {
661 case NWID_VSCROLLBAR:
662 case NWID_HSCROLLBAR:
663 ScrollbarClickHandler(w, nw, x, y);
664 break;
665
666 case WWT_EDITBOX: {
667 QueryString *query = w->GetQueryString(widget_index);
668 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
669 break;
670 }
671
672 case WWT_CLOSEBOX: // 'X'
673 w->Close();
674 return;
675
676 case WWT_CAPTION: // 'Title bar'
678 return;
679
680 case WWT_RESIZEBOX:
681 /* When the resize widget is on the left size of the window
682 * we assume that that button is used to resize to the left. */
683 StartWindowSizing(w, nw->pos_x < (w->width / 2));
684 nw->SetDirty(w);
685 return;
686
687 case WWT_DEFSIZEBOX: {
688 if (_ctrl_pressed) {
689 if (click_count > 1) {
690 w->window_desc.pref_width = 0;
692 } else {
695 }
696 } else {
697 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
698 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
699
700 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
701 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
702 /* dx and dy has to go by step.. calculate it.
703 * The cast to int is necessary else dx/dy are implicitly casted to unsigned int, which won't work. */
704 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
705 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
706 ResizeWindow(w, dx, dy, false);
707 }
708
709 nw->SetLowered(true);
710 nw->SetDirty(w);
711 w->SetTimeout();
712 break;
713 }
714
715 case WWT_DEBUGBOX:
717 break;
718
719 case WWT_SHADEBOX:
720 nw->SetDirty(w);
721 w->SetShaded(!w->IsShaded());
722 return;
723
724 case WWT_STICKYBOX:
726 nw->SetDirty(w);
728 return;
729
730 default:
731 break;
732 }
733
734 /* Widget has no index, so the window is not interested in it. */
735 if (widget_index < 0) return;
736
737 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
738 if (w->IsWidgetHighlighted(widget_index)) {
739 w->SetWidgetHighlight(widget_index, TC_INVALID);
740 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
741 }
742
743 w->OnClick(pt, widget_index, click_count);
744}
745
752static void DispatchRightClickEvent(Window *w, int x, int y)
753{
754 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
755 if (wid == nullptr) return;
756
757 Point pt = { x, y };
758
759 /* No widget to handle, or the window is not interested in it. */
760 if (wid->GetIndex() >= 0) {
761 if (w->OnRightClick(pt, wid->GetIndex())) return;
762 }
763
764 /* Right-click close is enabled and there is a closebox. */
766 w->Close();
768 /* Right-click close is enabled, but excluding sticky windows. */
769 w->Close();
770 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
771 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_RIGHT_CLICK);
772 }
773}
774
781static void DispatchHoverEvent(Window *w, int x, int y)
782{
783 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
784
785 /* No widget to handle */
786 if (wid == nullptr) return;
787
788 Point pt = { x, y };
789
790 /* Show the tooltip if there is any */
791 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
792 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_HOVER);
793 return;
794 }
795
796 /* Widget has no index, so the window is not interested in it. */
797 if (wid->GetIndex() < 0) return;
798
799 w->OnHover(pt, wid->GetIndex());
800}
801
809static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
810{
811 if (nwid == nullptr) return;
812
813 /* Using wheel on caption/shade-box shades or unshades the window. */
814 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
815 w->SetShaded(wheel < 0);
816 return;
817 }
818
819 /* Wheeling a vertical scrollbar. */
820 if (nwid->type == NWID_VSCROLLBAR) {
821 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
822 if (sb->GetCount() > sb->GetCapacity()) {
823 if (sb->UpdatePosition(wheel)) {
824 w->OnScrollbarScroll(nwid->GetIndex());
825 w->SetDirty();
826 }
827 }
828 return;
829 }
830
831 /* Scroll the widget attached to the scrollbar. */
832 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
833 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
834 if (sb->UpdatePosition(wheel)) {
836 w->SetDirty();
837 }
838 }
839}
840
846static bool MayBeShown(const Window *w)
847{
848 /* If we're not modal, everything is okay. */
849 if (!HasModalProgress()) return true;
850
851 switch (w->window_class) {
852 case WC_MAIN_WINDOW:
853 case WC_MODAL_PROGRESS:
855 return true;
856
857 default:
858 return false;
859 }
860}
861
874static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
875{
877 ++it;
878 for (; !it.IsEnd(); ++it) {
879 const Window *v = *it;
880 if (MayBeShown(v) &&
881 right > v->left &&
882 bottom > v->top &&
883 left < v->left + v->width &&
884 top < v->top + v->height) {
885 /* v and rectangle intersect with each other */
886 int x;
887
888 if (left < (x = v->left)) {
889 DrawOverlappedWindow(w, left, top, x, bottom);
890 DrawOverlappedWindow(w, x, top, right, bottom);
891 return;
892 }
893
894 if (right > (x = v->left + v->width)) {
895 DrawOverlappedWindow(w, left, top, x, bottom);
896 DrawOverlappedWindow(w, x, top, right, bottom);
897 return;
898 }
899
900 if (top < (x = v->top)) {
901 DrawOverlappedWindow(w, left, top, right, x);
902 DrawOverlappedWindow(w, left, x, right, bottom);
903 return;
904 }
905
906 if (bottom > (x = v->top + v->height)) {
907 DrawOverlappedWindow(w, left, top, right, x);
908 DrawOverlappedWindow(w, left, x, right, bottom);
909 return;
910 }
911
912 return;
913 }
914 }
915
916 /* Setup blitter, and dispatch a repaint event to window *wz */
917 DrawPixelInfo *dp = _cur_dpi;
918 dp->width = right - left;
919 dp->height = bottom - top;
920 dp->left = left - w->left;
921 dp->top = top - w->top;
922 dp->pitch = _screen.pitch;
923 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
924 dp->zoom = ZoomLevel::Min;
925 w->OnPaint();
926}
927
936void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
937{
938 DrawPixelInfo bk;
939 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
940
941 for (Window *w : Window::IterateFromBack()) {
942 if (MayBeShown(w) &&
943 right > w->left &&
944 bottom > w->top &&
945 left < w->left + w->width &&
946 top < w->top + w->height) {
947 /* Window w intersects with the rectangle => needs repaint */
948 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));
949 }
950 }
951}
952
958{
959 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
960}
961
969void Window::ReInit(int rx, int ry, bool reposition)
970{
971 this->SetDirty(); // Mark whole current window as dirty.
972
973 /* Save current size. */
974 int window_width = this->width * _gui_scale / this->scale;
975 int window_height = this->height * _gui_scale / this->scale;
976 this->scale = _gui_scale;
977
978 this->OnInit();
979 /* Re-initialize window smallest size. */
980 this->nested_root->SetupSmallestSize(this);
981 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
982 this->width = this->nested_root->smallest_x;
983 this->height = this->nested_root->smallest_y;
984 this->resize.step_width = this->nested_root->resize_x;
985 this->resize.step_height = this->nested_root->resize_y;
986
987 /* Resize as close to the original size + requested resize as possible. */
988 window_width = std::max(window_width + rx, this->width);
989 window_height = std::max(window_height + ry, this->height);
990 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
991 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
992 /* dx and dy has to go by step.. calculate it.
993 * The cast to int is necessary else dx/dy are implicitly casted to unsigned int, which won't work. */
994 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
995 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
996
997 if (reposition) {
998 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
999 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1000 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1001 }
1002
1003 ResizeWindow(this, dx, dy, true, false);
1004 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1005}
1006
1012void Window::SetShaded(bool make_shaded)
1013{
1014 if (this->shade_select == nullptr) return;
1015
1016 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1017 if (this->shade_select->shown_plane != desired) {
1018 if (make_shaded) {
1019 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1020 this->unshaded_size.width = this->width;
1021 this->unshaded_size.height = this->height;
1022 this->shade_select->SetDisplayedPlane(desired);
1023 this->ReInit(0, -this->height);
1024 } else {
1025 this->shade_select->SetDisplayedPlane(desired);
1026 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1027 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1028 this->ReInit(dx, dy);
1029 }
1030 }
1031}
1032
1039{
1040 for (Window *v : Window::Iterate()) {
1041 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1042 }
1043
1044 return nullptr;
1045}
1046
1054{
1055 for (Window *v : Window::Iterate()) {
1056 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1057 }
1058
1059 return nullptr;
1060}
1061
1067{
1068 Window *child = this->FindChildWindow(wc);
1069 while (child != nullptr) {
1070 child->Close();
1071 child = this->FindChildWindow(wc);
1072 }
1073}
1074
1075
1082{
1083 Window *child = this->FindChildWindowById(wc, number);
1084 while (child != nullptr) {
1085 child->Close();
1086 child = this->FindChildWindowById(wc, number);
1087 }
1088}
1089
1093void Window::Close([[maybe_unused]] int data)
1094{
1095 /* Don't close twice. */
1096 if (*this->z_position == nullptr) return;
1097
1098 *this->z_position = nullptr;
1099
1100 if (_thd.window_class == this->window_class &&
1101 _thd.window_number == this->window_number) {
1103 }
1104
1105 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1106 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1107
1108 /* We can't scroll the window when it's closed. */
1109 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1110
1111 /* Make sure we don't try to access non-existing query strings. */
1112 this->querystrings.clear();
1113
1114 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1115 if (_focused_window == this) {
1116 this->OnFocusLost(true);
1117 _focused_window = nullptr;
1118 }
1119
1120 this->CloseChildWindows();
1121
1122 this->SetDirty();
1123
1124 Window::closed_windows.push_back(this);
1125}
1126
1131{
1132 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1133 assert(*this->z_position == nullptr);
1134}
1135
1143{
1144 for (Window *w : Window::Iterate()) {
1145 if (w->window_class == cls && w->window_number == number) return w;
1146 }
1147
1148 return nullptr;
1149}
1150
1158{
1159 for (Window *w : Window::Iterate()) {
1160 if (w->window_class == cls) return w;
1161 }
1162
1163 return nullptr;
1164}
1165
1172{
1174 assert(w != nullptr);
1175 return w;
1176}
1177
1184void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1185{
1186 Window *w = FindWindowById(cls, number);
1187 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1188 w->Close(data);
1189 }
1190}
1191
1197{
1198 /* Note: the container remains stable, even when deleting windows. */
1199 for (Window *w : Window::Iterate()) {
1200 if (w->window_class == cls) {
1201 w->Close(data);
1202 }
1203 }
1204}
1205
1213{
1214 /* Note: the container remains stable, even when deleting windows. */
1215 for (Window *w : Window::Iterate()) {
1216 if (w->owner == id) {
1217 w->Close();
1218 }
1219 }
1220
1221 /* Also delete the company specific windows that don't have a company-colour. */
1223}
1224
1232void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1233{
1234 for (Window *w : Window::Iterate()) {
1235 if (w->owner != old_owner) continue;
1236
1237 switch (w->window_class) {
1238 case WC_COMPANY_COLOUR:
1239 case WC_FINANCES:
1240 case WC_STATION_LIST:
1241 case WC_TRAINS_LIST:
1242 case WC_ROADVEH_LIST:
1243 case WC_SHIPS_LIST:
1244 case WC_AIRCRAFT_LIST:
1245 case WC_BUY_COMPANY:
1246 case WC_COMPANY:
1248 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().
1249 continue;
1250
1251 default:
1252 w->owner = new_owner;
1253 break;
1254 }
1255 }
1256}
1257
1258static void BringWindowToFront(Window *w, bool dirty = true);
1259
1268{
1269 Window *w = FindWindowById(cls, number);
1270
1271 if (w != nullptr) {
1272 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1273
1274 w->SetWhiteBorder();
1276 w->SetDirty();
1277 }
1278
1279 return w;
1280}
1281
1282static inline bool IsVitalWindow(const Window *w)
1283{
1284 switch (w->window_class) {
1285 case WC_MAIN_TOOLBAR:
1286 case WC_STATUS_BAR:
1287 case WC_NEWS_WINDOW:
1289 return true;
1290
1291 default:
1292 return false;
1293 }
1294}
1295
1305{
1306 assert(wc != WC_INVALID);
1307
1308 uint z_priority = 0;
1309
1310 switch (wc) {
1311 case WC_TOOLTIPS:
1312 ++z_priority;
1313 [[fallthrough]];
1314
1315 case WC_ERRMSG:
1317 ++z_priority;
1318 [[fallthrough]];
1319
1320 case WC_ENDSCREEN:
1321 ++z_priority;
1322 [[fallthrough]];
1323
1324 case WC_HIGHSCORE:
1325 ++z_priority;
1326 [[fallthrough]];
1327
1328 case WC_DROPDOWN_MENU:
1329 ++z_priority;
1330 [[fallthrough]];
1331
1332 case WC_MAIN_TOOLBAR:
1333 case WC_STATUS_BAR:
1334 ++z_priority;
1335 [[fallthrough]];
1336
1337 case WC_OSK:
1338 ++z_priority;
1339 [[fallthrough]];
1340
1341 case WC_QUERY_STRING:
1343 ++z_priority;
1344 [[fallthrough]];
1345
1347 case WC_MODAL_PROGRESS:
1349 case WC_SAVE_PRESET:
1350 ++z_priority;
1351 [[fallthrough]];
1352
1354 case WC_SAVELOAD:
1355 case WC_GAME_OPTIONS:
1356 case WC_CUSTOM_CURRENCY:
1357 case WC_NETWORK_WINDOW:
1358 case WC_GRF_PARAMETERS:
1359 case WC_SCRIPT_LIST:
1360 case WC_SCRIPT_SETTINGS:
1361 case WC_TEXTFILE:
1362 ++z_priority;
1363 [[fallthrough]];
1364
1365 case WC_CONSOLE:
1366 ++z_priority;
1367 [[fallthrough]];
1368
1369 case WC_NEWS_WINDOW:
1370 ++z_priority;
1371 [[fallthrough]];
1372
1373 default:
1374 ++z_priority;
1375 [[fallthrough]];
1376
1377 case WC_MAIN_WINDOW:
1378 return z_priority;
1379 }
1380}
1381
1388static void BringWindowToFront(Window *w, bool dirty)
1389{
1390 auto priority = GetWindowZPriority(w->window_class);
1391 WindowList::iterator dest = _z_windows.begin();
1392 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1393
1394 if (dest != w->z_position) {
1395 _z_windows.splice(dest, _z_windows, w->z_position);
1396 }
1397
1398 if (dirty) w->SetDirty();
1399}
1400
1409{
1410 /* Set up window properties; some of them are needed to set up smallest size below */
1411 this->window_class = this->window_desc.cls;
1412 this->SetWhiteBorder();
1414 this->owner = INVALID_OWNER;
1415 this->nested_focus = nullptr;
1416 this->window_number = window_number;
1417
1418 this->OnInit();
1419 /* Initialize smallest size. */
1420 this->nested_root->SetupSmallestSize(this);
1421 /* Initialize to smallest size. */
1422 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1423
1424 /* Further set up window properties,
1425 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1426 this->resize.step_width = this->nested_root->resize_x;
1427 this->resize.step_height = this->nested_root->resize_y;
1428
1429 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1430 * (so we don't interrupt typing) unless the new window has a text box. */
1431 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1432 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1433 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1434
1435 /* Insert the window into the correct location in the z-ordering. */
1436 BringWindowToFront(this, false);
1437}
1438
1446void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1447{
1448 this->left = x;
1449 this->top = y;
1450 this->width = sm_width;
1451 this->height = sm_height;
1452}
1453
1465void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1466{
1467 if (allow_resize) {
1468 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1469 def_height = std::max(def_height, this->height);
1470 /* Try to make windows smaller when our window is too small.
1471 * w->(width|height) is normally the same as min_(width|height),
1472 * but this way the GUIs can be made a little more dynamic;
1473 * one can use the same spec for multiple windows and those
1474 * can then determine the real minimum size of the window. */
1475 if (this->width != def_width || this->height != def_height) {
1476 /* Think about the overlapping toolbars when determining the minimum window size */
1477 int free_height = _screen.height;
1478 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1479 if (wt != nullptr) free_height -= wt->height;
1481 if (wt != nullptr) free_height -= wt->height;
1482
1483 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1484 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1485
1486 /* X and Y has to go by step.. calculate it.
1487 * The cast to int is necessary else x/y are implicitly casted to
1488 * unsigned int, which won't work. */
1489 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1490 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1491
1492 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1493 /* ResizeWindow() calls this->OnResize(). */
1494 } else {
1495 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1496 this->OnResize();
1497 }
1498 }
1499
1500 int nx = this->left;
1501 int ny = this->top;
1502
1503 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1504
1505 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1506 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1507 nx = std::max(nx, 0);
1508
1509 if (this->viewport != nullptr) {
1510 this->viewport->left += nx - this->left;
1511 this->viewport->top += ny - this->top;
1512 }
1513 this->left = nx;
1514 this->top = ny;
1515
1516 this->SetDirty();
1517}
1518
1531static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1532{
1533 int right = width + left;
1534 int bottom = height + top;
1535
1536 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1537
1538 /* Make sure it is not obscured by any window. */
1539 for (const Window *w : Window::Iterate()) {
1540 if (w->window_class == WC_MAIN_WINDOW) continue;
1541
1542 if (right > w->left &&
1543 w->left + w->width > left &&
1544 bottom > w->top &&
1545 w->top + w->height > top) {
1546 return false;
1547 }
1548 }
1549
1550 pos.x = left;
1551 pos.y = top;
1552 return true;
1553}
1554
1567static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1568{
1569 bool rtl = _current_text_dir == TD_RTL;
1570
1571 /* Left part of the rectangle may be at most 1/4 off-screen,
1572 * right part of the rectangle may be at most 1/2 off-screen
1573 */
1574 if (rtl) {
1575 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1576 } else {
1577 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1578 }
1579
1580 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1581 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1582
1583 /* Make sure it is not obscured by any window. */
1584 for (const Window *w : Window::Iterate()) {
1585 if (w->window_class == WC_MAIN_WINDOW) continue;
1586
1587 if (left + width > w->left &&
1588 w->left + w->width > left &&
1589 top + height > w->top &&
1590 w->top + w->height > top) {
1591 return false;
1592 }
1593 }
1594
1595 pos.x = left;
1596 pos.y = top;
1597 return true;
1598}
1599
1606static Point GetAutoPlacePosition(int width, int height)
1607{
1608 Point pt;
1609
1610 bool rtl = _current_text_dir == TD_RTL;
1611
1612 /* First attempt, try top-left of the screen */
1613 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1614 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1615 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1616
1617 /* Second attempt, try around all existing windows.
1618 * The new window must be entirely on-screen, and not overlap with an existing window.
1619 * Eight starting points are tried, two at each corner.
1620 */
1621 for (const Window *w : Window::Iterate()) {
1622 if (w->window_class == WC_MAIN_WINDOW) continue;
1623
1624 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1625 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1626 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1627 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1628 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1629 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1630 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1631 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1632 }
1633
1634 /* Third attempt, try around all existing windows.
1635 * The new window may be partly off-screen, and must not overlap with an existing window.
1636 * Only four starting points are tried.
1637 */
1638 for (const Window *w : Window::Iterate()) {
1639 if (w->window_class == WC_MAIN_WINDOW) continue;
1640
1641 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1642 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1643 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1644 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1645 }
1646
1647 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1648 * of the closebox
1649 */
1650 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1651 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1652 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1653
1654restart:
1655 for (const Window *w : Window::Iterate()) {
1656 if (w->left == left && w->top == top) {
1657 left += offset_x;
1658 top += offset_y;
1659 goto restart;
1660 }
1661 }
1662
1663 pt.x = left;
1664 pt.y = top;
1665 return pt;
1666}
1667
1675{
1676 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1677 assert(w != nullptr);
1678 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1679 return pt;
1680}
1681
1699static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1700{
1701 Point pt;
1702 const Window *w;
1703
1704 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1705 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1706
1707 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1708 bool rtl = _current_text_dir == TD_RTL;
1709 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1710 pt.x = w->left + (rtl ? w->width - default_width : 0);
1711 pt.y = w->top + w->height;
1712 return pt;
1713 } else {
1714 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1715 * - Y position: closebox of parent + closebox of child + statusbar
1716 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1717 */
1718 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1719 if (w->top + 3 * indent_y < _screen.height) {
1720 pt.y = w->top + indent_y;
1721 int indent_close = NWidgetLeaf::closebox_dimension.width;
1722 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1723 if (_current_text_dir == TD_RTL) {
1724 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1725 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1726 } else {
1727 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1728 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1729 }
1730 }
1731 }
1732 }
1733
1734 switch (desc.default_pos) {
1735 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1736 return GetToolbarAlignedWindowPosition(default_width);
1737
1738 case WDP_AUTO: // Find a good automatic position for the window
1739 return GetAutoPlacePosition(default_width, default_height);
1740
1741 case WDP_CENTER: // Centre the window horizontally
1742 pt.x = (_screen.width - default_width) / 2;
1743 pt.y = (_screen.height - default_height) / 2;
1744 break;
1745
1746 case WDP_MANUAL:
1747 pt.x = 0;
1748 pt.y = 0;
1749 break;
1750
1751 default:
1752 NOT_REACHED();
1753 }
1754
1755 return pt;
1756}
1757
1758/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1759{
1760 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1761}
1762
1771{
1772 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1773 this->nested_root->FillWidgetLookup(this->widget_lookup);
1774}
1775
1781{
1782 this->InitializeData(window_number);
1783 this->ApplyDefaults();
1784 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1785 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1786 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1787}
1788
1794{
1795 this->CreateNestedTree();
1796 this->FinishInitNested(window_number);
1797}
1798
1803Window::Window(WindowDesc &desc) : window_desc(desc), scale(_gui_scale), mouse_capture_widget(-1)
1804{
1805 this->z_position = _z_windows.insert(_z_windows.end(), this);
1806}
1807
1816{
1817 for (Window *w : Window::IterateFromFront()) {
1818 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1819 return w;
1820 }
1821 }
1822
1823 return nullptr;
1824}
1825
1830{
1831 IConsoleClose();
1832
1833 _focused_window = nullptr;
1834 _mouseover_last_w = nullptr;
1835 _last_scroll_window = nullptr;
1836 _scrolling_viewport = false;
1837 _mouse_hovering = false;
1838
1840 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1841 NWidgetScrollbar::InvalidateDimensionCache();
1842
1844
1846}
1847
1852{
1854
1855 for (Window *w : Window::Iterate()) w->Close();
1856
1858
1859 assert(_z_windows.empty());
1860}
1861
1866{
1869 _thd.Reset();
1870}
1871
1872static void DecreaseWindowCounters()
1873{
1874 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1875
1876 for (Window *w : Window::Iterate()) {
1877 if (_scroller_click_timeout == 0) {
1878 /* Unclick scrollbar buttons if they are pressed. */
1879 for (auto &pair : w->widget_lookup) {
1880 NWidgetBase *nwid = pair.second;
1881 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1882 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1883 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1885 w->mouse_capture_widget = -1;
1886 sb->SetDirty(w);
1887 }
1888 }
1889 }
1890 }
1891
1892 /* Handle editboxes */
1893 for (auto &pair : w->querystrings) {
1894 pair.second->HandleEditBox(w, pair.first);
1895 }
1896
1897 w->OnMouseLoop();
1898 }
1899
1900 for (Window *w : Window::Iterate()) {
1901 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1903
1904 w->OnTimeout();
1905 w->RaiseButtons(true);
1906 }
1907 }
1908}
1909
1910static void HandlePlacePresize()
1911{
1912 if (_special_mouse_mode != WSM_PRESIZE) return;
1913
1914 Window *w = _thd.GetCallbackWnd();
1915 if (w == nullptr) return;
1916
1917 Point pt = GetTileBelowCursor();
1918 if (pt.x == -1) {
1919 _thd.selend.x = -1;
1920 return;
1921 }
1922
1923 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1924}
1925
1931{
1933
1934 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1935
1936 Window *w = _thd.GetCallbackWnd();
1937 if (w != nullptr) {
1938 /* Send an event in client coordinates. */
1939 Point pt;
1940 pt.x = _cursor.pos.x - w->left;
1941 pt.y = _cursor.pos.y - w->top;
1942 if (_left_button_down) {
1943 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1944 } else {
1945 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1946 }
1947 }
1948
1949 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1950 return ES_HANDLED;
1951}
1952
1954static void HandleMouseOver()
1955{
1956 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1957
1958 /* We changed window, put an OnMouseOver event to the last window */
1959 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1960 /* Reset mouse-over coordinates of previous window */
1961 Point pt = { -1, -1 };
1963 }
1964
1965 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1967
1968 if (w != nullptr) {
1969 /* send an event in client coordinates. */
1970 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
1971 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
1972 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
1973 }
1974}
1975
1981
1992static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
1993{
1994 if (v == nullptr) return;
1995
1996 const int min_visible = rect.Height();
1997
1998 int v_bottom = v->top + v->height - 1;
1999 int v_right = v->left + v->width - 1;
2000 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2001
2002 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2003 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2004
2005 /* Vertically, the rectangle is hidden behind v. */
2006 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2007 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2008 return;
2009 }
2010 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2011 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2012 return;
2013 }
2014
2015 /* Horizontally also hidden, force movement to a safe area. */
2016 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2017 *nx = v->left - min_visible - rect.left;
2018 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2019 *nx = v_right + min_visible - rect.right;
2020 } else {
2021 *ny = safe_y;
2022 }
2023}
2024
2032static void EnsureVisibleCaption(Window *w, int nx, int ny)
2033{
2034 /* Search for the title bar rectangle. */
2035 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2036 if (caption != nullptr) {
2037 const Rect caption_rect = caption->GetCurrentRect();
2038
2039 const int min_visible = caption_rect.Height();
2040
2041 /* Make sure the window doesn't leave the screen */
2042 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2043 ny = Clamp(ny, 0, _screen.height - min_visible);
2044
2045 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2046 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2047 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2048 }
2049
2050 if (w->viewport != nullptr) {
2051 w->viewport->left += nx - w->left;
2052 w->viewport->top += ny - w->top;
2053 }
2054
2055 w->left = nx;
2056 w->top = ny;
2057}
2058
2069void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2070{
2071 if (delta_x != 0 || delta_y != 0) {
2072 if (clamp_to_screen) {
2073 /* Determine the new right/bottom position. If that is outside of the bounds of
2074 * the resolution clamp it in such a manner that it stays within the bounds. */
2075 int new_right = w->left + w->width + delta_x;
2076 int new_bottom = w->top + w->height + delta_y;
2077 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2078 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2079 }
2080
2081 w->SetDirty();
2082
2083 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);
2084 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);
2085 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2086 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2087
2088 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);
2089 w->width = w->nested_root->current_x;
2090 w->height = w->nested_root->current_y;
2091 }
2092
2093 EnsureVisibleCaption(w, w->left, w->top);
2094
2095 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2096 if (schedule_resize) {
2097 w->ScheduleResize();
2098 } else {
2099 w->OnResize();
2100 }
2101 w->SetDirty();
2102}
2103
2110{
2112 return (w == nullptr) ? 0 : w->top + w->height;
2113}
2114
2121{
2123 return (w == nullptr) ? _screen.height : w->top;
2124}
2125
2126static bool _dragging_window;
2127
2133{
2134 /* Get out immediately if no window is being dragged at all. */
2135 if (!_dragging_window) return ES_NOT_HANDLED;
2136
2137 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2138 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2139
2140 /* Otherwise find the window... */
2141 for (Window *w : Window::Iterate()) {
2143 /* Stop the dragging if the left mouse button was released */
2144 if (!_left_button_down) {
2146 break;
2147 }
2148
2149 w->SetDirty();
2150
2151 int x = _cursor.pos.x + _drag_delta.x;
2152 int y = _cursor.pos.y + _drag_delta.y;
2153 int nx = x;
2154 int ny = y;
2155
2159 int delta;
2160
2161 for (const Window *v : Window::Iterate()) {
2162 if (v == w) continue; // Don't snap at yourself
2163
2164 if (y + w->height > v->top && y < v->top + v->height) {
2165 /* Your left border <-> other right border */
2166 delta = abs(v->left + v->width - x);
2167 if (delta <= hsnap) {
2168 nx = v->left + v->width;
2169 hsnap = delta;
2170 }
2171
2172 /* Your right border <-> other left border */
2173 delta = abs(v->left - x - w->width);
2174 if (delta <= hsnap) {
2175 nx = v->left - w->width;
2176 hsnap = delta;
2177 }
2178 }
2179
2180 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2181 /* Your left border <-> other left border */
2182 delta = abs(v->left - x);
2183 if (delta <= hsnap) {
2184 nx = v->left;
2185 hsnap = delta;
2186 }
2187
2188 /* Your right border <-> other right border */
2189 delta = abs(v->left + v->width - x - w->width);
2190 if (delta <= hsnap) {
2191 nx = v->left + v->width - w->width;
2192 hsnap = delta;
2193 }
2194 }
2195
2196 if (x + w->width > v->left && x < v->left + v->width) {
2197 /* Your top border <-> other bottom border */
2198 delta = abs(v->top + v->height - y);
2199 if (delta <= vsnap) {
2200 ny = v->top + v->height;
2201 vsnap = delta;
2202 }
2203
2204 /* Your bottom border <-> other top border */
2205 delta = abs(v->top - y - w->height);
2206 if (delta <= vsnap) {
2207 ny = v->top - w->height;
2208 vsnap = delta;
2209 }
2210 }
2211
2212 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2213 /* Your top border <-> other top border */
2214 delta = abs(v->top - y);
2215 if (delta <= vsnap) {
2216 ny = v->top;
2217 vsnap = delta;
2218 }
2219
2220 /* Your bottom border <-> other bottom border */
2221 delta = abs(v->top + v->height - y - w->height);
2222 if (delta <= vsnap) {
2223 ny = v->top + v->height - w->height;
2224 vsnap = delta;
2225 }
2226 }
2227 }
2228 }
2229
2230 EnsureVisibleCaption(w, nx, ny);
2231
2232 w->SetDirty();
2233 return ES_HANDLED;
2235 /* Stop the sizing if the left mouse button was released */
2236 if (!_left_button_down) {
2239 w->SetDirty();
2240 break;
2241 }
2242
2243 /* Compute difference in pixels between cursor position and reference point in the window.
2244 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2245 */
2246 int x, y = _cursor.pos.y - _drag_delta.y;
2248 x = _drag_delta.x - _cursor.pos.x;
2249 } else {
2250 x = _cursor.pos.x - _drag_delta.x;
2251 }
2252
2253 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2254 if (w->resize.step_width == 0) x = 0;
2255 if (w->resize.step_height == 0) y = 0;
2256
2257 /* Check the resize button won't go past the bottom of the screen */
2258 if (w->top + w->height + y > _screen.height) {
2259 y = _screen.height - w->height - w->top;
2260 }
2261
2262 /* X and Y has to go by step.. calculate it.
2263 * The cast to int is necessary else x/y are implicitly casted to
2264 * unsigned int, which won't work. */
2265 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2266 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2267
2268 /* Check that we don't go below the minimum set size */
2269 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2270 x = w->nested_root->smallest_x - w->width;
2271 }
2272 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2273 y = w->nested_root->smallest_y - w->height;
2274 }
2275
2276 /* Window already on size */
2277 if (x == 0 && y == 0) return ES_HANDLED;
2278
2279 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2280 _drag_delta.y += y;
2281 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2282 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2283 w->SetDirty();
2284 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2285 /* ResizeWindow() below ensures marking new position as dirty. */
2286 } else {
2287 _drag_delta.x += x;
2288 }
2289
2290 /* ResizeWindow sets both pre- and after-size to dirty for redrawal */
2291 ResizeWindow(w, x, y);
2292 return ES_HANDLED;
2293 }
2294 }
2295
2296 _dragging_window = false;
2297 return ES_HANDLED;
2298}
2299
2305{
2308 _dragging_window = true;
2309
2310 _drag_delta.x = w->left - _cursor.pos.x;
2311 _drag_delta.y = w->top - _cursor.pos.y;
2312
2314}
2315
2321static void StartWindowSizing(Window *w, bool to_left)
2322{
2325 _dragging_window = true;
2326
2327 _drag_delta.x = _cursor.pos.x;
2328 _drag_delta.y = _cursor.pos.y;
2329
2331}
2332
2338{
2339 int i;
2341 bool rtl = false;
2342
2343 if (sb->type == NWID_HSCROLLBAR) {
2344 i = _cursor.pos.x - _cursorpos_drag_start.x;
2345 rtl = _current_text_dir == TD_RTL;
2346 } else {
2347 i = _cursor.pos.y - _cursorpos_drag_start.y;
2348 }
2349
2350 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2351 if (_scroller_click_timeout == 1) {
2352 _scroller_click_timeout = 3;
2353 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2355 w->SetDirty();
2356 }
2357 }
2358 return;
2359 }
2360
2361 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2362 int range = sb->GetCount() - sb->GetCapacity();
2363 if (range <= 0) return;
2364
2365 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2366 if (rtl) pos = range - pos;
2367 if (sb->SetPosition(pos)) {
2369 w->SetDirty();
2370 }
2371}
2372
2378{
2379 for (Window *w : Window::Iterate()) {
2380 if (w->mouse_capture_widget >= 0) {
2381 /* Abort if no button is clicked any more. */
2382 if (!_left_button_down) {
2384 w->mouse_capture_widget = -1;
2385 return ES_HANDLED;
2386 }
2387
2388 /* Handle scrollbar internally, or dispatch click event */
2390 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2392 } else {
2393 /* If cursor hasn't moved, there is nothing to do. */
2394 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2395
2396 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2397 w->OnClick(pt, w->mouse_capture_widget, 0);
2398 }
2399 return ES_HANDLED;
2400 }
2401 }
2402
2403 return ES_NOT_HANDLED;
2404}
2405
2411{
2412 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2413
2415
2416 /* When we don't have a last scroll window we are starting to scroll.
2417 * When the last scroll window and this are not the same we went
2418 * outside of the window and should not left-mouse scroll anymore. */
2419 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2420
2422 _cursor.fix_at = false;
2423 _scrolling_viewport = false;
2424 _last_scroll_window = nullptr;
2425 return ES_NOT_HANDLED;
2426 }
2427
2428 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2429 /* If the main window is following a vehicle, then first let go of it! */
2430 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
2431 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2432 return ES_NOT_HANDLED;
2433 }
2434
2435 Point delta;
2436 if (scrollwheel_scrolling) {
2437 /* We are using scrollwheels for scrolling */
2438 /* Use the integer part for movement */
2439 delta.x = static_cast<int>(_cursor.h_wheel);
2440 delta.y = static_cast<int>(_cursor.v_wheel);
2441 /* Keep the fractional part so that subtle movement is accumulated */
2442 float temp;
2443 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2444 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2445 } else {
2447 delta.x = -_cursor.delta.x;
2448 delta.y = -_cursor.delta.y;
2449 } else {
2450 delta.x = _cursor.delta.x;
2451 delta.y = _cursor.delta.y;
2452 }
2453 }
2454
2455 /* Create a scroll-event and send it to the window */
2456 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2457
2458 _cursor.delta.x = 0;
2459 _cursor.delta.y = 0;
2460 _cursor.wheel_moved = false;
2461 return ES_HANDLED;
2462}
2463
2475{
2476 bool bring_to_front = false;
2477
2478 if (w->window_class == WC_MAIN_WINDOW ||
2479 IsVitalWindow(w) ||
2480 w->window_class == WC_TOOLTIPS ||
2482 return true;
2483 }
2484
2485 /* Use unshaded window size rather than current size for shaded windows. */
2486 int w_width = w->width;
2487 int w_height = w->height;
2488 if (w->IsShaded()) {
2489 w_width = w->unshaded_size.width;
2490 w_height = w->unshaded_size.height;
2491 }
2492
2494 ++it;
2495 for (; !it.IsEnd(); ++it) {
2496 Window *u = *it;
2497 /* A modal child will prevent the activation of the parent window */
2499 u->SetWhiteBorder();
2500 u->SetDirty();
2501 return false;
2502 }
2503
2504 if (u->window_class == WC_MAIN_WINDOW ||
2505 IsVitalWindow(u) ||
2506 u->window_class == WC_TOOLTIPS ||
2508 continue;
2509 }
2510
2511 /* Window sizes don't interfere, leave z-order alone */
2512 if (w->left + w_width <= u->left ||
2513 u->left + u->width <= w->left ||
2514 w->top + w_height <= u->top ||
2515 u->top + u->height <= w->top) {
2516 continue;
2517 }
2518
2519 bring_to_front = true;
2520 }
2521
2522 if (bring_to_front) BringWindowToFront(w);
2523 return true;
2524}
2525
2534EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2535{
2536 QueryString *query = this->GetQueryString(wid);
2537 if (query == nullptr) return ES_NOT_HANDLED;
2538
2539 int action = QueryString::ACTION_NOTHING;
2540
2541 switch (query->text.HandleKeyPress(key, keycode)) {
2542 case HKPR_EDITING:
2543 this->SetWidgetDirty(wid);
2544 this->OnEditboxChanged(wid);
2545 break;
2546
2547 case HKPR_CURSOR:
2548 this->SetWidgetDirty(wid);
2549 /* For the OSK also invalidate the parent window */
2550 if (this->window_class == WC_OSK) this->InvalidateData();
2551 break;
2552
2553 case HKPR_CONFIRM:
2554 if (this->window_class == WC_OSK) {
2555 this->OnClick(Point(), WID_OSK_OK, 1);
2556 } else if (query->ok_button >= 0) {
2557 this->OnClick(Point(), query->ok_button, 1);
2558 } else {
2559 action = query->ok_button;
2560 }
2561 break;
2562
2563 case HKPR_CANCEL:
2564 if (this->window_class == WC_OSK) {
2565 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2566 } else if (query->cancel_button >= 0) {
2567 this->OnClick(Point(), query->cancel_button, 1);
2568 } else {
2569 action = query->cancel_button;
2570 }
2571 break;
2572
2573 case HKPR_NOT_HANDLED:
2574 return ES_NOT_HANDLED;
2575
2576 default: break;
2577 }
2578
2579 switch (action) {
2581 this->UnfocusFocusedWidget();
2582 break;
2583
2585 if (query->text.GetText().empty()) {
2586 /* If already empty, unfocus instead */
2587 this->UnfocusFocusedWidget();
2588 } else {
2589 query->text.DeleteAll();
2590 this->SetWidgetDirty(wid);
2591 this->OnEditboxChanged(wid);
2592 }
2593 break;
2594
2595 default:
2596 break;
2597 }
2598
2599 return ES_HANDLED;
2600}
2601
2606void HandleToolbarHotkey(int hotkey)
2607{
2608 assert(HasModalProgress() || IsLocalCompany());
2609
2611 if (w != nullptr) {
2612 if (w->window_desc.hotkeys != nullptr) {
2613 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2614 }
2615 }
2616}
2617
2623void HandleKeypress(uint keycode, char32_t key)
2624{
2625 /* World generation is multithreaded and messes with companies.
2626 * But there is no company related window open anyway, so _current_company is not used. */
2627 assert(HasModalProgress() || IsLocalCompany());
2628
2629 /*
2630 * The Unicode standard defines an area called the private use area. Code points in this
2631 * area are reserved for private use and thus not portable between systems. For instance,
2632 * Apple defines code points for the arrow keys in this area, but these are only printable
2633 * on a system running OS X. We don't want these keys to show up in text fields and such,
2634 * and thus we have to clear the unicode character when we encounter such a key.
2635 */
2636 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2637
2638 /*
2639 * If both key and keycode is zero, we don't bother to process the event.
2640 */
2641 if (key == 0 && keycode == 0) return;
2642
2643 /* Check if the focused window has a focused editbox */
2644 if (EditBoxInGlobalFocus()) {
2645 /* All input will in this case go to the focused editbox */
2646 if (_focused_window->window_class == WC_CONSOLE) {
2647 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2648 } else {
2649 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2650 }
2651 }
2652
2653 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2654 for (Window *w : Window::IterateFromFront()) {
2655 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2656 if (w->window_desc.hotkeys != nullptr) {
2657 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2658 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2659 }
2660 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2661 }
2662
2664 /* When there is no toolbar w is null, check for that */
2665 if (w != nullptr) {
2666 if (w->window_desc.hotkeys != nullptr) {
2667 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2668 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2669 }
2670 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2671 }
2672
2673 HandleGlobalHotkeys(key, keycode);
2674}
2675
2680{
2681 /* Call the event, start with the uppermost window. */
2682 for (Window *w : Window::IterateFromFront()) {
2683 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2684 }
2685}
2686
2692/* 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)
2693{
2694 QueryString *query = this->GetQueryString(wid);
2695 if (query == nullptr) return;
2696
2697 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2698 this->SetWidgetDirty(wid);
2699 this->OnEditboxChanged(wid);
2700 }
2701}
2702
2709void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2710{
2711 if (!EditBoxInGlobalFocus()) return;
2712
2713 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2714}
2715
2720static void HandleAutoscroll()
2721{
2722 if (_game_mode == GM_MENU || HasModalProgress()) return;
2724 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2725
2726 int x = _cursor.pos.x;
2727 int y = _cursor.pos.y;
2728 Window *w = FindWindowFromPt(x, y);
2729 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2731
2732 Viewport *vp = IsPtInWindowViewport(w, x, y);
2733 if (vp == nullptr) return;
2734
2735 x -= vp->left;
2736 y -= vp->top;
2737
2738 /* here allows scrolling in both x and y axis */
2739 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2740 static const int SCROLLSPEED = 3;
2741 if (x - 15 < 0) {
2742 w->viewport->CancelFollow(*w);
2743 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2744 } else if (15 - (vp->width - x) > 0) {
2745 w->viewport->CancelFollow(*w);
2746 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2747 }
2748 if (y - 15 < 0) {
2749 w->viewport->CancelFollow(*w);
2750 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2751 } else if (15 - (vp->height - y) > 0) {
2752 w->viewport->CancelFollow(*w);
2753 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2754 }
2755}
2756
2757enum MouseClick : uint8_t {
2758 MC_NONE = 0,
2759 MC_LEFT,
2760 MC_RIGHT,
2761 MC_DOUBLE_LEFT,
2762 MC_HOVER,
2763};
2764
2765static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2766static constexpr int MAX_OFFSET_HOVER = 5;
2767
2769
2770const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2771
2772static void ScrollMainViewport(int x, int y)
2773{
2774 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2775 Window *w = GetMainWindow();
2776 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2777 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2778 }
2779}
2780
2790static const int8_t scrollamt[16][2] = {
2791 { 0, 0},
2792 {-2, 0},
2793 { 0, -2},
2794 {-2, -1},
2795 { 2, 0},
2796 { 0, 0},
2797 { 2, -1},
2798 { 0, -2},
2799 { 0, 2},
2800 {-2, 1},
2801 { 0, 0},
2802 {-2, 0},
2803 { 2, 1},
2804 { 0, 2},
2805 { 2, 0},
2806 { 0, 0},
2807};
2808
2809static void HandleKeyScrolling()
2810{
2811 /*
2812 * Check that any of the dirkeys is pressed and that the focused window
2813 * doesn't have an edit-box as focused widget.
2814 */
2815 if (_dirkeys && !EditBoxInGlobalFocus()) {
2816 int factor = _shift_pressed ? 50 : 10;
2817
2818 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2819 /* Key scrolling stops following a vehicle. */
2820 Window *main_window = GetMainWindow();
2821 main_window->viewport->CancelFollow(*main_window);
2822 }
2823
2824 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2825 }
2826}
2827
2828static void MouseLoop(MouseClick click, int mousewheel)
2829{
2830 /* World generation is multithreaded and messes with companies.
2831 * But there is no company related window open anyway, so _current_company is not used. */
2832 assert(HasModalProgress() || IsLocalCompany());
2833
2834 HandlePlacePresize();
2836
2837 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2838 if (HandleMouseDragDrop() == ES_HANDLED) return;
2839 if (HandleWindowDragging() == ES_HANDLED) return;
2840 if (HandleActiveWidget() == ES_HANDLED) return;
2841 if (HandleViewportScroll() == ES_HANDLED) return;
2842
2844
2845 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2846 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2847
2848 int x = _cursor.pos.x;
2849 int y = _cursor.pos.y;
2850 Window *w = FindWindowFromPt(x, y);
2851 if (w == nullptr) return;
2852
2853 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2854 Viewport *vp = IsPtInWindowViewport(w, x, y);
2855
2856 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2857 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2858
2859 if (mousewheel != 0) {
2860 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2861 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) {
2862 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2863 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2864 }
2865 }
2866
2867 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2868 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2869 }
2870
2871 if (vp != nullptr) {
2872 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2873 _scrolling_viewport = true;
2874 _cursor.fix_at = true;
2875 return;
2876 }
2877
2878 switch (click) {
2879 case MC_DOUBLE_LEFT:
2880 case MC_LEFT:
2881 if (HandleViewportClicked(*vp, x, y)) return;
2884 _scrolling_viewport = true;
2885 _cursor.fix_at = false;
2886 return;
2887 }
2888 break;
2889
2890 case MC_RIGHT:
2893 _scrolling_viewport = true;
2896 DispatchRightClickEvent(w, x - w->left, y - w->top);
2897 return;
2898 }
2899 break;
2900
2901 default:
2902 break;
2903 }
2904 }
2905
2906 switch (click) {
2907 case MC_LEFT:
2908 case MC_DOUBLE_LEFT:
2909 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2910 return;
2911
2912 default:
2913 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2914 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2915 * Simulate a right button click so we can get started. */
2916 [[fallthrough]];
2917
2918 case MC_RIGHT:
2919 DispatchRightClickEvent(w, x - w->left, y - w->top);
2920 return;
2921
2922 case MC_HOVER:
2923 DispatchHoverEvent(w, x - w->left, y - w->top);
2924 break;
2925 }
2926
2927 /* We're not doing anything with 2D scrolling, so reset the value. */
2928 _cursor.h_wheel = 0.0f;
2929 _cursor.v_wheel = 0.0f;
2930 _cursor.wheel_moved = false;
2931}
2932
2937{
2938 /* World generation is multithreaded and messes with companies.
2939 * But there is no company related window open anyway, so _current_company is not used. */
2940 assert(HasModalProgress() || IsLocalCompany());
2941
2942 static std::chrono::steady_clock::time_point double_click_time = {};
2943 static Point double_click_pos = {0, 0};
2944
2945 /* Mouse event? */
2946 MouseClick click = MC_NONE;
2948 click = MC_LEFT;
2949 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2950 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2951 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2952 click = MC_DOUBLE_LEFT;
2953 }
2954 double_click_time = std::chrono::steady_clock::now();
2955 double_click_pos = _cursor.pos;
2956 _left_button_clicked = true;
2957 } else if (_right_button_clicked) {
2958 _right_button_clicked = false;
2959 click = MC_RIGHT;
2960 }
2961
2962 int mousewheel = 0;
2963 if (_cursor.wheel) {
2964 mousewheel = _cursor.wheel;
2965 _cursor.wheel = 0;
2966 }
2967
2968 static std::chrono::steady_clock::time_point hover_time = {};
2969 static Point hover_pos = {0, 0};
2970
2972 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
2973 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
2974 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
2975 hover_pos = _cursor.pos;
2976 hover_time = std::chrono::steady_clock::now();
2977 _mouse_hovering = false;
2978 } else if (!_mouse_hovering) {
2979 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
2980 click = MC_HOVER;
2981 _mouse_hovering = true;
2982 hover_time = std::chrono::steady_clock::now();
2983 }
2984 }
2985 }
2986
2987 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
2988 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
2990 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
2992 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
2994 } else {
2995 MouseLoop(click, mousewheel);
2996 }
2997
2998 /* We have moved the mouse the required distance,
2999 * no need to move it at any later time. */
3000 _cursor.delta.x = 0;
3001 _cursor.delta.y = 0;
3002}
3003
3007static void CheckSoftLimit()
3008{
3009 if (_settings_client.gui.window_soft_limit == 0) return;
3010
3011 for (;;) {
3012 uint deletable_count = 0;
3013 Window *last_deletable = nullptr;
3014 for (Window *w : Window::IterateFromFront()) {
3015 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3016
3017 last_deletable = w;
3018 deletable_count++;
3019 }
3020
3021 /* We've not reached the soft limit yet. */
3022 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3023
3024 assert(last_deletable != nullptr);
3025 last_deletable->Close();
3026 }
3027}
3028
3033{
3034 /* World generation is multithreaded and messes with companies.
3035 * But there is no company related window open anyway, so _current_company is not used. */
3036 assert(HasModalProgress() || IsLocalCompany());
3037
3039
3040 /* Process scheduled window deletion. */
3042
3043 /* HandleMouseEvents was already called for this tick */
3045}
3046
3047static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3048
3049bool CanContinueRealtimeTick()
3050{
3051 auto now = std::chrono::steady_clock::now();
3052 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3053}
3054
3059{
3060 _realtime_tick_start = std::chrono::steady_clock::now();
3061 for (Window *w : Window::Iterate()) {
3062 w->OnRealtimeTick(delta_ms);
3063 }
3064}
3065
3067static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3068 extern int _caret_timer;
3069 _caret_timer += 3;
3070 CursorTick();
3071
3072 HandleKeyScrolling();
3074 DecreaseWindowCounters();
3075});
3076
3080});
3081
3083static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3084 if (_network_dedicated) return;
3085
3086 for (Window *w : Window::Iterate()) {
3089 w->SetDirty();
3090 }
3091 }
3092});
3093
3098{
3099 static auto last_time = std::chrono::steady_clock::now();
3100 auto now = std::chrono::steady_clock::now();
3101 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3102
3103 if (delta_ms.count() == 0) return;
3104
3105 last_time = now;
3106
3109
3111
3113 CallWindowRealtimeTickEvent(delta_ms.count());
3114
3115 /* Process invalidations before anything else. */
3116 for (Window *w : Window::Iterate()) {
3120 }
3121
3122 /* Skip the actual drawing on dedicated servers without screen.
3123 * But still empty the invalidation queues above. */
3124 if (_network_dedicated) return;
3125
3127
3128 for (Window *w : Window::Iterate()) {
3129 /* Update viewport only if window is not shaded. */
3130 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3131 }
3133 /* Redraw mouse cursor in case it was hidden */
3134 DrawMouseCursor();
3135
3136 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3137 /* We are done with the last draw-frame, so we know what sprites we
3138 * clicked on. Reset the picker mode and invalidate the window. */
3141 }
3142}
3143
3150{
3151 for (const Window *w : Window::Iterate()) {
3152 if (w->window_class == cls && w->window_number == number) w->SetDirty();
3153 }
3154}
3155
3163{
3164 for (const Window *w : Window::Iterate()) {
3165 if (w->window_class == cls && w->window_number == number) {
3166 w->SetWidgetDirty(widget_index);
3167 }
3168 }
3169}
3170
3176{
3177 for (const Window *w : Window::Iterate()) {
3178 if (w->window_class == cls) w->SetDirty();
3179 }
3180}
3181
3186{
3187 this->scheduled_resize = true;
3188}
3189
3194{
3195 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3196 while (this->scheduled_resize) {
3197 this->scheduled_resize = false;
3198 this->OnResize();
3199 }
3200}
3201
3207void Window::InvalidateData(int data, bool gui_scope)
3208{
3209 this->SetDirty();
3210 if (!gui_scope) {
3211 /* Schedule GUI-scope invalidation for next redraw. */
3212 this->scheduled_invalidation_data.push_back(data);
3213 }
3214 this->OnInvalidateData(data, gui_scope);
3215}
3216
3221{
3222 for (int data : this->scheduled_invalidation_data) {
3223 if (this->window_class == WC_INVALID) break;
3224 this->OnInvalidateData(data, true);
3225 }
3226 this->scheduled_invalidation_data.clear();
3227}
3228
3233{
3234 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3235
3236 for (const auto &pair : this->widget_lookup) {
3237 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3238 }
3239}
3240
3267void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3268{
3269 for (Window *w : Window::Iterate()) {
3270 if (w->window_class == cls && w->window_number == number) {
3271 w->InvalidateData(data, gui_scope);
3272 }
3273 }
3274}
3275
3284void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3285{
3286 for (Window *w : Window::Iterate()) {
3287 if (w->window_class == cls) {
3288 w->InvalidateData(data, gui_scope);
3289 }
3290 }
3291}
3292
3297{
3298 for (Window *w : Window::Iterate()) {
3299 w->OnGameTick();
3300 }
3301}
3302
3310{
3311 /* Note: the container remains stable, even when deleting windows. */
3312 for (Window *w : Window::Iterate()) {
3314 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3315
3316 w->Close();
3317 }
3318 }
3319}
3320
3329{
3330 /* Note: the container remains stable, even when closing windows. */
3331 for (Window *w : Window::Iterate()) {
3333 w->Close();
3334 }
3335 }
3336}
3337
3342{
3344 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3345 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3346 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3347}
3348
3354{
3355 /* Note: the container remains stable, even when deleting windows. */
3356 for (Window *w : Window::Iterate()) {
3358 w->Close();
3359 }
3360 }
3361
3362 for (const Window *w : Window::Iterate()) w->SetDirty();
3363}
3364
3371
3372void ReInitWindow(Window *w, bool zoom_changed)
3373{
3374 if (w == nullptr) return;
3375 if (zoom_changed) {
3376 w->nested_root->AdjustPaddingForZoom();
3378 }
3379 w->ReInit();
3380}
3381
3383void ReInitAllWindows(bool zoom_changed)
3384{
3386 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3387 NWidgetScrollbar::InvalidateDimensionCache();
3388
3390
3391 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3392 * so EnsureVisibleCaption uses the updated size information. */
3393 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3394 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3395 for (Window *w : Window::Iterate()) {
3396 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3397 ReInitWindow(w, zoom_changed);
3398 }
3399
3402
3403 /* Make sure essential parts of all windows are visible */
3404 RelocateAllWindows(_screen.width, _screen.height);
3406}
3407
3415static int PositionWindow(Window *w, WindowClass clss, int setting)
3416{
3417 if (w == nullptr || w->window_class != clss) {
3418 w = FindWindowById(clss, 0);
3419 }
3420 if (w == nullptr) return 0;
3421
3422 int old_left = w->left;
3423 switch (setting) {
3424 case 1: w->left = (_screen.width - w->width) / 2; break;
3425 case 2: w->left = _screen.width - w->width; break;
3426 default: w->left = 0; break;
3427 }
3428 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3429 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3430 return w->left;
3431}
3432
3439{
3440 Debug(misc, 5, "Repositioning Main Toolbar...");
3442}
3443
3450{
3451 Debug(misc, 5, "Repositioning statusbar...");
3453}
3454
3461{
3462 Debug(misc, 5, "Repositioning news message...");
3464}
3465
3472{
3473 Debug(misc, 5, "Repositioning network chat window...");
3475}
3476
3477
3484{
3485 for (const Window *w : Window::Iterate()) {
3486 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3487 w->viewport->follow_vehicle = to_index;
3488 w->SetDirty();
3489 }
3490 }
3491}
3492
3493
3499void RelocateAllWindows(int neww, int newh)
3500{
3502
3503 /* Reposition toolbar then status bar before other all windows. */
3504 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3505 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3506 wt->left = PositionMainToolbar(wt);
3507 }
3508
3509 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3510 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3511 ws->top = newh - ws->height;
3512 ws->left = PositionStatusbar(ws);
3513 }
3514
3515 for (Window *w : Window::Iterate()) {
3516 int left, top;
3517 /* XXX - this probably needs something more sane. For example specifying
3518 * in a 'backup'-desc that the window should always be centered. */
3519 switch (w->window_class) {
3520 case WC_MAIN_WINDOW:
3521 case WC_BOOTSTRAP:
3522 case WC_HIGHSCORE:
3523 case WC_ENDSCREEN:
3524 ResizeWindow(w, neww, newh);
3525 continue;
3526
3527 case WC_MAIN_TOOLBAR:
3528 case WC_STATUS_BAR:
3529 continue;
3530
3531 case WC_NEWS_WINDOW:
3532 top = newh - w->height;
3533 left = PositionNewsMessage(w);
3534 break;
3535
3537 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3538
3539 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3540 left = PositionNetworkChatWindow(w);
3541 break;
3542
3543 case WC_CONSOLE:
3544 IConsoleResize(w);
3545 continue;
3546
3547 default: {
3548 if (w->flags.Test(WindowFlag::Centred)) {
3549 top = (newh - w->height) >> 1;
3550 left = (neww - w->width) >> 1;
3551 break;
3552 }
3553
3554 left = w->left;
3555 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3556 if (left < 0) left = 0;
3557
3558 top = w->top;
3559 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3560 break;
3561 }
3562 }
3563
3564 EnsureVisibleCaption(w, left, top);
3565 }
3566}
3567
3572void PickerWindowBase::Close([[maybe_unused]] int data)
3573{
3575 this->Window::Close();
3576}
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:942
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:1271
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1244
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:2653
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:1433
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:3572
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:40
bool _left_button_down
Is left mouse button pressed?
Definition gfx.cpp:42
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
uint8_t _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
Definition gfx.cpp:35
bool _left_button_clicked
Is left mouse button clicked?
Definition gfx.cpp:43
bool _right_button_clicked
Is right mouse button clicked?
Definition gfx.cpp:45
bool _right_button_down
Is right mouse button pressed?
Definition gfx.cpp:44
int _gui_scale
GUI scale, 100 is 100%.
Definition gfx.cpp:64
Functions related to the gfx engine.
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:250
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:308
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
Definition gfx_type.h:371
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:3404
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:957
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:1510
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1446
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1543
Hotkey related functions.
Types related to reading/writing '*.ini' files.
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:403
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:688
bool _networking
are we in networking mode?
Definition network.cpp:67
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:70
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:716
@ 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:129
Point pos
logical mouse position
Definition gfx_type.h:126
bool in_window
mouse inside this window, determines drawing logic
Definition gfx_type.h:148
int wheel
mouse wheel movement
Definition gfx_type.h:128
Point delta
relative mouse movement in this tick
Definition gfx_type.h:127
Data about how and where to blit pixels.
Definition gfx_type.h:158
uint8_t window_snap_radius
windows snap at each other if closer than this
uint16_t hover_delay_ms
time required to activate a hover event, in milliseconds
uint8_t auto_scrolling
scroll when moving mouse to the edge (see ViewportAutoscrolling)
uint8_t scroll_mode
viewport scroll mode
RightClickClose right_click_wnd_close
close window with right click
uint8_t window_soft_limit
soft limit of maximum number of non-stickied non-vital windows (0 = no limit)
uint8_t toolbar_pos
position of toolbars, 0=left, 1=center, 2=right
uint8_t statusbar_pos
position of statusbar, 0=left, 1=center, 2=right
uint8_t scrollwheel_scrolling
scrolling using the scroll wheel?
List of hotkeys for a window.
Definition hotkeys.h:37
int CheckMatch(uint16_t keycode, bool global_only=false) const
Check if a keycode is bound to something.
Definition hotkeys.cpp: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:186
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:846
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:788
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:816
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:212
uint step_width
Step-size of width resize changes.
Definition window_gui.h:211
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:167
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:186
const WindowPosition default_pos
Preferred position of the window.
Definition window_gui.h:177
bool pref_sticky
Preferred stickyness.
Definition window_gui.h:185
int16_t pref_height
User-preferred height of the window. Zero if unset.
Definition window_gui.h:187
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:197
const int16_t default_width_trad
Preferred initial width of the window (pixels at 1x zoom).
Definition window_gui.h:196
const WindowClass cls
Class of the window,.
Definition window_gui.h:178
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:180
const WindowDefaultFlags flags
Flags.
Definition window_gui.h:181
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:179
const HotkeyList * hotkeys
Hotkeys for the window.
Definition window_gui.h:183
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:182
Number to differentiate different windows of the same class.
Iterator to iterate all valid Windows.
Definition window_gui.h:874
Data structure for an opened window.
Definition window_gui.h:273
virtual const struct Textbuf * GetFocusedTextbuf() const
Get the current input text buffer.
Definition window.cpp:364
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:969
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1093
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:792
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1780
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:320
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:307
NWidgetStacked * shade_select
Selection widget (NWID_SELECTION) to use for shading the window. If nullptr, window cannot shade.
Definition window_gui.h:323
void InitializePositionSize(int x, int y, int min_width, int min_height)
Set the position and smallest size of the window.
Definition window.cpp:1446
Dimension unshaded_size
Last known unshaded size (only valid while shaded).
Definition window_gui.h:324
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3207
virtual EventState OnKeyPress(char32_t key, uint16_t keycode)
A key has been pressed.
Definition window_gui.h:650
Window * parent
Parent window.
Definition window_gui.h:328
AllWindows< false > IterateFromBack
Iterate all windows in Z order from back to front.
Definition window_gui.h:933
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition window.cpp:1130
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:469
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:556
uint8_t timeout_timer
Timer value of the WindowFlag::Timeout for flags.
Definition window_gui.h:306
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:504
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
Definition window_gui.h:326
virtual void OnGameTick()
Called once per (game) tick.
Definition window_gui.h:745
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition window_gui.h:867
virtual bool OnRightClick(Point pt, WidgetID widget)
A click with the right mouse button has been made on the window.
Definition window_gui.h:677
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:719
virtual void OnDropdownSelect(WidgetID widget, int index, int click_result)
A dropdown option associated to this window has been selected.
Definition window_gui.h:769
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition window.cpp:3220
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1066
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:469
virtual void OnMouseLoop()
Called for every mouse loop run, which is at least once per (game) tick.
Definition window_gui.h:740
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition window.cpp:1012
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:304
void ScheduleResize()
Mark this window as resized and in need of OnResize() event.
Definition window.cpp:3185
virtual void OnPaint()
The window must be repainted.
Definition window_gui.h:595
virtual void OnDragDrop(Point pt, WidgetID widget)
A dragged 'object' has been released.
Definition window_gui.h:706
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1770
WindowDesc & window_desc
Window description.
Definition window_gui.h:299
WindowClass window_class
Window class.
Definition window_gui.h:301
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
Definition window_gui.h:750
virtual void OnMouseWheel(int wheel, WidgetID widget)
The mouse wheel has been turned.
Definition window_gui.h:734
AllWindows< true > IterateFromFront
Iterate all windows in Z order from front to back.
Definition window_gui.h:934
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1081
void SetWhiteBorder()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:364
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:485
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:521
static std::vector< Window * > closed_windows
List of closed windows to delete.
Definition window_gui.h:275
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition window.cpp:530
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1758
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:316
virtual Point GetCaretPosition() const
Get the current caret position if an edit box has the focus.
Definition window.cpp:377
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1465
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:2692
int left
x position of left edge of the window
Definition window_gui.h:309
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:559
Window * FindChildWindow(WindowClass wc=WC_INVALID) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1038
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:355
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:319
virtual void OnEditboxChanged(WidgetID widget)
The text in an editbox has been edited.
Definition window_gui.h:777
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition window.cpp:353
virtual void OnScroll(Point delta)
Handle the request for (viewport) scrolling.
Definition window_gui.h:712
int top
y position of top edge of the window
Definition window_gui.h:310
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:668
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:333
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:322
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:408
std::vector< int > scheduled_invalidation_data
Data of scheduled OnInvalidateData() calls.
Definition window_gui.h:282
void InitializeData(WindowNumber window_number)
Initializes the data (except the position and initial size) of a new Window.
Definition window.cpp:1408
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:727
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1803
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:982
virtual bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition window_gui.h:692
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:460
virtual EventState OnCTRLStateChange()
The state of the control key has changed.
Definition window_gui.h:659
void ProcessScheduledResize()
Process scheduled OnResize() event.
Definition window.cpp:3193
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2534
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:699
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:595
virtual void OnResize()
Called after the window got resized.
Definition window_gui.h:762
Window * FindChildWindowById(WindowClass wc, WindowNumber number) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1053
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:513
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1793
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h:755
WindowFlags flags
Window flags.
Definition window_gui.h:300
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:313
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3232
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:570
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:321
bool scheduled_resize
Set if window has been resized.
Definition window_gui.h:283
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:393
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:851
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:932
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:312
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:684
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
virtual void OnInit()
Notification that the nested widget tree gets initialized.
Definition window_gui.h:578
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
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:283
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:259
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:36
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:75
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:63
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:58
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:56
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:53
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:61
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:38
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:64
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:76
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:60
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:57
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition widget_type.h:55
@ 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:3449
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:1992
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2126
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:3353
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:1184
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:1699
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1171
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:846
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1212
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2679
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:874
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:3032
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3097
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3438
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3309
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:936
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2069
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:1567
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3471
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2321
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:1606
PreventHideDirection
Direction for moving the window.
Definition window.cpp:1977
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:1979
@ PHD_UP
Above v is a safe position.
Definition window.cpp:1978
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2720
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:2606
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3415
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:421
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:1531
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2765
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1157
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3483
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:2410
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:809
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:613
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1815
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3341
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:2109
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:1388
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3383
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:2032
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1196
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2936
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:1674
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2132
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3460
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2120
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:781
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:1232
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2623
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3175
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:2766
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:461
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1865
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1304
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:447
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:3267
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3366
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:1267
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3499
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:2337
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:2304
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3296
static EventState HandleActiveWidget()
Handle active widget (mouse draggin on widget) with the mouse.
Definition window.cpp:2377
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:752
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:2790
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2474
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:3058
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:1930
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3328
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1954
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:3007
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1142
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1851
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1829
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:3162
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:2709
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3149
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:3284
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:421
@ SizingLeft
Window is being resized towards the left.
@ DisableVpScroll
Window does not do autoscroll,.
@ Highlighted
Window has a widget that has a highlight.
@ Centred
Window is centered and shall stay centered after ReInit.
@ Dragging
Window is being dragged.
@ SizingRight
Window is being resized towards the right.
@ WhiteBorder
Window white border counter bit mask.
@ Timeout
Window timeout counter.
@ Sticky
Window is made sticky by user.
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
Returns the index for the widget located at the given position relative to the window.
Definition widget.cpp:283
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.
WindowClass
Window classes.
Definition window_type.h:46
@ WC_INVALID
Invalid window.
@ WC_OSK
On Screen Keyboard; Window numbers:
@ WC_CONSOLE
Console; Window numbers:
@ WC_NEWS_WINDOW
News window; Window numbers:
@ WC_HIGHSCORE
Highscore; Window numbers:
@ WC_BUY_COMPANY
Buyout company (merger); Window numbers:
@ WC_SPRITE_ALIGNER
Sprite aligner (debug); Window numbers:
@ WC_COMPANY_INFRASTRUCTURE
Company infrastructure overview; Window numbers:
@ WC_ENDSCREEN
Endscreen; Window numbers:
@ WC_COMPANY_COLOUR
Company colour selection; Window numbers:
@ WC_STATION_LIST
Station list; Window numbers:
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition window_type.h:66
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_SEND_NETWORK_MSG
Chatbox; Window numbers:
@ WC_ERRMSG
Error message; Window numbers:
@ WC_BUILD_TOOLBAR
Build toolbar; Window numbers:
Definition window_type.h:75
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_SCRIPT_SETTINGS
Script settings; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:47
@ WC_SCEN_LAND_GEN
Landscape generation (in Scenario Editor); Window numbers:
@ WC_SCRIPT_LIST
Scripts list; Window numbers:
@ WC_SAVE_PRESET
Save preset; Window numbers:
@ WC_SHIPS_LIST
Ships list; Window numbers:
@ WC_MESSAGE_HISTORY
News history list; Window numbers:
@ WC_CONFIRM_POPUP_QUERY
Popup with confirm question; Window numbers:
@ WC_GENERATE_LANDSCAPE
Generate landscape (newgame); Window numbers:
@ WC_CUSTOM_CURRENCY
Custom currency; Window numbers:
@ WC_MAIN_WINDOW
Main window; Window numbers:
Definition window_type.h:53
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers:
@ WC_NETWORK_WINDOW
Network window; Window numbers:
@ WC_FINANCES
Finances of a company; Window numbers:
@ WC_TEXTFILE
textfile; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_QUERY_STRING
Query string window; Window numbers:
@ WC_SMALLMAP
Small map; Window numbers:
@ WC_BOOTSTRAP
Bootstrap; Window numbers:
@ WC_SAVELOAD
Saveload window; Window numbers:
@ WC_GRF_PARAMETERS
NewGRF parameters; Window numbers:
@ WC_MODAL_PROGRESS
Progress report of landscape generation; Window numbers:
@ WC_MAIN_TOOLBAR
Main toolbar (the long bar at the top); Window numbers:
Definition window_type.h:60
@ WC_TOOLTIPS
Tooltip window; Window numbers:
@ WC_COMPANY
Company view; Window numbers:
@ WC_NETWORK_STATUS_WINDOW
Network status window; Window numbers:
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::Min) When shifting right,...
Definition zoom_func.h:22
@ Min
Minimum zoom level.