OpenTTD Source 20250908-master-g16cd420e4c
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
623 /* If clicked on a window that previously did not have focus */
624 if (_focused_window != w) {
625 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
626 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
627 focused_widget_changed = true;
629 } else if (_focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU) {
630 /* The previously focused window was a dropdown menu, but the user clicked on another window that
631 * isn't focusable. Close the dropdown menu anyway. */
632 SetFocusedWindow(nullptr);
633 }
634 }
635
636 if (nw == nullptr) return; // exit if clicked outside of widgets
637
638 /* don't allow any interaction if the button has been disabled */
639 if (nw->IsDisabled()) return;
640
641 WidgetID widget_index = nw->GetIndex();
642
643 /* Clicked on a widget that is not disabled.
644 * So unless the clicked widget is the caption bar, change focus to this widget.
645 * Exception: In the OSK we always want the editbox to stay focused. */
646 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
647 /* focused_widget_changed is 'now' only true if the window this widget
648 * is in gained focus. In that case it must remain true, also if the
649 * local widget focus did not change. As such it's the logical-or of
650 * both changed states.
651 *
652 * If this is not preserved, then the OSK window would be opened when
653 * a user has the edit box focused and then click on another window and
654 * then back again on the edit box (to type some text).
655 */
656 focused_widget_changed |= w->SetFocusedWidget(widget_index);
657 }
658
659 /* Dropdown window of this widget was closed so don't process click this time. */
661
662 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
663
664 Point pt = { x, y };
665
666 switch (widget_type) {
667 case NWID_VSCROLLBAR:
668 case NWID_HSCROLLBAR:
669 ScrollbarClickHandler(w, nw, x, y);
670 break;
671
672 case WWT_EDITBOX: {
673 QueryString *query = w->GetQueryString(widget_index);
674 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
675 break;
676 }
677
678 case WWT_CLOSEBOX: // 'X'
679 w->Close();
680 return;
681
682 case WWT_CAPTION: // 'Title bar'
684 return;
685
686 case WWT_RESIZEBOX:
687 /* When the resize widget is on the left size of the window
688 * we assume that that button is used to resize to the left. */
689 StartWindowSizing(w, nw->pos_x < (w->width / 2));
690 nw->SetDirty(w);
691 return;
692
693 case WWT_DEFSIZEBOX: {
694 if (_ctrl_pressed) {
695 if (click_count > 1) {
696 w->window_desc.pref_width = 0;
698 } else {
701 }
702 } else {
703 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
704 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
705
706 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
707 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
708 /* dx and dy has to go by step.. calculate it.
709 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
710 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
711 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
712 ResizeWindow(w, dx, dy, false);
713 }
714
715 nw->SetLowered(true);
716 nw->SetDirty(w);
717 w->SetTimeout();
718 break;
719 }
720
721 case WWT_DEBUGBOX:
723 break;
724
725 case WWT_SHADEBOX:
726 nw->SetDirty(w);
727 w->SetShaded(!w->IsShaded());
728 return;
729
730 case WWT_STICKYBOX:
732 nw->SetDirty(w);
734 return;
735
736 default:
737 break;
738 }
739
740 /* Widget has no index, so the window is not interested in it. */
741 if (widget_index < 0) return;
742
743 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
744 if (w->IsWidgetHighlighted(widget_index)) {
745 w->SetWidgetHighlight(widget_index, TC_INVALID);
746 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
747 }
748
749 w->OnClick(pt, widget_index, click_count);
750}
751
758static void DispatchRightClickEvent(Window *w, int x, int y)
759{
760 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
761 if (wid == nullptr) return;
762
763 Point pt = { x, y };
764
765 /* No widget to handle, or the window is not interested in it. */
766 if (wid->GetIndex() >= 0) {
767 if (w->OnRightClick(pt, wid->GetIndex())) return;
768 }
769
770 /* Right-click close is enabled and there is a closebox. */
772 w->Close();
774 /* Right-click close is enabled, but excluding sticky windows. */
775 w->Close();
776 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
777 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_RIGHT_CLICK);
778 }
779}
780
787static void DispatchHoverEvent(Window *w, int x, int y)
788{
789 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
790
791 /* No widget to handle */
792 if (wid == nullptr) return;
793
794 Point pt = { x, y };
795
796 /* Show the tooltip if there is any */
797 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
798 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_HOVER);
799 return;
800 }
801
802 /* Widget has no index, so the window is not interested in it. */
803 if (wid->GetIndex() < 0) return;
804
805 w->OnHover(pt, wid->GetIndex());
806}
807
815static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
816{
817 if (nwid == nullptr) return;
818
819 /* Using wheel on caption/shade-box shades or unshades the window. */
820 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
821 w->SetShaded(wheel < 0);
822 return;
823 }
824
825 /* Wheeling a vertical scrollbar. */
826 if (nwid->type == NWID_VSCROLLBAR) {
827 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
828 if (sb->GetCount() > sb->GetCapacity()) {
829 if (sb->UpdatePosition(wheel)) {
830 w->OnScrollbarScroll(nwid->GetIndex());
831 w->SetDirty();
832 }
833 }
834 return;
835 }
836
837 /* Scroll the widget attached to the scrollbar. */
838 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
839 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
840 if (sb->UpdatePosition(wheel)) {
842 w->SetDirty();
843 }
844 }
845}
846
852static bool MayBeShown(const Window *w)
853{
854 /* If we're not modal, everything is okay. */
855 if (!HasModalProgress()) return true;
856
857 switch (w->window_class) {
858 case WC_MAIN_WINDOW:
859 case WC_MODAL_PROGRESS:
861 return true;
862
863 default:
864 return false;
865 }
866}
867
880static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
881{
883 ++it;
884 for (; !it.IsEnd(); ++it) {
885 const Window *v = *it;
886 if (MayBeShown(v) &&
887 right > v->left &&
888 bottom > v->top &&
889 left < v->left + v->width &&
890 top < v->top + v->height) {
891 /* v and rectangle intersect with each other */
892 int x;
893
894 if (left < (x = v->left)) {
895 DrawOverlappedWindow(w, left, top, x, bottom);
896 DrawOverlappedWindow(w, x, top, right, bottom);
897 return;
898 }
899
900 if (right > (x = v->left + v->width)) {
901 DrawOverlappedWindow(w, left, top, x, bottom);
902 DrawOverlappedWindow(w, x, top, right, bottom);
903 return;
904 }
905
906 if (top < (x = v->top)) {
907 DrawOverlappedWindow(w, left, top, right, x);
908 DrawOverlappedWindow(w, left, x, right, bottom);
909 return;
910 }
911
912 if (bottom > (x = v->top + v->height)) {
913 DrawOverlappedWindow(w, left, top, right, x);
914 DrawOverlappedWindow(w, left, x, right, bottom);
915 return;
916 }
917
918 return;
919 }
920 }
921
922 /* Setup blitter, and dispatch a repaint event to window *wz */
923 DrawPixelInfo *dp = _cur_dpi;
924 dp->width = right - left;
925 dp->height = bottom - top;
926 dp->left = left - w->left;
927 dp->top = top - w->top;
928 dp->pitch = _screen.pitch;
929 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
930 dp->zoom = ZoomLevel::Min;
931 w->OnPaint();
932}
933
942void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
943{
944 DrawPixelInfo bk;
945 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
946
947 for (Window *w : Window::IterateFromBack()) {
948 if (MayBeShown(w) &&
949 right > w->left &&
950 bottom > w->top &&
951 left < w->left + w->width &&
952 top < w->top + w->height) {
953 /* Window w intersects with the rectangle => needs repaint */
954 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));
955 }
956 }
957}
958
964{
965 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
966}
967
975void Window::ReInit(int rx, int ry, bool reposition)
976{
977 this->SetDirty(); // Mark whole current window as dirty.
978
979 /* Save current size. */
980 int window_width = this->width * _gui_scale / this->scale;
981 int window_height = this->height * _gui_scale / this->scale;
982 this->scale = _gui_scale;
983
984 this->OnInit();
985 /* Re-initialize window smallest size. */
986 this->nested_root->SetupSmallestSize(this);
987 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
988 this->width = this->nested_root->smallest_x;
989 this->height = this->nested_root->smallest_y;
990 this->resize.step_width = this->nested_root->resize_x;
991 this->resize.step_height = this->nested_root->resize_y;
992
993 /* Resize as close to the original size + requested resize as possible. */
994 window_width = std::max(window_width + rx, this->width);
995 window_height = std::max(window_height + ry, this->height);
996 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
997 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
998 /* dx and dy has to go by step.. calculate it.
999 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1000 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1001 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1002
1003 if (reposition) {
1004 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1005 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1006 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1007 }
1008
1009 ResizeWindow(this, dx, dy, true, false);
1010 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1011}
1012
1018void Window::SetShaded(bool make_shaded)
1019{
1020 if (this->shade_select == nullptr) return;
1021
1022 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1023 if (this->shade_select->shown_plane != desired) {
1024 if (make_shaded) {
1025 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1026 this->unshaded_size.width = this->width;
1027 this->unshaded_size.height = this->height;
1028 this->shade_select->SetDisplayedPlane(desired);
1029 this->ReInit(0, -this->height);
1030 } else {
1031 this->shade_select->SetDisplayedPlane(desired);
1032 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1033 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1034 this->ReInit(dx, dy);
1035 }
1036 }
1037}
1038
1045{
1046 for (Window *v : Window::Iterate()) {
1047 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1048 }
1049
1050 return nullptr;
1051}
1052
1060{
1061 for (Window *v : Window::Iterate()) {
1062 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1063 }
1064
1065 return nullptr;
1066}
1067
1073{
1074 Window *child = this->FindChildWindow(wc);
1075 while (child != nullptr) {
1076 child->Close();
1077 child = this->FindChildWindow(wc);
1078 }
1079}
1080
1081
1088{
1089 Window *child = this->FindChildWindowById(wc, number);
1090 while (child != nullptr) {
1091 child->Close();
1092 child = this->FindChildWindowById(wc, number);
1093 }
1094}
1095
1099void Window::Close([[maybe_unused]] int data)
1100{
1101 /* Don't close twice. */
1102 if (*this->z_position == nullptr) return;
1103
1104 *this->z_position = nullptr;
1105
1106 if (_thd.window_class == this->window_class &&
1107 _thd.window_number == this->window_number) {
1109 }
1110
1111 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1112 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1113
1114 /* We can't scroll the window when it's closed. */
1115 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1116
1117 /* Make sure we don't try to access non-existing query strings. */
1118 this->querystrings.clear();
1119
1120 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1121 if (_focused_window == this) {
1122 this->OnFocusLost(true);
1123 _focused_window = nullptr;
1124 }
1125
1126 this->CloseChildWindows();
1127
1128 this->SetDirty();
1129
1130 Window::closed_windows.push_back(this);
1131}
1132
1137{
1138 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1139 assert(*this->z_position == nullptr);
1140}
1141
1149{
1150 for (Window *w : Window::Iterate()) {
1151 if (w->window_class == cls && w->window_number == number) return w;
1152 }
1153
1154 return nullptr;
1155}
1156
1164{
1165 for (Window *w : Window::Iterate()) {
1166 if (w->window_class == cls) return w;
1167 }
1168
1169 return nullptr;
1170}
1171
1178{
1180 assert(w != nullptr);
1181 return w;
1182}
1183
1190void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1191{
1192 Window *w = FindWindowById(cls, number);
1193 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1194 w->Close(data);
1195 }
1196}
1197
1203{
1204 /* Note: the container remains stable, even when deleting windows. */
1205 for (Window *w : Window::Iterate()) {
1206 if (w->window_class == cls) {
1207 w->Close(data);
1208 }
1209 }
1210}
1211
1219{
1220 /* Note: the container remains stable, even when deleting windows. */
1221 for (Window *w : Window::Iterate()) {
1222 if (w->owner == id) {
1223 w->Close();
1224 }
1225 }
1226
1227 /* Also delete the company specific windows that don't have a company-colour. */
1229}
1230
1238void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1239{
1240 for (Window *w : Window::Iterate()) {
1241 if (w->owner != old_owner) continue;
1242
1243 switch (w->window_class) {
1244 case WC_COMPANY_COLOUR:
1245 case WC_FINANCES:
1246 case WC_STATION_LIST:
1247 case WC_TRAINS_LIST:
1248 case WC_ROADVEH_LIST:
1249 case WC_SHIPS_LIST:
1250 case WC_AIRCRAFT_LIST:
1251 case WC_BUY_COMPANY:
1252 case WC_COMPANY:
1254 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().
1255 continue;
1256
1257 default:
1258 w->owner = new_owner;
1259 break;
1260 }
1261 }
1262}
1263
1264static void BringWindowToFront(Window *w, bool dirty = true);
1265
1274{
1275 Window *w = FindWindowById(cls, number);
1276
1277 if (w != nullptr) {
1278 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1279
1280 w->SetWhiteBorder();
1282 w->SetDirty();
1283 }
1284
1285 return w;
1286}
1287
1288static inline bool IsVitalWindow(const Window *w)
1289{
1290 switch (w->window_class) {
1291 case WC_MAIN_TOOLBAR:
1292 case WC_STATUS_BAR:
1293 case WC_NEWS_WINDOW:
1295 return true;
1296
1297 default:
1298 return false;
1299 }
1300}
1301
1311{
1312 assert(wc != WC_INVALID);
1313
1314 uint z_priority = 0;
1315
1316 switch (wc) {
1317 case WC_TOOLTIPS:
1318 ++z_priority;
1319 [[fallthrough]];
1320
1321 case WC_ERRMSG:
1323 ++z_priority;
1324 [[fallthrough]];
1325
1326 case WC_ENDSCREEN:
1327 ++z_priority;
1328 [[fallthrough]];
1329
1330 case WC_HIGHSCORE:
1331 ++z_priority;
1332 [[fallthrough]];
1333
1334 case WC_DROPDOWN_MENU:
1335 ++z_priority;
1336 [[fallthrough]];
1337
1338 case WC_MAIN_TOOLBAR:
1339 case WC_STATUS_BAR:
1340 ++z_priority;
1341 [[fallthrough]];
1342
1343 case WC_OSK:
1344 ++z_priority;
1345 [[fallthrough]];
1346
1347 case WC_QUERY_STRING:
1349 ++z_priority;
1350 [[fallthrough]];
1351
1353 case WC_MODAL_PROGRESS:
1355 case WC_SAVE_PRESET:
1356 ++z_priority;
1357 [[fallthrough]];
1358
1360 case WC_SAVELOAD:
1361 case WC_GAME_OPTIONS:
1362 case WC_CUSTOM_CURRENCY:
1363 case WC_NETWORK_WINDOW:
1364 case WC_GRF_PARAMETERS:
1365 case WC_SCRIPT_LIST:
1366 case WC_SCRIPT_SETTINGS:
1367 case WC_TEXTFILE:
1368 ++z_priority;
1369 [[fallthrough]];
1370
1371 case WC_CONSOLE:
1372 ++z_priority;
1373 [[fallthrough]];
1374
1375 case WC_NEWS_WINDOW:
1376 ++z_priority;
1377 [[fallthrough]];
1378
1379 default:
1380 ++z_priority;
1381 [[fallthrough]];
1382
1383 case WC_MAIN_WINDOW:
1384 return z_priority;
1385 }
1386}
1387
1394static void BringWindowToFront(Window *w, bool dirty)
1395{
1396 auto priority = GetWindowZPriority(w->window_class);
1397 WindowList::iterator dest = _z_windows.begin();
1398 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1399
1400 if (dest != w->z_position) {
1401 _z_windows.splice(dest, _z_windows, w->z_position);
1402 }
1403
1404 if (dirty) w->SetDirty();
1405}
1406
1415{
1416 /* Set up window properties; some of them are needed to set up smallest size below */
1417 this->window_class = this->window_desc.cls;
1418 this->SetWhiteBorder();
1420 this->owner = INVALID_OWNER;
1421 this->nested_focus = nullptr;
1422 this->window_number = window_number;
1423
1424 this->OnInit();
1425 /* Initialize smallest size. */
1426 this->nested_root->SetupSmallestSize(this);
1427 /* Initialize to smallest size. */
1428 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1429
1430 /* Further set up window properties,
1431 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1432 this->resize.step_width = this->nested_root->resize_x;
1433 this->resize.step_height = this->nested_root->resize_y;
1434
1435 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1436 * (so we don't interrupt typing) unless the new window has a text box. */
1437 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1438 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1439 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1440
1441 /* Insert the window into the correct location in the z-ordering. */
1442 BringWindowToFront(this, false);
1443}
1444
1452void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1453{
1454 this->left = x;
1455 this->top = y;
1456 this->width = sm_width;
1457 this->height = sm_height;
1458}
1459
1471void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1472{
1473 if (allow_resize) {
1474 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1475 def_height = std::max(def_height, this->height);
1476 /* Try to make windows smaller when our window is too small.
1477 * w->(width|height) is normally the same as min_(width|height),
1478 * but this way the GUIs can be made a little more dynamic;
1479 * one can use the same spec for multiple windows and those
1480 * can then determine the real minimum size of the window. */
1481 if (this->width != def_width || this->height != def_height) {
1482 /* Think about the overlapping toolbars when determining the minimum window size */
1483 int free_height = _screen.height;
1484 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1485 if (wt != nullptr) free_height -= wt->height;
1487 if (wt != nullptr) free_height -= wt->height;
1488
1489 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1490 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1491
1492 /* X and Y has to go by step.. calculate it.
1493 * The cast to int is necessary else x/y are implicitly cast to
1494 * unsigned int, which won't work. */
1495 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1496 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1497
1498 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1499 /* ResizeWindow() calls this->OnResize(). */
1500 } else {
1501 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1502 this->OnResize();
1503 }
1504 }
1505
1506 int nx = this->left;
1507 int ny = this->top;
1508
1509 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1510
1511 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1512 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1513 nx = std::max(nx, 0);
1514
1515 if (this->viewport != nullptr) {
1516 this->viewport->left += nx - this->left;
1517 this->viewport->top += ny - this->top;
1518 }
1519 this->left = nx;
1520 this->top = ny;
1521
1522 this->SetDirty();
1523}
1524
1537static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1538{
1539 int right = width + left;
1540 int bottom = height + top;
1541
1542 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1543
1544 /* Make sure it is not obscured by any window. */
1545 for (const Window *w : Window::Iterate()) {
1546 if (w->window_class == WC_MAIN_WINDOW) continue;
1547
1548 if (right > w->left &&
1549 w->left + w->width > left &&
1550 bottom > w->top &&
1551 w->top + w->height > top) {
1552 return false;
1553 }
1554 }
1555
1556 pos.x = left;
1557 pos.y = top;
1558 return true;
1559}
1560
1573static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1574{
1575 bool rtl = _current_text_dir == TD_RTL;
1576
1577 /* Left part of the rectangle may be at most 1/4 off-screen,
1578 * right part of the rectangle may be at most 1/2 off-screen
1579 */
1580 if (rtl) {
1581 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1582 } else {
1583 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1584 }
1585
1586 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1587 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1588
1589 /* Make sure it is not obscured by any window. */
1590 for (const Window *w : Window::Iterate()) {
1591 if (w->window_class == WC_MAIN_WINDOW) continue;
1592
1593 if (left + width > w->left &&
1594 w->left + w->width > left &&
1595 top + height > w->top &&
1596 w->top + w->height > top) {
1597 return false;
1598 }
1599 }
1600
1601 pos.x = left;
1602 pos.y = top;
1603 return true;
1604}
1605
1612static Point GetAutoPlacePosition(int width, int height)
1613{
1614 Point pt;
1615
1616 bool rtl = _current_text_dir == TD_RTL;
1617
1618 /* First attempt, try top-left of the screen */
1619 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1620 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1621 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1622
1623 /* Second attempt, try around all existing windows.
1624 * The new window must be entirely on-screen, and not overlap with an existing window.
1625 * Eight starting points are tried, two at each corner.
1626 */
1627 for (const Window *w : Window::Iterate()) {
1628 if (w->window_class == WC_MAIN_WINDOW) continue;
1629
1630 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1631 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1632 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1633 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1634 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1635 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1636 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1637 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1638 }
1639
1640 /* Third attempt, try around all existing windows.
1641 * The new window may be partly off-screen, and must not overlap with an existing window.
1642 * Only four starting points are tried.
1643 */
1644 for (const Window *w : Window::Iterate()) {
1645 if (w->window_class == WC_MAIN_WINDOW) continue;
1646
1647 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1648 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1649 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1650 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1651 }
1652
1653 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1654 * of the closebox
1655 */
1656 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1657 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1658 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1659
1660restart:
1661 for (const Window *w : Window::Iterate()) {
1662 if (w->left == left && w->top == top) {
1663 left += offset_x;
1664 top += offset_y;
1665 goto restart;
1666 }
1667 }
1668
1669 pt.x = left;
1670 pt.y = top;
1671 return pt;
1672}
1673
1681{
1682 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1683 assert(w != nullptr);
1684 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1685 return pt;
1686}
1687
1705static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1706{
1707 Point pt;
1708 const Window *w;
1709
1710 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1711 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1712
1713 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1714 bool rtl = _current_text_dir == TD_RTL;
1715 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1716 pt.x = w->left + (rtl ? w->width - default_width : 0);
1717 pt.y = w->top + w->height;
1718 return pt;
1719 } else {
1720 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1721 * - Y position: closebox of parent + closebox of child + statusbar
1722 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1723 */
1724 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1725 if (w->top + 3 * indent_y < _screen.height) {
1726 pt.y = w->top + indent_y;
1727 int indent_close = NWidgetLeaf::closebox_dimension.width;
1728 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1729 if (_current_text_dir == TD_RTL) {
1730 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1731 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1732 } else {
1733 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1734 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1735 }
1736 }
1737 }
1738 }
1739
1740 switch (desc.default_pos) {
1741 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1742 return GetToolbarAlignedWindowPosition(default_width);
1743
1744 case WDP_AUTO: // Find a good automatic position for the window
1745 return GetAutoPlacePosition(default_width, default_height);
1746
1747 case WDP_CENTER: // Centre the window horizontally
1748 pt.x = (_screen.width - default_width) / 2;
1749 pt.y = (_screen.height - default_height) / 2;
1750 break;
1751
1752 case WDP_MANUAL:
1753 pt.x = 0;
1754 pt.y = 0;
1755 break;
1756
1757 default:
1758 NOT_REACHED();
1759 }
1760
1761 return pt;
1762}
1763
1764/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1765{
1766 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1767}
1768
1777{
1778 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1779 this->nested_root->FillWidgetLookup(this->widget_lookup);
1780}
1781
1787{
1788 this->InitializeData(window_number);
1789 this->ApplyDefaults();
1790 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1791 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1792 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1793}
1794
1800{
1801 this->CreateNestedTree();
1802 this->FinishInitNested(window_number);
1803}
1804
1809Window::Window(WindowDesc &desc) : window_desc(desc), scale(_gui_scale), mouse_capture_widget(-1)
1810{
1811 this->z_position = _z_windows.insert(_z_windows.end(), this);
1812}
1813
1822{
1823 for (Window *w : Window::IterateFromFront()) {
1824 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1825 return w;
1826 }
1827 }
1828
1829 return nullptr;
1830}
1831
1836{
1837 IConsoleClose();
1838
1839 _focused_window = nullptr;
1840 _mouseover_last_w = nullptr;
1841 _last_scroll_window = nullptr;
1842 _scrolling_viewport = false;
1843 _mouse_hovering = false;
1844
1846 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1847 NWidgetScrollbar::InvalidateDimensionCache();
1848
1850
1852}
1853
1858{
1860
1861 for (Window *w : Window::Iterate()) w->Close();
1862
1864
1865 assert(_z_windows.empty());
1866}
1867
1872{
1875 _thd.Reset();
1876}
1877
1878static void DecreaseWindowCounters()
1879{
1880 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1881
1882 for (Window *w : Window::Iterate()) {
1883 if (_scroller_click_timeout == 0) {
1884 /* Unclick scrollbar buttons if they are pressed. */
1885 for (auto &pair : w->widget_lookup) {
1886 NWidgetBase *nwid = pair.second;
1887 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1888 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1889 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1891 w->mouse_capture_widget = -1;
1892 sb->SetDirty(w);
1893 }
1894 }
1895 }
1896 }
1897
1898 /* Handle editboxes */
1899 for (auto &pair : w->querystrings) {
1900 pair.second->HandleEditBox(w, pair.first);
1901 }
1902
1903 w->OnMouseLoop();
1904 }
1905
1906 for (Window *w : Window::Iterate()) {
1907 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1909
1910 w->OnTimeout();
1911 w->RaiseButtons(true);
1912 }
1913 }
1914}
1915
1916static void HandlePlacePresize()
1917{
1918 if (_special_mouse_mode != WSM_PRESIZE) return;
1919
1920 Window *w = _thd.GetCallbackWnd();
1921 if (w == nullptr) return;
1922
1923 Point pt = GetTileBelowCursor();
1924 if (pt.x == -1) {
1925 _thd.selend.x = -1;
1926 return;
1927 }
1928
1929 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1930}
1931
1937{
1939
1940 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1941
1942 Window *w = _thd.GetCallbackWnd();
1943 if (w != nullptr) {
1944 /* Send an event in client coordinates. */
1945 Point pt;
1946 pt.x = _cursor.pos.x - w->left;
1947 pt.y = _cursor.pos.y - w->top;
1948 if (_left_button_down) {
1949 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1950 } else {
1951 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1952 }
1953 }
1954
1955 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1956 return ES_HANDLED;
1957}
1958
1960static void HandleMouseOver()
1961{
1962 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1963
1964 /* We changed window, put an OnMouseOver event to the last window */
1965 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1966 /* Reset mouse-over coordinates of previous window */
1967 Point pt = { -1, -1 };
1969 }
1970
1971 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1973
1974 if (w != nullptr) {
1975 /* send an event in client coordinates. */
1976 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
1977 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
1978 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
1979 }
1980}
1981
1987
1998static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
1999{
2000 if (v == nullptr) return;
2001
2002 const int min_visible = rect.Height();
2003
2004 int v_bottom = v->top + v->height - 1;
2005 int v_right = v->left + v->width - 1;
2006 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2007
2008 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2009 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2010
2011 /* Vertically, the rectangle is hidden behind v. */
2012 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2013 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2014 return;
2015 }
2016 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2017 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2018 return;
2019 }
2020
2021 /* Horizontally also hidden, force movement to a safe area. */
2022 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2023 *nx = v->left - min_visible - rect.left;
2024 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2025 *nx = v_right + min_visible - rect.right;
2026 } else {
2027 *ny = safe_y;
2028 }
2029}
2030
2038static void EnsureVisibleCaption(Window *w, int nx, int ny)
2039{
2040 /* Search for the title bar rectangle. */
2041 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2042 if (caption != nullptr) {
2043 const Rect caption_rect = caption->GetCurrentRect();
2044
2045 const int min_visible = caption_rect.Height();
2046
2047 /* Make sure the window doesn't leave the screen */
2048 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2049 ny = Clamp(ny, 0, _screen.height - min_visible);
2050
2051 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2052 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2053 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2054 }
2055
2056 if (w->viewport != nullptr) {
2057 w->viewport->left += nx - w->left;
2058 w->viewport->top += ny - w->top;
2059 }
2060
2061 w->left = nx;
2062 w->top = ny;
2063}
2064
2075void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2076{
2077 if (delta_x != 0 || delta_y != 0) {
2078 if (clamp_to_screen) {
2079 /* Determine the new right/bottom position. If that is outside of the bounds of
2080 * the resolution clamp it in such a manner that it stays within the bounds. */
2081 int new_right = w->left + w->width + delta_x;
2082 int new_bottom = w->top + w->height + delta_y;
2083 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2084 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2085 }
2086
2087 w->SetDirty();
2088
2089 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);
2090 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);
2091 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2092 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2093
2094 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);
2095 w->width = w->nested_root->current_x;
2096 w->height = w->nested_root->current_y;
2097 }
2098
2099 EnsureVisibleCaption(w, w->left, w->top);
2100
2101 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2102 if (schedule_resize) {
2103 w->ScheduleResize();
2104 } else {
2105 w->OnResize();
2106 }
2107 w->SetDirty();
2108}
2109
2116{
2118 return (w == nullptr) ? 0 : w->top + w->height;
2119}
2120
2127{
2129 return (w == nullptr) ? _screen.height : w->top;
2130}
2131
2132static bool _dragging_window;
2133
2139{
2140 /* Get out immediately if no window is being dragged at all. */
2141 if (!_dragging_window) return ES_NOT_HANDLED;
2142
2143 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2144 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2145
2146 /* Otherwise find the window... */
2147 for (Window *w : Window::Iterate()) {
2149 /* Stop the dragging if the left mouse button was released */
2150 if (!_left_button_down) {
2152 break;
2153 }
2154
2155 w->SetDirty();
2156
2157 int x = _cursor.pos.x + _drag_delta.x;
2158 int y = _cursor.pos.y + _drag_delta.y;
2159 int nx = x;
2160 int ny = y;
2161
2165 int delta;
2166
2167 for (const Window *v : Window::Iterate()) {
2168 if (v == w) continue; // Don't snap at yourself
2169
2170 if (y + w->height > v->top && y < v->top + v->height) {
2171 /* Your left border <-> other right border */
2172 delta = abs(v->left + v->width - x);
2173 if (delta <= hsnap) {
2174 nx = v->left + v->width;
2175 hsnap = delta;
2176 }
2177
2178 /* Your right border <-> other left border */
2179 delta = abs(v->left - x - w->width);
2180 if (delta <= hsnap) {
2181 nx = v->left - w->width;
2182 hsnap = delta;
2183 }
2184 }
2185
2186 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2187 /* Your left border <-> other left border */
2188 delta = abs(v->left - x);
2189 if (delta <= hsnap) {
2190 nx = v->left;
2191 hsnap = delta;
2192 }
2193
2194 /* Your right border <-> other right border */
2195 delta = abs(v->left + v->width - x - w->width);
2196 if (delta <= hsnap) {
2197 nx = v->left + v->width - w->width;
2198 hsnap = delta;
2199 }
2200 }
2201
2202 if (x + w->width > v->left && x < v->left + v->width) {
2203 /* Your top border <-> other bottom border */
2204 delta = abs(v->top + v->height - y);
2205 if (delta <= vsnap) {
2206 ny = v->top + v->height;
2207 vsnap = delta;
2208 }
2209
2210 /* Your bottom border <-> other top border */
2211 delta = abs(v->top - y - w->height);
2212 if (delta <= vsnap) {
2213 ny = v->top - w->height;
2214 vsnap = delta;
2215 }
2216 }
2217
2218 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2219 /* Your top border <-> other top border */
2220 delta = abs(v->top - y);
2221 if (delta <= vsnap) {
2222 ny = v->top;
2223 vsnap = delta;
2224 }
2225
2226 /* Your bottom border <-> other bottom border */
2227 delta = abs(v->top + v->height - y - w->height);
2228 if (delta <= vsnap) {
2229 ny = v->top + v->height - w->height;
2230 vsnap = delta;
2231 }
2232 }
2233 }
2234 }
2235
2236 EnsureVisibleCaption(w, nx, ny);
2237
2238 w->SetDirty();
2239 return ES_HANDLED;
2241 /* Stop the sizing if the left mouse button was released */
2242 if (!_left_button_down) {
2245 w->SetDirty();
2246 break;
2247 }
2248
2249 /* Compute difference in pixels between cursor position and reference point in the window.
2250 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2251 */
2252 int x, y = _cursor.pos.y - _drag_delta.y;
2254 x = _drag_delta.x - _cursor.pos.x;
2255 } else {
2256 x = _cursor.pos.x - _drag_delta.x;
2257 }
2258
2259 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2260 if (w->resize.step_width == 0) x = 0;
2261 if (w->resize.step_height == 0) y = 0;
2262
2263 /* Check the resize button won't go past the bottom of the screen */
2264 if (w->top + w->height + y > _screen.height) {
2265 y = _screen.height - w->height - w->top;
2266 }
2267
2268 /* X and Y has to go by step.. calculate it.
2269 * The cast to int is necessary else x/y are implicitly cast to
2270 * unsigned int, which won't work. */
2271 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2272 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2273
2274 /* Check that we don't go below the minimum set size */
2275 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2276 x = w->nested_root->smallest_x - w->width;
2277 }
2278 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2279 y = w->nested_root->smallest_y - w->height;
2280 }
2281
2282 /* Window already on size */
2283 if (x == 0 && y == 0) return ES_HANDLED;
2284
2285 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2286 _drag_delta.y += y;
2287 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2288 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2289 w->SetDirty();
2290 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2291 /* ResizeWindow() below ensures marking new position as dirty. */
2292 } else {
2293 _drag_delta.x += x;
2294 }
2295
2296 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2297 ResizeWindow(w, x, y);
2298 return ES_HANDLED;
2299 }
2300 }
2301
2302 _dragging_window = false;
2303 return ES_HANDLED;
2304}
2305
2311{
2314 _dragging_window = true;
2315
2316 _drag_delta.x = w->left - _cursor.pos.x;
2317 _drag_delta.y = w->top - _cursor.pos.y;
2318
2320}
2321
2327static void StartWindowSizing(Window *w, bool to_left)
2328{
2331 _dragging_window = true;
2332
2333 _drag_delta.x = _cursor.pos.x;
2334 _drag_delta.y = _cursor.pos.y;
2335
2337}
2338
2344{
2345 int i;
2347 bool rtl = false;
2348
2349 if (sb->type == NWID_HSCROLLBAR) {
2350 i = _cursor.pos.x - _cursorpos_drag_start.x;
2351 rtl = _current_text_dir == TD_RTL;
2352 } else {
2353 i = _cursor.pos.y - _cursorpos_drag_start.y;
2354 }
2355
2356 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2357 if (_scroller_click_timeout == 1) {
2358 _scroller_click_timeout = 3;
2359 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2361 w->SetDirty();
2362 }
2363 }
2364 return;
2365 }
2366
2367 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2368 int range = sb->GetCount() - sb->GetCapacity();
2369 if (range <= 0) return;
2370
2371 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2372 if (rtl) pos = range - pos;
2373 if (sb->SetPosition(pos)) {
2375 w->SetDirty();
2376 }
2377}
2378
2384{
2385 for (Window *w : Window::Iterate()) {
2386 if (w->mouse_capture_widget >= 0) {
2387 /* Abort if no button is clicked any more. */
2388 if (!_left_button_down) {
2390 w->mouse_capture_widget = -1;
2391 return ES_HANDLED;
2392 }
2393
2394 /* Handle scrollbar internally, or dispatch click event */
2396 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2398 } else {
2399 /* If cursor hasn't moved, there is nothing to do. */
2400 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2401
2402 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2403 w->OnClick(pt, w->mouse_capture_widget, 0);
2404 }
2405 return ES_HANDLED;
2406 }
2407 }
2408
2409 return ES_NOT_HANDLED;
2410}
2411
2417{
2418 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2419
2421
2422 /* When we don't have a last scroll window we are starting to scroll.
2423 * When the last scroll window and this are not the same we went
2424 * outside of the window and should not left-mouse scroll anymore. */
2425 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2426
2428 _cursor.fix_at = false;
2429 _scrolling_viewport = false;
2430 _last_scroll_window = nullptr;
2431 return ES_NOT_HANDLED;
2432 }
2433
2434 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2435 /* If the main window is following a vehicle, then first let go of it! */
2436 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
2437 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2438 return ES_NOT_HANDLED;
2439 }
2440
2441 Point delta;
2442 if (scrollwheel_scrolling) {
2443 /* We are using scrollwheels for scrolling */
2444 /* Use the integer part for movement */
2445 delta.x = static_cast<int>(_cursor.h_wheel);
2446 delta.y = static_cast<int>(_cursor.v_wheel);
2447 /* Keep the fractional part so that subtle movement is accumulated */
2448 float temp;
2449 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2450 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2451 } else {
2453 delta.x = -_cursor.delta.x;
2454 delta.y = -_cursor.delta.y;
2455 } else {
2456 delta.x = _cursor.delta.x;
2457 delta.y = _cursor.delta.y;
2458 }
2459 }
2460
2461 /* Create a scroll-event and send it to the window */
2462 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2463
2464 _cursor.delta.x = 0;
2465 _cursor.delta.y = 0;
2466 _cursor.wheel_moved = false;
2467 return ES_HANDLED;
2468}
2469
2481{
2482 bool bring_to_front = false;
2483
2484 if (w->window_class == WC_MAIN_WINDOW ||
2485 IsVitalWindow(w) ||
2486 w->window_class == WC_TOOLTIPS ||
2488 return true;
2489 }
2490
2491 /* Use unshaded window size rather than current size for shaded windows. */
2492 int w_width = w->width;
2493 int w_height = w->height;
2494 if (w->IsShaded()) {
2495 w_width = w->unshaded_size.width;
2496 w_height = w->unshaded_size.height;
2497 }
2498
2500 ++it;
2501 for (; !it.IsEnd(); ++it) {
2502 Window *u = *it;
2503 /* A modal child will prevent the activation of the parent window */
2505 u->SetWhiteBorder();
2506 u->SetDirty();
2507 return false;
2508 }
2509
2510 if (u->window_class == WC_MAIN_WINDOW ||
2511 IsVitalWindow(u) ||
2512 u->window_class == WC_TOOLTIPS ||
2514 continue;
2515 }
2516
2517 /* Window sizes don't interfere, leave z-order alone */
2518 if (w->left + w_width <= u->left ||
2519 u->left + u->width <= w->left ||
2520 w->top + w_height <= u->top ||
2521 u->top + u->height <= w->top) {
2522 continue;
2523 }
2524
2525 bring_to_front = true;
2526 }
2527
2528 if (bring_to_front) BringWindowToFront(w);
2529 return true;
2530}
2531
2540EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2541{
2542 QueryString *query = this->GetQueryString(wid);
2543 if (query == nullptr) return ES_NOT_HANDLED;
2544
2545 int action = QueryString::ACTION_NOTHING;
2546
2547 switch (query->text.HandleKeyPress(key, keycode)) {
2548 case HKPR_EDITING:
2549 this->SetWidgetDirty(wid);
2550 this->OnEditboxChanged(wid);
2551 break;
2552
2553 case HKPR_CURSOR:
2554 this->SetWidgetDirty(wid);
2555 /* For the OSK also invalidate the parent window */
2556 if (this->window_class == WC_OSK) this->InvalidateData();
2557 break;
2558
2559 case HKPR_CONFIRM:
2560 if (this->window_class == WC_OSK) {
2561 this->OnClick(Point(), WID_OSK_OK, 1);
2562 } else if (query->ok_button >= 0) {
2563 this->OnClick(Point(), query->ok_button, 1);
2564 } else {
2565 action = query->ok_button;
2566 }
2567 break;
2568
2569 case HKPR_CANCEL:
2570 if (this->window_class == WC_OSK) {
2571 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2572 } else if (query->cancel_button >= 0) {
2573 this->OnClick(Point(), query->cancel_button, 1);
2574 } else {
2575 action = query->cancel_button;
2576 }
2577 break;
2578
2579 case HKPR_NOT_HANDLED:
2580 return ES_NOT_HANDLED;
2581
2582 default: break;
2583 }
2584
2585 switch (action) {
2587 this->UnfocusFocusedWidget();
2588 break;
2589
2591 if (query->text.GetText().empty()) {
2592 /* If already empty, unfocus instead */
2593 this->UnfocusFocusedWidget();
2594 } else {
2595 query->text.DeleteAll();
2596 this->SetWidgetDirty(wid);
2597 this->OnEditboxChanged(wid);
2598 }
2599 break;
2600
2601 default:
2602 break;
2603 }
2604
2605 return ES_HANDLED;
2606}
2607
2612void HandleToolbarHotkey(int hotkey)
2613{
2614 assert(HasModalProgress() || IsLocalCompany());
2615
2617 if (w != nullptr) {
2618 if (w->window_desc.hotkeys != nullptr) {
2619 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2620 }
2621 }
2622}
2623
2629void HandleKeypress(uint keycode, char32_t key)
2630{
2631 /* World generation is multithreaded and messes with companies.
2632 * But there is no company related window open anyway, so _current_company is not used. */
2633 assert(HasModalProgress() || IsLocalCompany());
2634
2635 /*
2636 * The Unicode standard defines an area called the private use area. Code points in this
2637 * area are reserved for private use and thus not portable between systems. For instance,
2638 * Apple defines code points for the arrow keys in this area, but these are only printable
2639 * on a system running OS X. We don't want these keys to show up in text fields and such,
2640 * and thus we have to clear the unicode character when we encounter such a key.
2641 */
2642 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2643
2644 /*
2645 * If both key and keycode is zero, we don't bother to process the event.
2646 */
2647 if (key == 0 && keycode == 0) return;
2648
2649 /* Check if the focused window has a focused editbox */
2650 if (EditBoxInGlobalFocus()) {
2651 /* All input will in this case go to the focused editbox */
2652 if (_focused_window->window_class == WC_CONSOLE) {
2653 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2654 } else {
2655 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2656 }
2657 }
2658
2659 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2660 for (Window *w : Window::IterateFromFront()) {
2661 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2662 if (w->window_desc.hotkeys != nullptr) {
2663 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2664 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2665 }
2666 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2667 }
2668
2670 /* When there is no toolbar w is null, check for that */
2671 if (w != nullptr) {
2672 if (w->window_desc.hotkeys != nullptr) {
2673 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2674 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2675 }
2676 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2677 }
2678
2679 HandleGlobalHotkeys(key, keycode);
2680}
2681
2686{
2687 /* Call the event, start with the uppermost window. */
2688 for (Window *w : Window::IterateFromFront()) {
2689 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2690 }
2691}
2692
2698/* 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)
2699{
2700 QueryString *query = this->GetQueryString(wid);
2701 if (query == nullptr) return;
2702
2703 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2704 this->SetWidgetDirty(wid);
2705 this->OnEditboxChanged(wid);
2706 }
2707}
2708
2715void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2716{
2717 if (!EditBoxInGlobalFocus()) return;
2718
2719 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2720}
2721
2726static void HandleAutoscroll()
2727{
2728 if (_game_mode == GM_MENU || HasModalProgress()) return;
2730 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2731
2732 int x = _cursor.pos.x;
2733 int y = _cursor.pos.y;
2734 Window *w = FindWindowFromPt(x, y);
2735 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2737
2738 Viewport *vp = IsPtInWindowViewport(w, x, y);
2739 if (vp == nullptr) return;
2740
2741 x -= vp->left;
2742 y -= vp->top;
2743
2744 /* here allows scrolling in both x and y axis */
2745 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2746 static const int SCROLLSPEED = 3;
2747 if (x - 15 < 0) {
2748 w->viewport->CancelFollow(*w);
2749 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2750 } else if (15 - (vp->width - x) > 0) {
2751 w->viewport->CancelFollow(*w);
2752 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2753 }
2754 if (y - 15 < 0) {
2755 w->viewport->CancelFollow(*w);
2756 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2757 } else if (15 - (vp->height - y) > 0) {
2758 w->viewport->CancelFollow(*w);
2759 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2760 }
2761}
2762
2763enum MouseClick : uint8_t {
2764 MC_NONE = 0,
2765 MC_LEFT,
2766 MC_RIGHT,
2767 MC_DOUBLE_LEFT,
2768 MC_HOVER,
2769};
2770
2771static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2772static constexpr int MAX_OFFSET_HOVER = 5;
2773
2775
2776const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2777
2778static void ScrollMainViewport(int x, int y)
2779{
2780 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2781 Window *w = GetMainWindow();
2782 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2783 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2784 }
2785}
2786
2796static const int8_t scrollamt[16][2] = {
2797 { 0, 0},
2798 {-2, 0},
2799 { 0, -2},
2800 {-2, -1},
2801 { 2, 0},
2802 { 0, 0},
2803 { 2, -1},
2804 { 0, -2},
2805 { 0, 2},
2806 {-2, 1},
2807 { 0, 0},
2808 {-2, 0},
2809 { 2, 1},
2810 { 0, 2},
2811 { 2, 0},
2812 { 0, 0},
2813};
2814
2815static void HandleKeyScrolling()
2816{
2817 /*
2818 * Check that any of the dirkeys is pressed and that the focused window
2819 * doesn't have an edit-box as focused widget.
2820 */
2821 if (_dirkeys && !EditBoxInGlobalFocus()) {
2822 int factor = _shift_pressed ? 50 : 10;
2823
2824 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2825 /* Key scrolling stops following a vehicle. */
2826 Window *main_window = GetMainWindow();
2827 main_window->viewport->CancelFollow(*main_window);
2828 }
2829
2830 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2831 }
2832}
2833
2834static void MouseLoop(MouseClick click, int mousewheel)
2835{
2836 /* World generation is multithreaded and messes with companies.
2837 * But there is no company related window open anyway, so _current_company is not used. */
2838 assert(HasModalProgress() || IsLocalCompany());
2839
2840 HandlePlacePresize();
2842
2843 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2844 if (HandleMouseDragDrop() == ES_HANDLED) return;
2845 if (HandleWindowDragging() == ES_HANDLED) return;
2846 if (HandleActiveWidget() == ES_HANDLED) return;
2847 if (HandleViewportScroll() == ES_HANDLED) return;
2848
2850
2851 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2852 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2853
2854 int x = _cursor.pos.x;
2855 int y = _cursor.pos.y;
2856 Window *w = FindWindowFromPt(x, y);
2857 if (w == nullptr) return;
2858
2859 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2860 Viewport *vp = IsPtInWindowViewport(w, x, y);
2861
2862 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2863 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2864
2865 if (mousewheel != 0) {
2866 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2867 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) {
2868 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2869 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2870 }
2871 }
2872
2873 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2874 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2875 }
2876
2877 if (vp != nullptr) {
2878 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2879 _scrolling_viewport = true;
2880 _cursor.fix_at = true;
2881 return;
2882 }
2883
2884 switch (click) {
2885 case MC_DOUBLE_LEFT:
2886 case MC_LEFT:
2887 if (HandleViewportClicked(*vp, x, y)) return;
2890 _scrolling_viewport = true;
2891 _cursor.fix_at = false;
2892 return;
2893 }
2894 break;
2895
2896 case MC_RIGHT:
2899 _scrolling_viewport = true;
2902 DispatchRightClickEvent(w, x - w->left, y - w->top);
2903 return;
2904 }
2905 break;
2906
2907 default:
2908 break;
2909 }
2910 }
2911
2912 switch (click) {
2913 case MC_LEFT:
2914 case MC_DOUBLE_LEFT:
2915 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2916 return;
2917
2918 default:
2919 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2920 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2921 * Simulate a right button click so we can get started. */
2922 [[fallthrough]];
2923
2924 case MC_RIGHT:
2925 DispatchRightClickEvent(w, x - w->left, y - w->top);
2926 return;
2927
2928 case MC_HOVER:
2929 DispatchHoverEvent(w, x - w->left, y - w->top);
2930 break;
2931 }
2932
2933 /* We're not doing anything with 2D scrolling, so reset the value. */
2934 _cursor.h_wheel = 0.0f;
2935 _cursor.v_wheel = 0.0f;
2936 _cursor.wheel_moved = false;
2937}
2938
2943{
2944 /* World generation is multithreaded and messes with companies.
2945 * But there is no company related window open anyway, so _current_company is not used. */
2946 assert(HasModalProgress() || IsLocalCompany());
2947
2948 static std::chrono::steady_clock::time_point double_click_time = {};
2949 static Point double_click_pos = {0, 0};
2950
2951 /* Mouse event? */
2952 MouseClick click = MC_NONE;
2954 click = MC_LEFT;
2955 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2956 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2957 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2958 click = MC_DOUBLE_LEFT;
2959 }
2960 double_click_time = std::chrono::steady_clock::now();
2961 double_click_pos = _cursor.pos;
2962 _left_button_clicked = true;
2963 } else if (_right_button_clicked) {
2964 _right_button_clicked = false;
2965 click = MC_RIGHT;
2966 }
2967
2968 int mousewheel = 0;
2969 if (_cursor.wheel) {
2970 mousewheel = _cursor.wheel;
2971 _cursor.wheel = 0;
2972 }
2973
2974 static std::chrono::steady_clock::time_point hover_time = {};
2975 static Point hover_pos = {0, 0};
2976
2978 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
2979 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
2980 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
2981 hover_pos = _cursor.pos;
2982 hover_time = std::chrono::steady_clock::now();
2983 _mouse_hovering = false;
2984 } else if (!_mouse_hovering) {
2985 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
2986 click = MC_HOVER;
2987 _mouse_hovering = true;
2988 hover_time = std::chrono::steady_clock::now();
2989 }
2990 }
2991 }
2992
2993 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
2994 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
2996 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
2998 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3000 } else {
3001 MouseLoop(click, mousewheel);
3002 }
3003
3004 /* We have moved the mouse the required distance,
3005 * no need to move it at any later time. */
3006 _cursor.delta.x = 0;
3007 _cursor.delta.y = 0;
3008}
3009
3013static void CheckSoftLimit()
3014{
3015 if (_settings_client.gui.window_soft_limit == 0) return;
3016
3017 for (;;) {
3018 uint deletable_count = 0;
3019 Window *last_deletable = nullptr;
3020 for (Window *w : Window::IterateFromFront()) {
3021 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3022
3023 last_deletable = w;
3024 deletable_count++;
3025 }
3026
3027 /* We've not reached the soft limit yet. */
3028 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3029
3030 assert(last_deletable != nullptr);
3031 last_deletable->Close();
3032 }
3033}
3034
3039{
3040 /* World generation is multithreaded and messes with companies.
3041 * But there is no company related window open anyway, so _current_company is not used. */
3042 assert(HasModalProgress() || IsLocalCompany());
3043
3045
3046 /* Process scheduled window deletion. */
3048
3049 /* HandleMouseEvents was already called for this tick */
3051}
3052
3053static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3054
3055bool CanContinueRealtimeTick()
3056{
3057 auto now = std::chrono::steady_clock::now();
3058 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3059}
3060
3065{
3066 _realtime_tick_start = std::chrono::steady_clock::now();
3067 for (Window *w : Window::Iterate()) {
3068 w->OnRealtimeTick(delta_ms);
3069 }
3070}
3071
3073static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3074 extern int _caret_timer;
3075 _caret_timer += 3;
3076 CursorTick();
3077
3078 HandleKeyScrolling();
3080 DecreaseWindowCounters();
3081});
3082
3086});
3087
3089static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3090 if (_network_dedicated) return;
3091
3092 for (Window *w : Window::Iterate()) {
3095 w->SetDirty();
3096 }
3097 }
3098});
3099
3104{
3105 static auto last_time = std::chrono::steady_clock::now();
3106 auto now = std::chrono::steady_clock::now();
3107 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3108
3109 if (delta_ms.count() == 0) return;
3110
3111 last_time = now;
3112
3115
3117
3119 CallWindowRealtimeTickEvent(delta_ms.count());
3120
3121 /* Process invalidations before anything else. */
3122 for (Window *w : Window::Iterate()) {
3126 }
3127
3128 /* Skip the actual drawing on dedicated servers without screen.
3129 * But still empty the invalidation queues above. */
3130 if (_network_dedicated) return;
3131
3133
3134 for (Window *w : Window::Iterate()) {
3135 /* Update viewport only if window is not shaded. */
3136 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3137 }
3139 /* Redraw mouse cursor in case it was hidden */
3140 DrawMouseCursor();
3141
3142 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3143 /* We are done with the last draw-frame, so we know what sprites we
3144 * clicked on. Reset the picker mode and invalidate the window. */
3147 }
3148}
3149
3156{
3157 for (const Window *w : Window::Iterate()) {
3158 if (w->window_class == cls && w->window_number == number) w->SetDirty();
3159 }
3160}
3161
3169{
3170 for (const Window *w : Window::Iterate()) {
3171 if (w->window_class == cls && w->window_number == number) {
3172 w->SetWidgetDirty(widget_index);
3173 }
3174 }
3175}
3176
3182{
3183 for (const Window *w : Window::Iterate()) {
3184 if (w->window_class == cls) w->SetDirty();
3185 }
3186}
3187
3192{
3193 this->scheduled_resize = true;
3194}
3195
3200{
3201 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3202 while (this->scheduled_resize) {
3203 this->scheduled_resize = false;
3204 this->OnResize();
3205 }
3206}
3207
3213void Window::InvalidateData(int data, bool gui_scope)
3214{
3215 this->SetDirty();
3216 if (!gui_scope) {
3217 /* Schedule GUI-scope invalidation for next redraw. */
3218 this->scheduled_invalidation_data.push_back(data);
3219 }
3220 this->OnInvalidateData(data, gui_scope);
3221}
3222
3227{
3228 for (int data : this->scheduled_invalidation_data) {
3229 if (this->window_class == WC_INVALID) break;
3230 this->OnInvalidateData(data, true);
3231 }
3232 this->scheduled_invalidation_data.clear();
3233}
3234
3239{
3240 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3241
3242 for (const auto &pair : this->widget_lookup) {
3243 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3244 }
3245}
3246
3273void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3274{
3275 for (Window *w : Window::Iterate()) {
3276 if (w->window_class == cls && w->window_number == number) {
3277 w->InvalidateData(data, gui_scope);
3278 }
3279 }
3280}
3281
3290void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3291{
3292 for (Window *w : Window::Iterate()) {
3293 if (w->window_class == cls) {
3294 w->InvalidateData(data, gui_scope);
3295 }
3296 }
3297}
3298
3303{
3304 for (Window *w : Window::Iterate()) {
3305 w->OnGameTick();
3306 }
3307}
3308
3316{
3317 /* Note: the container remains stable, even when deleting windows. */
3318 for (Window *w : Window::Iterate()) {
3320 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3321
3322 w->Close();
3323 }
3324 }
3325}
3326
3335{
3336 /* Note: the container remains stable, even when closing windows. */
3337 for (Window *w : Window::Iterate()) {
3339 w->Close();
3340 }
3341 }
3342}
3343
3348{
3350 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3351 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3352 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3353}
3354
3360{
3361 /* Note: the container remains stable, even when deleting windows. */
3362 for (Window *w : Window::Iterate()) {
3364 w->Close();
3365 }
3366 }
3367
3368 for (const Window *w : Window::Iterate()) w->SetDirty();
3369}
3370
3377
3378void ReInitWindow(Window *w, bool zoom_changed)
3379{
3380 if (w == nullptr) return;
3381 if (zoom_changed) {
3382 w->nested_root->AdjustPaddingForZoom();
3384 }
3385 w->ReInit();
3386}
3387
3389void ReInitAllWindows(bool zoom_changed)
3390{
3392 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3393 NWidgetScrollbar::InvalidateDimensionCache();
3394
3396
3397 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3398 * so EnsureVisibleCaption uses the updated size information. */
3399 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3400 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3401 for (Window *w : Window::Iterate()) {
3402 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3403 ReInitWindow(w, zoom_changed);
3404 }
3405
3408
3409 /* Make sure essential parts of all windows are visible */
3410 RelocateAllWindows(_screen.width, _screen.height);
3412}
3413
3421static int PositionWindow(Window *w, WindowClass clss, int setting)
3422{
3423 if (w == nullptr || w->window_class != clss) {
3424 w = FindWindowById(clss, 0);
3425 }
3426 if (w == nullptr) return 0;
3427
3428 int old_left = w->left;
3429 switch (setting) {
3430 case 1: w->left = (_screen.width - w->width) / 2; break;
3431 case 2: w->left = _screen.width - w->width; break;
3432 default: w->left = 0; break;
3433 }
3434 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3435 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3436 return w->left;
3437}
3438
3445{
3446 Debug(misc, 5, "Repositioning Main Toolbar...");
3448}
3449
3456{
3457 Debug(misc, 5, "Repositioning statusbar...");
3459}
3460
3467{
3468 Debug(misc, 5, "Repositioning news message...");
3470}
3471
3478{
3479 Debug(misc, 5, "Repositioning network chat window...");
3481}
3482
3483
3490{
3491 for (const Window *w : Window::Iterate()) {
3492 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3493 w->viewport->follow_vehicle = to_index;
3494 w->SetDirty();
3495 }
3496 }
3497}
3498
3499
3505void RelocateAllWindows(int neww, int newh)
3506{
3508
3509 /* Reposition toolbar then status bar before other all windows. */
3510 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3511 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3512 wt->left = PositionMainToolbar(wt);
3513 }
3514
3515 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3516 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3517 ws->top = newh - ws->height;
3518 ws->left = PositionStatusbar(ws);
3519 }
3520
3521 for (Window *w : Window::Iterate()) {
3522 int left, top;
3523 /* XXX - this probably needs something more sane. For example specifying
3524 * in a 'backup'-desc that the window should always be centered. */
3525 switch (w->window_class) {
3526 case WC_MAIN_WINDOW:
3527 case WC_BOOTSTRAP:
3528 case WC_HIGHSCORE:
3529 case WC_ENDSCREEN:
3530 ResizeWindow(w, neww, newh);
3531 continue;
3532
3533 case WC_MAIN_TOOLBAR:
3534 case WC_STATUS_BAR:
3535 continue;
3536
3537 case WC_NEWS_WINDOW:
3538 top = newh - w->height;
3539 left = PositionNewsMessage(w);
3540 break;
3541
3543 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3544
3545 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3546 left = PositionNetworkChatWindow(w);
3547 break;
3548
3549 case WC_CONSOLE:
3550 IConsoleResize(w);
3551 continue;
3552
3553 default: {
3554 if (w->flags.Test(WindowFlag::Centred)) {
3555 top = (newh - w->height) >> 1;
3556 left = (neww - w->width) >> 1;
3557 break;
3558 }
3559
3560 left = w->left;
3561 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3562 if (left < 0) left = 0;
3563
3564 top = w->top;
3565 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3566 break;
3567 }
3568 }
3569
3570 EnsureVisibleCaption(w, left, top);
3571 }
3572}
3573
3578void PickerWindowBase::Close([[maybe_unused]] int data)
3579{
3581 this->Window::Close();
3582}
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:3578
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:249
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
Definition gfx_type.h:370
std::unique_ptr< NWidgetBase > MakeWindowNWidgetTree(std::span< const NWidgetPart > nwid_parts, NWidgetStacked **shade_select)
Make a nested widget tree for a window from a parts array.
Definition widget.cpp:3404
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:963
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:128
Point pos
logical mouse position
Definition gfx_type.h:125
bool in_window
mouse inside this window, determines drawing logic
Definition gfx_type.h:147
int wheel
mouse wheel movement
Definition gfx_type.h:127
Point delta
relative mouse movement in this tick
Definition gfx_type.h:126
Data about how and where to blit pixels.
Definition gfx_type.h:157
uint8_t window_snap_radius
windows snap at each other if closer than this
uint16_t hover_delay_ms
time required to activate a hover event, in milliseconds
uint8_t auto_scrolling
scroll when moving mouse to the edge (see ViewportAutoscrolling)
uint8_t scroll_mode
viewport scroll mode
RightClickClose right_click_wnd_close
close window with right click
uint8_t window_soft_limit
soft limit of maximum number of non-stickied non-vital windows (0 = no limit)
uint8_t toolbar_pos
position of toolbars, 0=left, 1=center, 2=right
uint8_t statusbar_pos
position of statusbar, 0=left, 1=center, 2=right
uint8_t scrollwheel_scrolling
scrolling using the scroll wheel?
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:975
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1099
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:1786
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:1452
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:3213
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:1136
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:3226
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1072
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:1018
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:3191
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:1776
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:1087
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:1764
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:1471
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:2698
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:1044
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:1414
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:1809
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:3199
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2540
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:1059
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:1799
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:3238
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:3455
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:1998
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2132
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:3359
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:1190
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:1705
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1177
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:852
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1218
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2685
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:880
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:3038
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3103
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3444
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3315
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:942
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2075
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:1573
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3477
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2327
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:1612
PreventHideDirection
Direction for moving the window.
Definition window.cpp:1983
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:1985
@ PHD_UP
Above v is a safe position.
Definition window.cpp:1984
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2726
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:2612
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3421
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:1537
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2771
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1163
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3489
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:2416
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:815
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:1821
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3347
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:2115
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:1394
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3389
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:2038
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1202
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2942
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:1680
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2138
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3466
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2126
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:787
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:1238
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2629
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3181
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:2772
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:1871
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1310
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:3273
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3372
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:1273
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3505
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:2343
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:2310
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3302
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2383
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:758
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:2796
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2480
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:3064
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:1936
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3334
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1960
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:3013
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1148
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1857
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1835
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:3168
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:2715
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3155
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:3290
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.