OpenTTD Source 20251126-master-g67ded4f980
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 /* Button click for this widget may already have been handled. */
598 if (this->IsWidgetLowered(widget) && this->timeout_timer == TIMEOUT_DURATION) return;
599
600 this->LowerWidget(widget);
601 this->SetTimeout();
602 this->SetWidgetDirty(widget);
603 SndClickBeep();
604}
605
606static void StartWindowDrag(Window *w);
607static void StartWindowSizing(Window *w, bool to_left);
608
616static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
617{
618 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
619 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
620
621 /* Allow dropdown close flag detection to work. */
623
624 bool focused_widget_changed = false;
625
626 /* If clicked on a window that previously did not have focus */
627 if (_focused_window != w) {
628 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
629 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
630 focused_widget_changed = true;
632 } else if (_focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU) {
633 /* The previously focused window was a dropdown menu, but the user clicked on another window that
634 * isn't focusable. Close the dropdown menu anyway. */
635 SetFocusedWindow(nullptr);
636 }
637 }
638
639 if (nw == nullptr) return; // exit if clicked outside of widgets
640
641 /* don't allow any interaction if the button has been disabled */
642 if (nw->IsDisabled()) return;
643
644 WidgetID widget_index = nw->GetIndex();
645
646 /* Clicked on a widget that is not disabled.
647 * So unless the clicked widget is the caption bar, change focus to this widget.
648 * Exception: In the OSK we always want the editbox to stay focused. */
649 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
650 /* focused_widget_changed is 'now' only true if the window this widget
651 * is in gained focus. In that case it must remain true, also if the
652 * local widget focus did not change. As such it's the logical-or of
653 * both changed states.
654 *
655 * If this is not preserved, then the OSK window would be opened when
656 * a user has the edit box focused and then click on another window and
657 * then back again on the edit box (to type some text).
658 */
659 focused_widget_changed |= w->SetFocusedWidget(widget_index);
660 }
661
662 /* Dropdown window of this widget was closed so don't process click this time. */
664
665 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
666
667 Point pt = { x, y };
668
669 switch (widget_type) {
670 case NWID_VSCROLLBAR:
671 case NWID_HSCROLLBAR:
672 ScrollbarClickHandler(w, nw, x, y);
673 break;
674
675 case WWT_EDITBOX: {
676 QueryString *query = w->GetQueryString(widget_index);
677 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
678 break;
679 }
680
681 case WWT_CLOSEBOX: // 'X'
682 w->Close();
683 return;
684
685 case WWT_CAPTION: // 'Title bar'
687 return;
688
689 case WWT_RESIZEBOX:
690 /* When the resize widget is on the left size of the window
691 * we assume that that button is used to resize to the left. */
692 StartWindowSizing(w, nw->pos_x < (w->width / 2));
693 nw->SetDirty(w);
694 return;
695
696 case WWT_DEFSIZEBOX: {
697 if (_ctrl_pressed) {
698 if (click_count > 1) {
699 w->window_desc.pref_width = 0;
701 } else {
704 }
705 } else {
706 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
707 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
708
709 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
710 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
711 /* dx and dy has to go by step.. calculate it.
712 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
713 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
714 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
715 ResizeWindow(w, dx, dy, false);
716 }
717
718 nw->SetLowered(true);
719 nw->SetDirty(w);
720 w->SetTimeout();
721 break;
722 }
723
724 case WWT_DEBUGBOX:
726 break;
727
728 case WWT_SHADEBOX:
729 nw->SetDirty(w);
730 w->SetShaded(!w->IsShaded());
731 return;
732
733 case WWT_STICKYBOX:
735 nw->SetDirty(w);
737 return;
738
739 default:
740 break;
741 }
742
743 /* Widget has no index, so the window is not interested in it. */
744 if (widget_index < 0) return;
745
746 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
747 if (w->IsWidgetHighlighted(widget_index)) {
748 w->SetWidgetHighlight(widget_index, TC_INVALID);
749 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
750 }
751
752 w->OnClick(pt, widget_index, click_count);
753}
754
761static void DispatchRightClickEvent(Window *w, int x, int y)
762{
763 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
764 if (wid == nullptr) return;
765
766 Point pt = { x, y };
767
768 /* No widget to handle, or the window is not interested in it. */
769 if (wid->GetIndex() >= 0) {
770 if (w->OnRightClick(pt, wid->GetIndex())) return;
771 }
772
773 /* Right-click close is enabled and there is a closebox. */
775 w->Close();
777 /* Right-click close is enabled, but excluding sticky windows. */
778 w->Close();
779 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
780 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_RIGHT_CLICK);
781 }
782}
783
790static void DispatchHoverEvent(Window *w, int x, int y)
791{
792 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
793
794 /* No widget to handle */
795 if (wid == nullptr) return;
796
797 Point pt = { x, y };
798
799 /* Show the tooltip if there is any */
800 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
801 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_HOVER);
802 return;
803 }
804
805 /* Widget has no index, so the window is not interested in it. */
806 if (wid->GetIndex() < 0) return;
807
808 w->OnHover(pt, wid->GetIndex());
809}
810
818static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
819{
820 if (nwid == nullptr) return;
821
822 /* Using wheel on caption/shade-box shades or unshades the window. */
823 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
824 w->SetShaded(wheel < 0);
825 return;
826 }
827
828 /* Wheeling a vertical scrollbar. */
829 if (nwid->type == NWID_VSCROLLBAR) {
830 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
831 if (sb->GetCount() > sb->GetCapacity()) {
832 if (sb->UpdatePosition(wheel)) {
833 w->OnScrollbarScroll(nwid->GetIndex());
834 w->SetDirty();
835 }
836 }
837 return;
838 }
839
840 /* Scroll the widget attached to the scrollbar. */
841 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
842 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
843 if (sb->UpdatePosition(wheel)) {
845 w->SetDirty();
846 }
847 }
848}
849
855static bool MayBeShown(const Window *w)
856{
857 /* If we're not modal, everything is okay. */
858 if (!HasModalProgress()) return true;
859
860 switch (w->window_class) {
861 case WC_MAIN_WINDOW:
862 case WC_MODAL_PROGRESS:
864 return true;
865
866 default:
867 return false;
868 }
869}
870
883static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
884{
886 ++it;
887 for (; !it.IsEnd(); ++it) {
888 const Window *v = *it;
889 if (MayBeShown(v) &&
890 right > v->left &&
891 bottom > v->top &&
892 left < v->left + v->width &&
893 top < v->top + v->height) {
894 /* v and rectangle intersect with each other */
895 int x;
896
897 if (left < (x = v->left)) {
898 DrawOverlappedWindow(w, left, top, x, bottom);
899 DrawOverlappedWindow(w, x, top, right, bottom);
900 return;
901 }
902
903 if (right > (x = v->left + v->width)) {
904 DrawOverlappedWindow(w, left, top, x, bottom);
905 DrawOverlappedWindow(w, x, top, right, bottom);
906 return;
907 }
908
909 if (top < (x = v->top)) {
910 DrawOverlappedWindow(w, left, top, right, x);
911 DrawOverlappedWindow(w, left, x, right, bottom);
912 return;
913 }
914
915 if (bottom > (x = v->top + v->height)) {
916 DrawOverlappedWindow(w, left, top, right, x);
917 DrawOverlappedWindow(w, left, x, right, bottom);
918 return;
919 }
920
921 return;
922 }
923 }
924
925 /* Setup blitter, and dispatch a repaint event to window *wz */
926 DrawPixelInfo *dp = _cur_dpi;
927 dp->width = right - left;
928 dp->height = bottom - top;
929 dp->left = left - w->left;
930 dp->top = top - w->top;
931 dp->pitch = _screen.pitch;
932 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
933 dp->zoom = ZoomLevel::Min;
934 w->OnPaint();
935}
936
945void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
946{
947 DrawPixelInfo bk;
948 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
949
950 for (Window *w : Window::IterateFromBack()) {
951 if (MayBeShown(w) &&
952 right > w->left &&
953 bottom > w->top &&
954 left < w->left + w->width &&
955 top < w->top + w->height) {
956 /* Window w intersects with the rectangle => needs repaint */
957 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));
958 }
959 }
960}
961
967{
968 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
969}
970
978void Window::ReInit(int rx, int ry, bool reposition)
979{
980 this->SetDirty(); // Mark whole current window as dirty.
981
982 /* Save current size. */
983 int window_width = this->width * _gui_scale / this->scale;
984 int window_height = this->height * _gui_scale / this->scale;
985 this->scale = _gui_scale;
986
987 this->OnInit();
988 /* Re-initialize window smallest size. */
989 this->nested_root->SetupSmallestSize(this);
990 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
991 this->width = this->nested_root->smallest_x;
992 this->height = this->nested_root->smallest_y;
993 this->resize.step_width = this->nested_root->resize_x;
994 this->resize.step_height = this->nested_root->resize_y;
995
996 /* Resize as close to the original size + requested resize as possible. */
997 window_width = std::max(window_width + rx, this->width);
998 window_height = std::max(window_height + ry, this->height);
999 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
1000 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
1001 /* dx and dy has to go by step.. calculate it.
1002 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1003 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1004 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1005
1006 if (reposition) {
1007 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1008 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1009 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1010 }
1011
1012 ResizeWindow(this, dx, dy, true, false);
1013 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1014}
1015
1021void Window::SetShaded(bool make_shaded)
1022{
1023 if (this->shade_select == nullptr) return;
1024
1025 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1026 if (this->shade_select->shown_plane != desired) {
1027 if (make_shaded) {
1028 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1029 this->unshaded_size.width = this->width;
1030 this->unshaded_size.height = this->height;
1031 this->shade_select->SetDisplayedPlane(desired);
1032 this->ReInit(0, -this->height);
1033 } else {
1034 this->shade_select->SetDisplayedPlane(desired);
1035 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1036 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1037 this->ReInit(dx, dy);
1038 }
1039 }
1040}
1041
1048{
1049 for (Window *v : Window::Iterate()) {
1050 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1051 }
1052
1053 return nullptr;
1054}
1055
1063{
1064 for (Window *v : Window::Iterate()) {
1065 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1066 }
1067
1068 return nullptr;
1069}
1070
1076{
1077 Window *child = this->FindChildWindow(wc);
1078 while (child != nullptr) {
1079 child->Close();
1080 child = this->FindChildWindow(wc);
1081 }
1082}
1083
1084
1091{
1092 Window *child = this->FindChildWindowById(wc, number);
1093 while (child != nullptr) {
1094 child->Close();
1095 child = this->FindChildWindowById(wc, number);
1096 }
1097}
1098
1102void Window::Close([[maybe_unused]] int data)
1103{
1104 /* Don't close twice. */
1105 if (*this->z_position == nullptr) return;
1106
1107 *this->z_position = nullptr;
1108
1109 if (_thd.window_class == this->window_class &&
1110 _thd.window_number == this->window_number) {
1112 }
1113
1114 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1115 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1116
1117 /* We can't scroll the window when it's closed. */
1118 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1119
1120 /* Make sure we don't try to access non-existing query strings. */
1121 this->querystrings.clear();
1122
1123 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1124 if (_focused_window == this) {
1125 this->OnFocusLost(true);
1126 _focused_window = nullptr;
1127 }
1128
1129 this->CloseChildWindows();
1130
1131 this->SetDirty();
1132
1133 Window::closed_windows.push_back(this);
1134}
1135
1140{
1141 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1142 assert(*this->z_position == nullptr);
1143}
1144
1152{
1153 for (Window *w : Window::Iterate()) {
1154 if (w->window_class == cls && w->window_number == number) return w;
1155 }
1156
1157 return nullptr;
1158}
1159
1167{
1168 for (Window *w : Window::Iterate()) {
1169 if (w->window_class == cls) return w;
1170 }
1171
1172 return nullptr;
1173}
1174
1181{
1183 assert(w != nullptr);
1184 return w;
1185}
1186
1193void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1194{
1195 Window *w = FindWindowById(cls, number);
1196 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1197 w->Close(data);
1198 }
1199}
1200
1206{
1207 /* Note: the container remains stable, even when deleting windows. */
1208 for (Window *w : Window::Iterate()) {
1209 if (w->window_class == cls) {
1210 w->Close(data);
1211 }
1212 }
1213}
1214
1222{
1223 /* Note: the container remains stable, even when deleting windows. */
1224 for (Window *w : Window::Iterate()) {
1225 if (w->owner == id) {
1226 w->Close();
1227 }
1228 }
1229
1230 /* Also delete the company specific windows that don't have a company-colour. */
1232}
1233
1241void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1242{
1243 for (Window *w : Window::Iterate()) {
1244 if (w->owner != old_owner) continue;
1245
1246 switch (w->window_class) {
1247 case WC_COMPANY_COLOUR:
1248 case WC_FINANCES:
1249 case WC_STATION_LIST:
1250 case WC_TRAINS_LIST:
1251 case WC_ROADVEH_LIST:
1252 case WC_SHIPS_LIST:
1253 case WC_AIRCRAFT_LIST:
1254 case WC_BUY_COMPANY:
1255 case WC_COMPANY:
1257 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().
1258 continue;
1259
1260 default:
1261 w->owner = new_owner;
1262 break;
1263 }
1264 }
1265}
1266
1267static void BringWindowToFront(Window *w, bool dirty = true);
1268
1277{
1278 Window *w = FindWindowById(cls, number);
1279
1280 if (w != nullptr) {
1281 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1282
1283 w->SetWhiteBorder();
1285 w->SetDirty();
1286 }
1287
1288 return w;
1289}
1290
1291static inline bool IsVitalWindow(const Window *w)
1292{
1293 switch (w->window_class) {
1294 case WC_MAIN_TOOLBAR:
1295 case WC_STATUS_BAR:
1296 case WC_NEWS_WINDOW:
1298 return true;
1299
1300 default:
1301 return false;
1302 }
1303}
1304
1314{
1315 assert(wc != WC_INVALID);
1316
1317 uint z_priority = 0;
1318
1319 switch (wc) {
1320 case WC_TOOLTIPS:
1321 ++z_priority;
1322 [[fallthrough]];
1323
1324 case WC_ERRMSG:
1326 ++z_priority;
1327 [[fallthrough]];
1328
1329 case WC_ENDSCREEN:
1330 ++z_priority;
1331 [[fallthrough]];
1332
1333 case WC_HIGHSCORE:
1334 ++z_priority;
1335 [[fallthrough]];
1336
1337 case WC_DROPDOWN_MENU:
1338 ++z_priority;
1339 [[fallthrough]];
1340
1341 case WC_MAIN_TOOLBAR:
1342 case WC_STATUS_BAR:
1343 ++z_priority;
1344 [[fallthrough]];
1345
1346 case WC_OSK:
1347 ++z_priority;
1348 [[fallthrough]];
1349
1350 case WC_QUERY_STRING:
1352 ++z_priority;
1353 [[fallthrough]];
1354
1356 case WC_MODAL_PROGRESS:
1358 case WC_SAVE_PRESET:
1359 ++z_priority;
1360 [[fallthrough]];
1361
1363 case WC_SAVELOAD:
1364 case WC_GAME_OPTIONS:
1365 case WC_CUSTOM_CURRENCY:
1366 case WC_NETWORK_WINDOW:
1367 case WC_GRF_PARAMETERS:
1368 case WC_SCRIPT_LIST:
1369 case WC_SCRIPT_SETTINGS:
1370 case WC_TEXTFILE:
1371 ++z_priority;
1372 [[fallthrough]];
1373
1374 case WC_CONSOLE:
1375 ++z_priority;
1376 [[fallthrough]];
1377
1378 case WC_NEWS_WINDOW:
1379 ++z_priority;
1380 [[fallthrough]];
1381
1382 default:
1383 ++z_priority;
1384 [[fallthrough]];
1385
1386 case WC_MAIN_WINDOW:
1387 return z_priority;
1388 }
1389}
1390
1397static void BringWindowToFront(Window *w, bool dirty)
1398{
1399 auto priority = GetWindowZPriority(w->window_class);
1400 WindowList::iterator dest = _z_windows.begin();
1401 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1402
1403 if (dest != w->z_position) {
1404 _z_windows.splice(dest, _z_windows, w->z_position);
1405 }
1406
1407 if (dirty) w->SetDirty();
1408}
1409
1418{
1419 /* Set up window properties; some of them are needed to set up smallest size below */
1420 this->window_class = this->window_desc.cls;
1421 this->SetWhiteBorder();
1423 this->owner = INVALID_OWNER;
1424 this->nested_focus = nullptr;
1425 this->window_number = window_number;
1426
1427 this->OnInit();
1428 /* Initialize smallest size. */
1429 this->nested_root->SetupSmallestSize(this);
1430 /* Initialize to smallest size. */
1431 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1432
1433 /* Further set up window properties,
1434 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1435 this->resize.step_width = this->nested_root->resize_x;
1436 this->resize.step_height = this->nested_root->resize_y;
1437
1438 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1439 * (so we don't interrupt typing) unless the new window has a text box. */
1440 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1441 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1442 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1443
1444 /* Insert the window into the correct location in the z-ordering. */
1445 BringWindowToFront(this, false);
1446}
1447
1455void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1456{
1457 this->left = x;
1458 this->top = y;
1459 this->width = sm_width;
1460 this->height = sm_height;
1461}
1462
1474void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1475{
1476 if (allow_resize) {
1477 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1478 def_height = std::max(def_height, this->height);
1479 /* Try to make windows smaller when our window is too small.
1480 * w->(width|height) is normally the same as min_(width|height),
1481 * but this way the GUIs can be made a little more dynamic;
1482 * one can use the same spec for multiple windows and those
1483 * can then determine the real minimum size of the window. */
1484 if (this->width != def_width || this->height != def_height) {
1485 /* Think about the overlapping toolbars when determining the minimum window size */
1486 int free_height = _screen.height;
1487 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1488 if (wt != nullptr) free_height -= wt->height;
1490 if (wt != nullptr) free_height -= wt->height;
1491
1492 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1493 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1494
1495 /* X and Y has to go by step.. calculate it.
1496 * The cast to int is necessary else x/y are implicitly cast to
1497 * unsigned int, which won't work. */
1498 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1499 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1500
1501 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1502 /* ResizeWindow() calls this->OnResize(). */
1503 } else {
1504 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1505 this->OnResize();
1506 }
1507 }
1508
1509 int nx = this->left;
1510 int ny = this->top;
1511
1512 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1513
1514 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1515 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1516 nx = std::max(nx, 0);
1517
1518 if (this->viewport != nullptr) {
1519 this->viewport->left += nx - this->left;
1520 this->viewport->top += ny - this->top;
1521 }
1522 this->left = nx;
1523 this->top = ny;
1524
1525 this->SetDirty();
1526}
1527
1540static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1541{
1542 int right = width + left;
1543 int bottom = height + top;
1544
1545 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1546
1547 /* Make sure it is not obscured by any window. */
1548 for (const Window *w : Window::Iterate()) {
1549 if (w->window_class == WC_MAIN_WINDOW) continue;
1550
1551 if (right > w->left &&
1552 w->left + w->width > left &&
1553 bottom > w->top &&
1554 w->top + w->height > top) {
1555 return false;
1556 }
1557 }
1558
1559 pos.x = left;
1560 pos.y = top;
1561 return true;
1562}
1563
1576static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1577{
1578 bool rtl = _current_text_dir == TD_RTL;
1579
1580 /* Left part of the rectangle may be at most 1/4 off-screen,
1581 * right part of the rectangle may be at most 1/2 off-screen
1582 */
1583 if (rtl) {
1584 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1585 } else {
1586 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1587 }
1588
1589 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1590 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1591
1592 /* Make sure it is not obscured by any window. */
1593 for (const Window *w : Window::Iterate()) {
1594 if (w->window_class == WC_MAIN_WINDOW) continue;
1595
1596 if (left + width > w->left &&
1597 w->left + w->width > left &&
1598 top + height > w->top &&
1599 w->top + w->height > top) {
1600 return false;
1601 }
1602 }
1603
1604 pos.x = left;
1605 pos.y = top;
1606 return true;
1607}
1608
1615static Point GetAutoPlacePosition(int width, int height)
1616{
1617 Point pt;
1618
1619 bool rtl = _current_text_dir == TD_RTL;
1620
1621 /* First attempt, try top-left of the screen */
1622 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1623 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1624 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1625
1626 /* Second attempt, try around all existing windows.
1627 * The new window must be entirely on-screen, and not overlap with an existing window.
1628 * Eight starting points are tried, two at each corner.
1629 */
1630 for (const Window *w : Window::Iterate()) {
1631 if (w->window_class == WC_MAIN_WINDOW) continue;
1632
1633 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1634 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1635 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1636 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1637 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1638 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1639 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1640 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1641 }
1642
1643 /* Third attempt, try around all existing windows.
1644 * The new window may be partly off-screen, and must not overlap with an existing window.
1645 * Only four starting points are tried.
1646 */
1647 for (const Window *w : Window::Iterate()) {
1648 if (w->window_class == WC_MAIN_WINDOW) continue;
1649
1650 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1651 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1652 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1653 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1654 }
1655
1656 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1657 * of the closebox
1658 */
1659 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1660 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1661 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1662
1663restart:
1664 for (const Window *w : Window::Iterate()) {
1665 if (w->left == left && w->top == top) {
1666 left += offset_x;
1667 top += offset_y;
1668 goto restart;
1669 }
1670 }
1671
1672 pt.x = left;
1673 pt.y = top;
1674 return pt;
1675}
1676
1684{
1685 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1686 assert(w != nullptr);
1687 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1688 return pt;
1689}
1690
1700{
1701 Point pt = GetToolbarAlignedWindowPosition(window_width);
1703 if (w != nullptr && w->top == pt.y && !_settings_client.gui.link_terraform_toolbar) {
1704 pt.x = w->left + (_current_text_dir == TD_RTL ? w->width : - window_width);
1705 }
1706 return pt;
1707}
1708
1726static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1727{
1728 Point pt;
1729 const Window *w;
1730
1731 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1732 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1733
1734 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1735 bool rtl = _current_text_dir == TD_RTL;
1736 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1737 pt.x = w->left + (rtl ? w->width - default_width : 0);
1738 pt.y = w->top + w->height;
1739 return pt;
1740 } else {
1741 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1742 * - Y position: closebox of parent + closebox of child + statusbar
1743 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1744 */
1745 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1746 if (w->top + 3 * indent_y < _screen.height) {
1747 pt.y = w->top + indent_y;
1748 int indent_close = NWidgetLeaf::closebox_dimension.width;
1749 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1750 if (_current_text_dir == TD_RTL) {
1751 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1752 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1753 } else {
1754 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1755 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1756 }
1757 }
1758 }
1759 }
1760
1761 switch (desc.default_pos) {
1762 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1763 return GetToolbarAlignedWindowPosition(default_width);
1764
1765 case WDP_AUTO: // Find a good automatic position for the window
1766 return GetAutoPlacePosition(default_width, default_height);
1767
1768 case WDP_CENTER: // Centre the window horizontally
1769 pt.x = (_screen.width - default_width) / 2;
1770 pt.y = (_screen.height - default_height) / 2;
1771 break;
1772
1773 case WDP_MANUAL:
1774 pt.x = 0;
1775 pt.y = 0;
1776 break;
1777
1778 default:
1779 NOT_REACHED();
1780 }
1781
1782 return pt;
1783}
1784
1785/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1786{
1787 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1788}
1789
1798{
1799 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1800 this->nested_root->FillWidgetLookup(this->widget_lookup);
1801}
1802
1808{
1809 this->InitializeData(window_number);
1810 this->ApplyDefaults();
1811 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1812 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1813 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1814}
1815
1821{
1822 this->CreateNestedTree();
1823 this->FinishInitNested(window_number);
1824}
1825
1830Window::Window(WindowDesc &desc) : window_desc(desc), scale(_gui_scale), mouse_capture_widget(INVALID_WIDGET)
1831{
1832 this->z_position = _z_windows.insert(_z_windows.end(), this);
1833}
1834
1843{
1844 for (Window *w : Window::IterateFromFront()) {
1845 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1846 return w;
1847 }
1848 }
1849
1850 return nullptr;
1851}
1852
1857{
1858 IConsoleClose();
1859
1860 _focused_window = nullptr;
1861 _mouseover_last_w = nullptr;
1862 _last_scroll_window = nullptr;
1863 _scrolling_viewport = false;
1864 _mouse_hovering = false;
1865
1867 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1868 NWidgetScrollbar::InvalidateDimensionCache();
1869
1871
1873}
1874
1879{
1881
1882 for (Window *w : Window::Iterate()) w->Close();
1883
1885
1886 assert(_z_windows.empty());
1887}
1888
1893{
1896 _thd.Reset();
1897}
1898
1899static void DecreaseWindowCounters()
1900{
1901 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1902
1903 for (Window *w : Window::Iterate()) {
1904 if (_scroller_click_timeout == 0) {
1905 /* Unclick scrollbar buttons if they are pressed. */
1906 for (auto &pair : w->widget_lookup) {
1907 NWidgetBase *nwid = pair.second;
1908 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1909 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1910 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1913 sb->SetDirty(w);
1914 }
1915 }
1916 }
1917 }
1918
1919 /* Handle editboxes */
1920 for (auto &pair : w->querystrings) {
1921 pair.second->HandleEditBox(w, pair.first);
1922 }
1923
1924 w->OnMouseLoop();
1925 }
1926
1927 for (Window *w : Window::Iterate()) {
1928 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1930
1931 w->OnTimeout();
1932 w->RaiseButtons(true);
1933 }
1934 }
1935}
1936
1937static void HandlePlacePresize()
1938{
1939 if (_special_mouse_mode != WSM_PRESIZE) return;
1940
1941 Window *w = _thd.GetCallbackWnd();
1942 if (w == nullptr) return;
1943
1944 Point pt = GetTileBelowCursor();
1945 if (pt.x == -1) {
1946 _thd.selend.x = -1;
1947 return;
1948 }
1949
1950 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1951}
1952
1958{
1960
1961 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1962
1963 Window *w = _thd.GetCallbackWnd();
1964 if (w != nullptr) {
1965 /* Send an event in client coordinates. */
1966 Point pt;
1967 pt.x = _cursor.pos.x - w->left;
1968 pt.y = _cursor.pos.y - w->top;
1969 if (_left_button_down) {
1970 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1971 } else {
1972 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1973 }
1974 }
1975
1976 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1977 return ES_HANDLED;
1978}
1979
1981static void HandleMouseOver()
1982{
1983 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1984
1985 /* We changed window, put an OnMouseOver event to the last window */
1986 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1987 /* Reset mouse-over coordinates of previous window */
1988 Point pt = { -1, -1 };
1990 }
1991
1992 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1994
1995 if (w != nullptr) {
1996 /* send an event in client coordinates. */
1997 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
1998 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
1999 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
2000 }
2001}
2002
2008
2019static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
2020{
2021 if (v == nullptr) return;
2022
2023 const int min_visible = rect.Height();
2024
2025 int v_bottom = v->top + v->height - 1;
2026 int v_right = v->left + v->width - 1;
2027 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2028
2029 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2030 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2031
2032 /* Vertically, the rectangle is hidden behind v. */
2033 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2034 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2035 return;
2036 }
2037 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2038 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2039 return;
2040 }
2041
2042 /* Horizontally also hidden, force movement to a safe area. */
2043 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2044 *nx = v->left - min_visible - rect.left;
2045 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2046 *nx = v_right + min_visible - rect.right;
2047 } else {
2048 *ny = safe_y;
2049 }
2050}
2051
2059static void EnsureVisibleCaption(Window *w, int nx, int ny)
2060{
2061 /* Search for the title bar rectangle. */
2062 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2063 if (caption != nullptr) {
2064 const Rect caption_rect = caption->GetCurrentRect();
2065
2066 const int min_visible = caption_rect.Height();
2067
2068 /* Make sure the window doesn't leave the screen */
2069 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2070 ny = Clamp(ny, 0, _screen.height - min_visible);
2071
2072 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2073 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2074 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2075 }
2076
2077 if (w->viewport != nullptr) {
2078 w->viewport->left += nx - w->left;
2079 w->viewport->top += ny - w->top;
2080 }
2081
2082 w->left = nx;
2083 w->top = ny;
2084}
2085
2096void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2097{
2098 if (delta_x != 0 || delta_y != 0) {
2099 if (clamp_to_screen) {
2100 /* Determine the new right/bottom position. If that is outside of the bounds of
2101 * the resolution clamp it in such a manner that it stays within the bounds. */
2102 int new_right = w->left + w->width + delta_x;
2103 int new_bottom = w->top + w->height + delta_y;
2104 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2105 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2106 }
2107
2108 w->SetDirty();
2109
2110 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);
2111 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);
2112 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2113 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2114
2115 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);
2116 w->width = w->nested_root->current_x;
2117 w->height = w->nested_root->current_y;
2118 }
2119
2120 EnsureVisibleCaption(w, w->left, w->top);
2121
2122 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2123 if (schedule_resize) {
2124 w->ScheduleResize();
2125 } else {
2126 w->OnResize();
2127 }
2128 w->SetDirty();
2129}
2130
2137{
2139 return (w == nullptr) ? 0 : w->top + w->height;
2140}
2141
2148{
2150 return (w == nullptr) ? _screen.height : w->top;
2151}
2152
2153static bool _dragging_window;
2154
2160{
2161 /* Get out immediately if no window is being dragged at all. */
2162 if (!_dragging_window) return ES_NOT_HANDLED;
2163
2164 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2165 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2166
2167 /* Otherwise find the window... */
2168 for (Window *w : Window::Iterate()) {
2170 /* Stop the dragging if the left mouse button was released */
2171 if (!_left_button_down) {
2173 break;
2174 }
2175
2176 w->SetDirty();
2177
2178 int x = _cursor.pos.x + _drag_delta.x;
2179 int y = _cursor.pos.y + _drag_delta.y;
2180 int nx = x;
2181 int ny = y;
2182
2186 int delta;
2187
2188 for (const Window *v : Window::Iterate()) {
2189 if (v == w) continue; // Don't snap at yourself
2190
2191 if (y + w->height > v->top && y < v->top + v->height) {
2192 /* Your left border <-> other right border */
2193 delta = abs(v->left + v->width - x);
2194 if (delta <= hsnap) {
2195 nx = v->left + v->width;
2196 hsnap = delta;
2197 }
2198
2199 /* Your right border <-> other left border */
2200 delta = abs(v->left - x - w->width);
2201 if (delta <= hsnap) {
2202 nx = v->left - w->width;
2203 hsnap = delta;
2204 }
2205 }
2206
2207 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2208 /* Your left border <-> other left border */
2209 delta = abs(v->left - x);
2210 if (delta <= hsnap) {
2211 nx = v->left;
2212 hsnap = delta;
2213 }
2214
2215 /* Your right border <-> other right border */
2216 delta = abs(v->left + v->width - x - w->width);
2217 if (delta <= hsnap) {
2218 nx = v->left + v->width - w->width;
2219 hsnap = delta;
2220 }
2221 }
2222
2223 if (x + w->width > v->left && x < v->left + v->width) {
2224 /* Your top border <-> other bottom border */
2225 delta = abs(v->top + v->height - y);
2226 if (delta <= vsnap) {
2227 ny = v->top + v->height;
2228 vsnap = delta;
2229 }
2230
2231 /* Your bottom border <-> other top border */
2232 delta = abs(v->top - y - w->height);
2233 if (delta <= vsnap) {
2234 ny = v->top - w->height;
2235 vsnap = delta;
2236 }
2237 }
2238
2239 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2240 /* Your top border <-> other top border */
2241 delta = abs(v->top - y);
2242 if (delta <= vsnap) {
2243 ny = v->top;
2244 vsnap = delta;
2245 }
2246
2247 /* Your bottom border <-> other bottom border */
2248 delta = abs(v->top + v->height - y - w->height);
2249 if (delta <= vsnap) {
2250 ny = v->top + v->height - w->height;
2251 vsnap = delta;
2252 }
2253 }
2254 }
2255 }
2256
2257 EnsureVisibleCaption(w, nx, ny);
2258
2259 w->SetDirty();
2260 return ES_HANDLED;
2262 /* Stop the sizing if the left mouse button was released */
2263 if (!_left_button_down) {
2266 w->SetDirty();
2267 break;
2268 }
2269
2270 /* Compute difference in pixels between cursor position and reference point in the window.
2271 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2272 */
2273 int x, y = _cursor.pos.y - _drag_delta.y;
2275 x = _drag_delta.x - _cursor.pos.x;
2276 } else {
2277 x = _cursor.pos.x - _drag_delta.x;
2278 }
2279
2280 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2281 if (w->resize.step_width == 0) x = 0;
2282 if (w->resize.step_height == 0) y = 0;
2283
2284 /* Check the resize button won't go past the bottom of the screen */
2285 if (w->top + w->height + y > _screen.height) {
2286 y = _screen.height - w->height - w->top;
2287 }
2288
2289 /* X and Y has to go by step.. calculate it.
2290 * The cast to int is necessary else x/y are implicitly cast to
2291 * unsigned int, which won't work. */
2292 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2293 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2294
2295 /* Check that we don't go below the minimum set size */
2296 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2297 x = w->nested_root->smallest_x - w->width;
2298 }
2299 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2300 y = w->nested_root->smallest_y - w->height;
2301 }
2302
2303 /* Window already on size */
2304 if (x == 0 && y == 0) return ES_HANDLED;
2305
2306 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2307 _drag_delta.y += y;
2308 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2309 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2310 w->SetDirty();
2311 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2312 /* ResizeWindow() below ensures marking new position as dirty. */
2313 } else {
2314 _drag_delta.x += x;
2315 }
2316
2317 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2318 ResizeWindow(w, x, y);
2319 return ES_HANDLED;
2320 }
2321 }
2322
2323 _dragging_window = false;
2324 return ES_HANDLED;
2325}
2326
2332{
2335 _dragging_window = true;
2336
2337 _drag_delta.x = w->left - _cursor.pos.x;
2338 _drag_delta.y = w->top - _cursor.pos.y;
2339
2341}
2342
2348static void StartWindowSizing(Window *w, bool to_left)
2349{
2352 _dragging_window = true;
2353
2354 _drag_delta.x = _cursor.pos.x;
2355 _drag_delta.y = _cursor.pos.y;
2356
2358}
2359
2365{
2366 int i;
2368 bool rtl = false;
2369
2370 if (sb->type == NWID_HSCROLLBAR) {
2371 i = _cursor.pos.x - _cursorpos_drag_start.x;
2372 rtl = _current_text_dir == TD_RTL;
2373 } else {
2374 i = _cursor.pos.y - _cursorpos_drag_start.y;
2375 }
2376
2377 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2378 if (_scroller_click_timeout == 1) {
2379 _scroller_click_timeout = 3;
2380 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2382 w->SetDirty();
2383 }
2384 }
2385 return;
2386 }
2387
2388 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2389 int range = sb->GetCount() - sb->GetCapacity();
2390 if (range <= 0) return;
2391
2392 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2393 if (rtl) pos = range - pos;
2394 if (sb->SetPosition(pos)) {
2396 w->SetDirty();
2397 }
2398}
2399
2405{
2406 for (Window *w : Window::Iterate()) {
2407 if (w->mouse_capture_widget >= 0) {
2408 /* Abort if no button is clicked any more. */
2409 if (!_left_button_down) {
2412 return ES_HANDLED;
2413 }
2414
2415 /* Handle scrollbar internally, or dispatch click event */
2417 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2419 } else {
2420 /* If cursor hasn't moved, there is nothing to do. */
2421 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2422
2423 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2424 w->OnClick(pt, w->mouse_capture_widget, 0);
2425 }
2426 return ES_HANDLED;
2427 }
2428 }
2429
2430 return ES_NOT_HANDLED;
2431}
2432
2438{
2439 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2440
2442
2443 /* When we don't have a last scroll window we are starting to scroll.
2444 * When the last scroll window and this are not the same we went
2445 * outside of the window and should not left-mouse scroll anymore. */
2446 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2447
2449 _cursor.fix_at = false;
2450 _scrolling_viewport = false;
2451 _last_scroll_window = nullptr;
2452 return ES_NOT_HANDLED;
2453 }
2454
2455 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2456 /* If the main window is following a vehicle, then first let go of it! */
2457 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
2458 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2459 return ES_NOT_HANDLED;
2460 }
2461
2462 Point delta;
2463 if (scrollwheel_scrolling) {
2464 /* We are using scrollwheels for scrolling */
2465 /* Use the integer part for movement */
2466 delta.x = static_cast<int>(_cursor.h_wheel);
2467 delta.y = static_cast<int>(_cursor.v_wheel);
2468 /* Keep the fractional part so that subtle movement is accumulated */
2469 float temp;
2470 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2471 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2472 } else {
2474 delta.x = -_cursor.delta.x;
2475 delta.y = -_cursor.delta.y;
2476 } else {
2477 delta.x = _cursor.delta.x;
2478 delta.y = _cursor.delta.y;
2479 }
2480 }
2481
2482 /* Create a scroll-event and send it to the window */
2483 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2484
2485 _cursor.delta.x = 0;
2486 _cursor.delta.y = 0;
2487 _cursor.wheel_moved = false;
2488 return ES_HANDLED;
2489}
2490
2502{
2503 bool bring_to_front = false;
2504
2505 if (w->window_class == WC_MAIN_WINDOW ||
2506 IsVitalWindow(w) ||
2507 w->window_class == WC_TOOLTIPS ||
2509 return true;
2510 }
2511
2512 /* Use unshaded window size rather than current size for shaded windows. */
2513 int w_width = w->width;
2514 int w_height = w->height;
2515 if (w->IsShaded()) {
2516 w_width = w->unshaded_size.width;
2517 w_height = w->unshaded_size.height;
2518 }
2519
2521 ++it;
2522 for (; !it.IsEnd(); ++it) {
2523 Window *u = *it;
2524 /* A modal child will prevent the activation of the parent window */
2526 u->SetWhiteBorder();
2527 u->SetDirty();
2528 return false;
2529 }
2530
2531 if (u->window_class == WC_MAIN_WINDOW ||
2532 IsVitalWindow(u) ||
2533 u->window_class == WC_TOOLTIPS ||
2535 continue;
2536 }
2537
2538 /* Window sizes don't interfere, leave z-order alone */
2539 if (w->left + w_width <= u->left ||
2540 u->left + u->width <= w->left ||
2541 w->top + w_height <= u->top ||
2542 u->top + u->height <= w->top) {
2543 continue;
2544 }
2545
2546 bring_to_front = true;
2547 }
2548
2549 if (bring_to_front) BringWindowToFront(w);
2550 return true;
2551}
2552
2561EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2562{
2563 QueryString *query = this->GetQueryString(wid);
2564 if (query == nullptr) return ES_NOT_HANDLED;
2565
2566 int action = QueryString::ACTION_NOTHING;
2567
2568 switch (query->text.HandleKeyPress(key, keycode)) {
2569 case HKPR_EDITING:
2570 this->SetWidgetDirty(wid);
2571 this->OnEditboxChanged(wid);
2572 break;
2573
2574 case HKPR_CURSOR:
2575 this->SetWidgetDirty(wid);
2576 /* For the OSK also invalidate the parent window */
2577 if (this->window_class == WC_OSK) this->InvalidateData();
2578 break;
2579
2580 case HKPR_CONFIRM:
2581 if (this->window_class == WC_OSK) {
2582 this->OnClick(Point(), WID_OSK_OK, 1);
2583 } else if (query->ok_button >= 0) {
2584 this->OnClick(Point(), query->ok_button, 1);
2585 } else {
2586 action = query->ok_button;
2587 }
2588 break;
2589
2590 case HKPR_CANCEL:
2591 if (this->window_class == WC_OSK) {
2592 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2593 } else if (query->cancel_button >= 0) {
2594 this->OnClick(Point(), query->cancel_button, 1);
2595 } else {
2596 action = query->cancel_button;
2597 }
2598 break;
2599
2600 case HKPR_NOT_HANDLED:
2601 return ES_NOT_HANDLED;
2602
2603 default: break;
2604 }
2605
2606 switch (action) {
2608 this->UnfocusFocusedWidget();
2609 break;
2610
2612 if (query->text.GetText().empty()) {
2613 /* If already empty, unfocus instead */
2614 this->UnfocusFocusedWidget();
2615 } else {
2616 query->text.DeleteAll();
2617 this->SetWidgetDirty(wid);
2618 this->OnEditboxChanged(wid);
2619 }
2620 break;
2621
2622 default:
2623 break;
2624 }
2625
2626 return ES_HANDLED;
2627}
2628
2633void HandleToolbarHotkey(int hotkey)
2634{
2635 assert(HasModalProgress() || IsLocalCompany());
2636
2638 if (w != nullptr) {
2639 if (w->window_desc.hotkeys != nullptr) {
2640 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2641 }
2642 }
2643}
2644
2650void HandleKeypress(uint keycode, char32_t key)
2651{
2652 /* World generation is multithreaded and messes with companies.
2653 * But there is no company related window open anyway, so _current_company is not used. */
2654 assert(HasModalProgress() || IsLocalCompany());
2655
2656 /*
2657 * The Unicode standard defines an area called the private use area. Code points in this
2658 * area are reserved for private use and thus not portable between systems. For instance,
2659 * Apple defines code points for the arrow keys in this area, but these are only printable
2660 * on a system running OS X. We don't want these keys to show up in text fields and such,
2661 * and thus we have to clear the unicode character when we encounter such a key.
2662 */
2663 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2664
2665 /*
2666 * If both key and keycode is zero, we don't bother to process the event.
2667 */
2668 if (key == 0 && keycode == 0) return;
2669
2670 /* Check if the focused window has a focused editbox */
2671 if (EditBoxInGlobalFocus()) {
2672 /* All input will in this case go to the focused editbox */
2673 if (_focused_window->window_class == WC_CONSOLE) {
2674 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2675 } else {
2676 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2677 }
2678 }
2679
2680 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2681 for (Window *w : Window::IterateFromFront()) {
2682 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2683 if (w->window_desc.hotkeys != nullptr) {
2684 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2685 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2686 }
2687 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2688 }
2689
2691 /* When there is no toolbar w is null, check for that */
2692 if (w != nullptr) {
2693 if (w->window_desc.hotkeys != nullptr) {
2694 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2695 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2696 }
2697 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2698 }
2699
2700 HandleGlobalHotkeys(key, keycode);
2701}
2702
2707{
2708 /* Call the event, start with the uppermost window. */
2709 for (Window *w : Window::IterateFromFront()) {
2710 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2711 }
2712}
2713
2719/* 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)
2720{
2721 QueryString *query = this->GetQueryString(wid);
2722 if (query == nullptr) return;
2723
2724 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2725 this->SetWidgetDirty(wid);
2726 this->OnEditboxChanged(wid);
2727 }
2728}
2729
2736void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2737{
2738 if (!EditBoxInGlobalFocus()) return;
2739
2740 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2741}
2742
2747static void HandleAutoscroll()
2748{
2749 if (_game_mode == GM_MENU || HasModalProgress()) return;
2751 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2752
2753 int x = _cursor.pos.x;
2754 int y = _cursor.pos.y;
2755 Window *w = FindWindowFromPt(x, y);
2756 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2758
2759 Viewport *vp = IsPtInWindowViewport(w, x, y);
2760 if (vp == nullptr) return;
2761
2762 x -= vp->left;
2763 y -= vp->top;
2764
2765 /* here allows scrolling in both x and y axis */
2766 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2767 static const int SCROLLSPEED = 3;
2768 if (x - 15 < 0) {
2769 w->viewport->CancelFollow(*w);
2770 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2771 } else if (15 - (vp->width - x) > 0) {
2772 w->viewport->CancelFollow(*w);
2773 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2774 }
2775 if (y - 15 < 0) {
2776 w->viewport->CancelFollow(*w);
2777 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2778 } else if (15 - (vp->height - y) > 0) {
2779 w->viewport->CancelFollow(*w);
2780 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2781 }
2782}
2783
2784enum MouseClick : uint8_t {
2785 MC_NONE = 0,
2786 MC_LEFT,
2787 MC_RIGHT,
2788 MC_DOUBLE_LEFT,
2789 MC_HOVER,
2790};
2791
2792static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2793static constexpr int MAX_OFFSET_HOVER = 5;
2794
2796
2797const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2798
2799static void ScrollMainViewport(int x, int y)
2800{
2801 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2802 Window *w = GetMainWindow();
2803 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2804 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2805 }
2806}
2807
2817static const int8_t scrollamt[16][2] = {
2818 { 0, 0},
2819 {-2, 0},
2820 { 0, -2},
2821 {-2, -1},
2822 { 2, 0},
2823 { 0, 0},
2824 { 2, -1},
2825 { 0, -2},
2826 { 0, 2},
2827 {-2, 1},
2828 { 0, 0},
2829 {-2, 0},
2830 { 2, 1},
2831 { 0, 2},
2832 { 2, 0},
2833 { 0, 0},
2834};
2835
2836static void HandleKeyScrolling()
2837{
2838 /*
2839 * Check that any of the dirkeys is pressed and that the focused window
2840 * doesn't have an edit-box as focused widget.
2841 */
2842 if (_dirkeys && !EditBoxInGlobalFocus()) {
2843 int factor = _shift_pressed ? 50 : 10;
2844
2845 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2846 /* Key scrolling stops following a vehicle. */
2847 Window *main_window = GetMainWindow();
2848 main_window->viewport->CancelFollow(*main_window);
2849 }
2850
2851 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2852 }
2853}
2854
2855static void MouseLoop(MouseClick click, int mousewheel)
2856{
2857 /* World generation is multithreaded and messes with companies.
2858 * But there is no company related window open anyway, so _current_company is not used. */
2859 assert(HasModalProgress() || IsLocalCompany());
2860
2861 HandlePlacePresize();
2863
2864 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2865 if (HandleMouseDragDrop() == ES_HANDLED) return;
2866 if (HandleWindowDragging() == ES_HANDLED) return;
2867 if (HandleActiveWidget() == ES_HANDLED) return;
2868 if (HandleViewportScroll() == ES_HANDLED) return;
2869
2871
2872 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved;
2873 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2874
2875 int x = _cursor.pos.x;
2876 int y = _cursor.pos.y;
2877 Window *w = FindWindowFromPt(x, y);
2878 if (w == nullptr) return;
2879
2880 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2881 Viewport *vp = IsPtInWindowViewport(w, x, y);
2882
2883 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2884 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2885
2886 if (mousewheel != 0) {
2887 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2888 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) {
2889 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2890 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2891 }
2892 }
2893
2894 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2895 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2896 }
2897
2898 if (vp != nullptr) {
2899 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2900 _scrolling_viewport = true;
2901 _cursor.fix_at = true;
2902 return;
2903 }
2904
2905 switch (click) {
2906 case MC_DOUBLE_LEFT:
2907 case MC_LEFT:
2908 if (HandleViewportClicked(*vp, x, y)) return;
2911 _scrolling_viewport = true;
2912 _cursor.fix_at = false;
2913 return;
2914 }
2915 break;
2916
2917 case MC_RIGHT:
2920 _scrolling_viewport = true;
2923 DispatchRightClickEvent(w, x - w->left, y - w->top);
2924 return;
2925 }
2926 break;
2927
2928 default:
2929 break;
2930 }
2931 }
2932
2933 switch (click) {
2934 case MC_LEFT:
2935 case MC_DOUBLE_LEFT:
2936 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2937 return;
2938
2939 default:
2940 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2941 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2942 * Simulate a right button click so we can get started. */
2943 [[fallthrough]];
2944
2945 case MC_RIGHT:
2946 DispatchRightClickEvent(w, x - w->left, y - w->top);
2947 return;
2948
2949 case MC_HOVER:
2950 DispatchHoverEvent(w, x - w->left, y - w->top);
2951 break;
2952 }
2953
2954 /* We're not doing anything with 2D scrolling, so reset the value. */
2955 _cursor.h_wheel = 0.0f;
2956 _cursor.v_wheel = 0.0f;
2957 _cursor.wheel_moved = false;
2958}
2959
2964{
2965 /* World generation is multithreaded and messes with companies.
2966 * But there is no company related window open anyway, so _current_company is not used. */
2967 assert(HasModalProgress() || IsLocalCompany());
2968
2969 static std::chrono::steady_clock::time_point double_click_time = {};
2970 static Point double_click_pos = {0, 0};
2971
2972 /* Mouse event? */
2973 MouseClick click = MC_NONE;
2975 click = MC_LEFT;
2976 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2977 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2978 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2979 click = MC_DOUBLE_LEFT;
2980 }
2981 double_click_time = std::chrono::steady_clock::now();
2982 double_click_pos = _cursor.pos;
2983 _left_button_clicked = true;
2984 } else if (_right_button_clicked) {
2985 _right_button_clicked = false;
2986 click = MC_RIGHT;
2987 }
2988
2989 int mousewheel = 0;
2990 if (_cursor.wheel) {
2991 mousewheel = _cursor.wheel;
2992 _cursor.wheel = 0;
2993 }
2994
2995 static std::chrono::steady_clock::time_point hover_time = {};
2996 static Point hover_pos = {0, 0};
2997
2999 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
3000 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
3001 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
3002 hover_pos = _cursor.pos;
3003 hover_time = std::chrono::steady_clock::now();
3004 _mouse_hovering = false;
3005 } else if (!_mouse_hovering) {
3006 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
3007 click = MC_HOVER;
3008 _mouse_hovering = true;
3009 hover_time = std::chrono::steady_clock::now();
3010 }
3011 }
3012 }
3013
3014 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
3015 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
3017 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
3019 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3021 } else {
3022 MouseLoop(click, mousewheel);
3023 }
3024
3025 /* We have moved the mouse the required distance,
3026 * no need to move it at any later time. */
3027 _cursor.delta.x = 0;
3028 _cursor.delta.y = 0;
3029}
3030
3034static void CheckSoftLimit()
3035{
3036 if (_settings_client.gui.window_soft_limit == 0) return;
3037
3038 for (;;) {
3039 uint deletable_count = 0;
3040 Window *last_deletable = nullptr;
3041 for (Window *w : Window::IterateFromFront()) {
3042 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3043
3044 last_deletable = w;
3045 deletable_count++;
3046 }
3047
3048 /* We've not reached the soft limit yet. */
3049 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3050
3051 assert(last_deletable != nullptr);
3052 last_deletable->Close();
3053 }
3054}
3055
3060{
3061 /* World generation is multithreaded and messes with companies.
3062 * But there is no company related window open anyway, so _current_company is not used. */
3063 assert(HasModalProgress() || IsLocalCompany());
3064
3066
3067 /* Process scheduled window deletion. */
3069
3070 /* HandleMouseEvents was already called for this tick */
3072}
3073
3074static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3075
3076bool CanContinueRealtimeTick()
3077{
3078 auto now = std::chrono::steady_clock::now();
3079 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3080}
3081
3086{
3087 _realtime_tick_start = std::chrono::steady_clock::now();
3088 for (Window *w : Window::Iterate()) {
3089 w->OnRealtimeTick(delta_ms);
3090 }
3091}
3092
3094static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3095 extern int _caret_timer;
3096 _caret_timer += 3;
3097 CursorTick();
3098
3099 HandleKeyScrolling();
3101 DecreaseWindowCounters();
3102});
3103
3107});
3108
3110static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3111 if (_network_dedicated) return;
3112
3113 for (Window *w : Window::Iterate()) {
3116 w->SetDirty();
3117 }
3118 }
3119});
3120
3125{
3126 static auto last_time = std::chrono::steady_clock::now();
3127 auto now = std::chrono::steady_clock::now();
3128 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3129
3130 if (delta_ms.count() == 0) return;
3131
3132 last_time = now;
3133
3136
3138
3140 CallWindowRealtimeTickEvent(delta_ms.count());
3141
3142 /* Process invalidations before anything else. */
3143 for (Window *w : Window::Iterate()) {
3147 }
3148
3149 /* Skip the actual drawing on dedicated servers without screen.
3150 * But still empty the invalidation queues above. */
3151 if (_network_dedicated) return;
3152
3154
3155 for (Window *w : Window::Iterate()) {
3156 /* Update viewport only if window is not shaded. */
3157 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3158 }
3160 /* Redraw mouse cursor in case it was hidden */
3161 DrawMouseCursor();
3162
3163 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3164 /* We are done with the last draw-frame, so we know what sprites we
3165 * clicked on. Reset the picker mode and invalidate the window. */
3168 }
3169}
3170
3177{
3178 for (const Window *w : Window::Iterate()) {
3179 if (w->window_class == cls && w->window_number == number) w->SetDirty();
3180 }
3181}
3182
3190{
3191 for (const Window *w : Window::Iterate()) {
3192 if (w->window_class == cls && w->window_number == number) {
3193 w->SetWidgetDirty(widget_index);
3194 }
3195 }
3196}
3197
3203{
3204 for (const Window *w : Window::Iterate()) {
3205 if (w->window_class == cls) w->SetDirty();
3206 }
3207}
3208
3213{
3214 this->scheduled_resize = true;
3215}
3216
3221{
3222 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3223 while (this->scheduled_resize) {
3224 this->scheduled_resize = false;
3225 this->OnResize();
3226 }
3227}
3228
3234void Window::InvalidateData(int data, bool gui_scope)
3235{
3236 this->SetDirty();
3237 if (!gui_scope) {
3238 /* Schedule GUI-scope invalidation for next redraw. */
3239 this->scheduled_invalidation_data.push_back(data);
3240 }
3241 this->OnInvalidateData(data, gui_scope);
3242}
3243
3248{
3249 for (int data : this->scheduled_invalidation_data) {
3250 if (this->window_class == WC_INVALID) break;
3251 this->OnInvalidateData(data, true);
3252 }
3253 this->scheduled_invalidation_data.clear();
3254}
3255
3260{
3261 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3262
3263 for (const auto &pair : this->widget_lookup) {
3264 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3265 }
3266}
3267
3294void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3295{
3296 for (Window *w : Window::Iterate()) {
3297 if (w->window_class == cls && w->window_number == number) {
3298 w->InvalidateData(data, gui_scope);
3299 }
3300 }
3301}
3302
3311void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3312{
3313 for (Window *w : Window::Iterate()) {
3314 if (w->window_class == cls) {
3315 w->InvalidateData(data, gui_scope);
3316 }
3317 }
3318}
3319
3324{
3325 for (Window *w : Window::Iterate()) {
3326 w->OnGameTick();
3327 }
3328}
3329
3337{
3338 /* Note: the container remains stable, even when deleting windows. */
3339 for (Window *w : Window::Iterate()) {
3341 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3342
3343 w->Close();
3344 }
3345 }
3346}
3347
3356{
3357 /* Note: the container remains stable, even when closing windows. */
3358 for (Window *w : Window::Iterate()) {
3360 w->Close();
3361 }
3362 }
3363}
3364
3369{
3371 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3372 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3373 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3374}
3375
3381{
3382 /* Note: the container remains stable, even when deleting windows. */
3383 for (Window *w : Window::Iterate()) {
3385 w->Close();
3386 }
3387 }
3388
3389 for (const Window *w : Window::Iterate()) w->SetDirty();
3390}
3391
3398
3399void ReInitWindow(Window *w, bool zoom_changed)
3400{
3401 if (w == nullptr) return;
3402 if (zoom_changed) {
3403 w->nested_root->AdjustPaddingForZoom();
3405 }
3406 w->ReInit();
3407}
3408
3410void ReInitAllWindows(bool zoom_changed)
3411{
3413 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3414 NWidgetScrollbar::InvalidateDimensionCache();
3415
3417
3418 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3419 * so EnsureVisibleCaption uses the updated size information. */
3420 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3421 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3422 for (Window *w : Window::Iterate()) {
3423 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3424 ReInitWindow(w, zoom_changed);
3425 }
3426
3429
3430 /* Make sure essential parts of all windows are visible */
3431 RelocateAllWindows(_screen.width, _screen.height);
3433}
3434
3442static int PositionWindow(Window *w, WindowClass clss, int setting)
3443{
3444 if (w == nullptr || w->window_class != clss) {
3445 w = FindWindowById(clss, 0);
3446 }
3447 if (w == nullptr) return 0;
3448
3449 int old_left = w->left;
3450 switch (setting) {
3451 case 1: w->left = (_screen.width - w->width) / 2; break;
3452 case 2: w->left = _screen.width - w->width; break;
3453 default: w->left = 0; break;
3454 }
3455 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3456 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3457 return w->left;
3458}
3459
3466{
3467 Debug(misc, 5, "Repositioning Main Toolbar...");
3469}
3470
3477{
3478 Debug(misc, 5, "Repositioning statusbar...");
3480}
3481
3488{
3489 Debug(misc, 5, "Repositioning news message...");
3491}
3492
3499{
3500 Debug(misc, 5, "Repositioning network chat window...");
3502}
3503
3504
3511{
3512 for (const Window *w : Window::Iterate()) {
3513 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3514 w->viewport->follow_vehicle = to_index;
3515 w->SetDirty();
3516 }
3517 }
3518}
3519
3520
3526void RelocateAllWindows(int neww, int newh)
3527{
3529
3530 /* Reposition toolbar then status bar before other all windows. */
3531 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3532 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3533 wt->left = PositionMainToolbar(wt);
3534 }
3535
3536 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3537 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3538 ws->top = newh - ws->height;
3539 ws->left = PositionStatusbar(ws);
3540 }
3541
3542 for (Window *w : Window::Iterate()) {
3543 int left, top;
3544 /* XXX - this probably needs something more sane. For example specifying
3545 * in a 'backup'-desc that the window should always be centered. */
3546 switch (w->window_class) {
3547 case WC_MAIN_WINDOW:
3548 case WC_BOOTSTRAP:
3549 case WC_HIGHSCORE:
3550 case WC_ENDSCREEN:
3551 ResizeWindow(w, neww, newh);
3552 continue;
3553
3554 case WC_MAIN_TOOLBAR:
3555 case WC_STATUS_BAR:
3556 continue;
3557
3558 case WC_NEWS_WINDOW:
3559 top = newh - w->height;
3560 left = PositionNewsMessage(w);
3561 break;
3562
3564 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3565
3566 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3567 left = PositionNetworkChatWindow(w);
3568 break;
3569
3570 case WC_CONSOLE:
3571 IConsoleResize(w);
3572 continue;
3573
3574 default: {
3575 if (w->flags.Test(WindowFlag::Centred)) {
3576 top = (newh - w->height) >> 1;
3577 left = (neww - w->width) >> 1;
3578 break;
3579 }
3580
3581 left = w->left;
3582 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3583 if (left < 0) left = 0;
3584
3585 top = w->top;
3586 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3587 break;
3588 }
3589 }
3590
3591 EnsureVisibleCaption(w, left, top);
3592 }
3593}
3594
3599void PickerWindowBase::Close([[maybe_unused]] int data)
3600{
3602 this->Window::Close();
3603}
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:136
How all blitters should look like.
Definition base.hpp:29
virtual void * MoveTo(void *video, int x, int y)=0
Move the destination pointer the requested amount x and y, keeping in mind any pitch and bpp of the r...
Enum-as-bit-set wrapper.
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Baseclass for nested widgets.
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:931
WidgetType type
Type of the widget / nested widget.
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
uint resize_y
Vertical resize step (0 means not resizable).
Base class for a 'real' widget.
bool IsDisabled() const
Return whether the widget is disabled.
NWidgetDisplayFlags disp_flags
Flags that affect display and interaction with the widget.
WidgetID GetScrollbarIndex() const
Get the WidgetID of this nested widget's scrollbar.
Definition widget.cpp:1260
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1233
void SetLowered(bool lowered)
Lower or raise the widget.
bool IsLowered() const
Return whether the widget is lowered.
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2642
static Dimension resizebox_dimension
Cached size of a resizebox widget.
static Dimension closebox_dimension
Cached size of a closebox widget.
Nested widget to display and control a scrollbar in a window.
int shown_plane
Plane being displayed (for NWID_SELECTION only).
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1422
static void Reset(PerformanceElement elem)
Store the previous accumulator value and reset for a new cycle of accumulating measurements.
RAII class for measuring simple elements of performance.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:3599
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:3393
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:966
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:1514
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1450
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1547
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:416
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
constexpr uint Ceil(uint a, uint b)
Computes ceil(a / b) * b for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
void GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:693
bool _networking
are we in networking mode?
Definition network.cpp: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?
bool link_terraform_toolbar
display terraform toolbar when displaying rail, road, water and airport toolbars
List of hotkeys for a window.
Definition hotkeys.h:37
int CheckMatch(uint16_t keycode, bool global_only=false) const
Check if a keycode is bound to something.
Definition hotkeys.cpp:301
Ini file that supports both loading and saving.
Definition ini_type.h:86
bool SaveToDisk(const std::string &filename)
Save the Ini file's data to the disk.
Definition ini.cpp:42
void LoadFromDisk(std::string_view filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition ini_load.cpp:185
NewGrfDebugSpritePickerMode mode
Current state.
void * clicked_pixel
Clicked pixel (pointer to blitter buffer)
FlatSet< SpriteID > sprites
Sprites found.
static Titem * Get(auto index)
Returns Titem with given index.
Data stored about a string that can be modified in the GUI.
int ok_button
Widget button of parent window to simulate when pressing OK in OSK.
static const int ACTION_DESELECT
Deselect editbox.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
ptrdiff_t GetCharAtPosition(const Window *w, WidgetID wid, const Point &pt) const
Get the character that is rendered at a position.
Definition misc_gui.cpp:851
static const int ACTION_NOTHING
Nothing.
static const int ACTION_CLEAR
Clear editbox.
Point GetCaretPosition(const Window *w, WidgetID wid) const
Get the current caret position.
Definition misc_gui.cpp:793
Rect GetBoundingRect(const Window *w, WidgetID wid, size_t from, size_t to) const
Get the bounding rectangle for a range of the query string.
Definition misc_gui.cpp:821
Specification of a rectangle with absolute coordinates of all edges.
int Height() const
Get height of Rect.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:213
uint step_width
Step-size of width resize changes.
Definition window_gui.h:212
Helper/buffer for input fields.
void UpdateSize()
Update Textbuf type with its actual physical character and screenlength Get the count of characters i...
Definition textbuf.cpp:445
void DeleteAll()
Delete every character in the textbuffer.
Definition textbuf.cpp:112
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
bool InsertString(std::string_view str, bool marked, std::optional< size_t > caret=std::nullopt, std::optional< size_t > insert_location=std::nullopt, std::optional< size_t > replacement_end=std::nullopt)
Insert a string into the text buffer.
Definition textbuf.cpp:157
Window * GetCallbackWnd()
Get the window that started the current highlighting.
WindowClass window_class
The WindowClass of the window that is responsible for the selection mode.
void Reset()
Reset tile highlighting.
Point selend
The location where the drag currently ends.
WindowNumber window_number
The WindowNumber of the window that is responsible for the selection mode.
Vehicle data structure.
int32_t z_pos
z coordinate.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
High level window description.
Definition window_gui.h:168
int16_t GetDefaultWidth() const
Determine default width of window.
Definition window.cpp:137
static void SaveToConfig()
Save all WindowDesc settings to _windows_file.
Definition window.cpp:176
int16_t pref_width
User-preferred width of the window. Zero if unset.
Definition window_gui.h:187
const WindowPosition default_pos
Preferred position of the window.
Definition window_gui.h:178
bool pref_sticky
Preferred stickyness.
Definition window_gui.h:186
int16_t pref_height
User-preferred height of the window. Zero if unset.
Definition window_gui.h:188
int16_t GetDefaultHeight() const
Determine default height of window.
Definition window.cpp:147
const int16_t default_height_trad
Preferred initial height of the window (pixels at 1x zoom).
Definition window_gui.h:198
const int16_t default_width_trad
Preferred initial width of the window (pixels at 1x zoom).
Definition window_gui.h:197
const WindowClass cls
Class of the window,.
Definition window_gui.h:179
const std::string_view ini_key
Key to store window defaults in openttd.cfg. An empty string if nothing shall be stored.
Definition window_gui.h:181
const WindowDefaultFlags flags
Flags.
Definition window_gui.h:182
static void LoadFromConfig()
Load all WindowDesc settings from _windows_file.
Definition window.cpp:155
const WindowClass parent_cls
Class of the parent window.
Definition window_gui.h:180
const HotkeyList * hotkeys
Hotkeys for the window.
Definition window_gui.h:184
WindowDesc(WindowPosition default_pos, std::string_view ini_key, int16_t def_width_trad, int16_t def_height_trad, WindowClass window_class, WindowClass parent_class, WindowDefaultFlags flags, const std::span< const NWidgetPart > nwid_parts, HotkeyList *hotkeys=nullptr, const std::source_location location=std::source_location::current())
Window description constructor.
Definition window.cpp:108
const std::span< const NWidgetPart > nwid_parts
Span of nested widget parts describing the window.
Definition window_gui.h:183
Number to differentiate different windows of the same class.
Iterator to iterate all valid Windows.
Definition window_gui.h:875
Data structure for an opened window.
Definition window_gui.h:274
virtual const struct Textbuf * GetFocusedTextbuf() const
Get the current input text buffer.
Definition window.cpp: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:978
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1102
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:793
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1807
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
virtual void ApplyDefaults()
Read default values from WindowDesc configuration an apply them to the window.
Definition window.cpp:193
uint8_t white_border_timer
Timer value of the WindowFlag::WhiteBorder for flags.
Definition window_gui.h:308
NWidgetStacked * shade_select
Selection widget (NWID_SELECTION) to use for shading the window. If nullptr, window cannot shade.
Definition window_gui.h:324
void InitializePositionSize(int x, int y, int min_width, int min_height)
Set the position and smallest size of the window.
Definition window.cpp:1455
Dimension unshaded_size
Last known unshaded size (only valid while shaded).
Definition window_gui.h:325
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3234
virtual EventState OnKeyPress(char32_t key, uint16_t keycode)
A key has been pressed.
Definition window_gui.h:651
Window * parent
Parent window.
Definition window_gui.h:329
AllWindows< false > IterateFromBack
Iterate all windows in Z order from back to front.
Definition window_gui.h:934
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition window.cpp:1139
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:470
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:556
uint8_t timeout_timer
Timer value of the WindowFlag::Timeout for flags.
Definition window_gui.h:307
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:319
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:504
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). INVALID_WIDGET if no widget has mouse ca...
Definition window_gui.h:327
virtual void OnGameTick()
Called once per (game) tick.
Definition window_gui.h:746
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition window_gui.h:868
virtual bool OnRightClick(Point pt, WidgetID widget)
A click with the right mouse button has been made on the window.
Definition window_gui.h:678
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:720
virtual void OnDropdownSelect(WidgetID widget, int index, int click_result)
A dropdown option associated to this window has been selected.
Definition window_gui.h:770
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition window.cpp:3247
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1075
ResizeInfo resize
Resize information.
Definition window_gui.h:315
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:741
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition window.cpp:1021
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:305
void ScheduleResize()
Mark this window as resized and in need of OnResize() event.
Definition window.cpp:3212
virtual void OnPaint()
The window must be repainted.
Definition window_gui.h:596
virtual void OnDragDrop(Point pt, WidgetID widget)
A dragged 'object' has been released.
Definition window_gui.h:707
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1797
WindowDesc & window_desc
Window description.
Definition window_gui.h:300
WindowClass window_class
Window class.
Definition window_gui.h:302
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
Definition window_gui.h:751
virtual void OnMouseWheel(int wheel, WidgetID widget)
The mouse wheel has been turned.
Definition window_gui.h:735
AllWindows< true > IterateFromFront
Iterate all windows in Z order from front to back.
Definition window_gui.h:935
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1090
void SetWhiteBorder()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:365
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:485
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:521
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:492
static std::vector< Window * > closed_windows
List of closed windows to delete.
Definition window_gui.h:276
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition window.cpp: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:1785
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:317
virtual Point GetCaretPosition() const
Get the current caret position if an edit box has the focus.
Definition window.cpp:377
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1474
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:2719
int left
x position of left edge of the window
Definition window_gui.h:310
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:560
Window * FindChildWindow(WindowClass wc=WC_INVALID) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1047
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:356
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:320
virtual void OnEditboxChanged(WidgetID widget)
The text in an editbox has been edited.
Definition window_gui.h:778
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition window.cpp:353
virtual void OnScroll(Point delta)
Handle the request for (viewport) scrolling.
Definition window_gui.h:713
int top
y position of top edge of the window
Definition window_gui.h:311
virtual void OnClick(Point pt, WidgetID widget, int click_count)
A click with the left mouse button has been made on the window.
Definition window_gui.h:669
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:333
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:323
virtual ptrdiff_t GetTextCharacterAtPosition(const Point &pt) const
Get the character that is rendered at a position by the focused edit box.
Definition window.cpp:408
std::vector< int > scheduled_invalidation_data
Data of scheduled OnInvalidateData() calls.
Definition window_gui.h:283
void InitializeData(WindowNumber window_number)
Initializes the data (except the position and initial size) of a new Window.
Definition window.cpp:1417
virtual void OnMouseOver(Point pt, WidgetID widget)
The mouse is currently moving over the window or has just moved outside of the window.
Definition window_gui.h:728
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1830
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:212
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:983
virtual bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition window_gui.h:693
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:461
virtual EventState OnCTRLStateChange()
The state of the control key has changed.
Definition window_gui.h:660
void ProcessScheduledResize()
Process scheduled OnResize() event.
Definition window.cpp:3220
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2561
virtual void OnMouseDrag(Point pt, WidgetID widget)
An 'object' is being dragged at the provided position, highlight the target if possible.
Definition window_gui.h:700
void HandleButtonClick(WidgetID widget)
Do all things to make a button look clicked and mark it to be unclicked in a few ticks.
Definition window.cpp:595
virtual void OnResize()
Called after the window got resized.
Definition window_gui.h:763
Window * FindChildWindowById(WindowClass wc, WindowNumber number) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1062
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:1820
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h:756
WindowFlags flags
Window flags.
Definition window_gui.h:301
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:313
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3259
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:322
bool scheduled_resize
Set if window has been resized.
Definition window_gui.h:284
virtual Rect GetTextBoundingRect(size_t from, size_t to) const
Get the bounding rectangle for a text range if an edit box has the focus.
Definition window.cpp: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:852
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:933
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:313
virtual void OnHover(Point pt, WidgetID widget)
The mouse is hovering over a widget in the window, perform an action for it.
Definition window_gui.h:685
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
virtual void OnInit()
Notification that the nested widget tree gets initialized.
Definition window_gui.h:579
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
std::vector< WindowDesc * > * _window_descs
List of WindowDescs.
Definition window.cpp:102
@ HKPR_NOT_HANDLED
Key does not affect editboxes.
@ HKPR_CANCEL
Escape key pressed.
@ HKPR_EDITING
Textbuf content changed.
@ HKPR_CONFIRM
Return or enter key pressed.
@ HKPR_CURSOR
Non-text change, e.g. cursor position.
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
void UpdateTileSelection()
Updates tile highlighting for all cases.
Definition of Interval and OneShot timers.
Definition of the Window system.
static constexpr std::chrono::milliseconds TIMER_BLINK_INTERVAL
Interval used by blinking interface elements.
uint _toolbar_width
Width of the toolbar, shared by statusbar.
Stuff related to the (main) toolbar.
Base class for all vehicles.
Base of all video drivers.
Viewport * IsPtInWindowViewport(const Window *w, int x, int y)
Is a xy position inside the viewport of the window?
Definition viewport.cpp:408
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
Update the viewport position being displayed.
Functions related to (drawing on) viewports.
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:80
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
Returns the index for the widget located at the given position relative to the window.
Definition widget.cpp:274
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:250
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h: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:3476
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:2019
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2153
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:3380
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:1193
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:1726
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1180
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:855
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1221
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2706
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:883
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:3059
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3124
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3465
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3336
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:945
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2096
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:1576
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3498
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2348
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:1615
PreventHideDirection
Direction for moving the window.
Definition window.cpp:2004
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:2006
@ PHD_UP
Above v is a safe position.
Definition window.cpp:2005
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2747
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:2633
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3442
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:1540
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2792
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1166
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3510
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:2437
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:818
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:616
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1842
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3368
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:2136
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:1397
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3410
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:2059
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1205
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2963
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:1683
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2159
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3487
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2147
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:790
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:1241
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2650
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3202
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:2793
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:1892
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1313
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:3294
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3393
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:1276
Point AlignInitialConstructionToolbar(int window_width)
Compute the position of the construction toolbars.
Definition window.cpp:1699
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3526
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:2364
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:2331
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3323
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2404
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:761
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:2817
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2501
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:3085
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:1957
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3355
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1981
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:3034
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1151
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1878
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1856
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:3189
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:2736
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3176
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:3311
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.
static const int TIMEOUT_DURATION
The initial timeout value for WindowFlag::Timeout.
Definition window_gui.h:241
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
Returns the index for the widget located at the given position relative to the window.
Definition widget.cpp:274
SpecialMouseMode
Mouse modes.
@ WSM_DRAGDROP
Drag&drop an object.
@ WSM_PRESIZE
Presizing mode (docks, tunnels).
WindowPosition
How do we the window to be placed?
Definition window_gui.h:142
@ WDP_CENTER
Center the window.
Definition window_gui.h:145
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
@ WDP_ALIGN_TOOLBAR
Align toward the toolbar.
Definition window_gui.h:146
@ WDP_MANUAL
Manually align the window (so no automatic location finding)
Definition window_gui.h:143
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:60
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Definition window_type.h:23
WindowClass
Window classes.
Definition window_type.h:49
@ WC_INVALID
Invalid window.
@ WC_OSK
On Screen Keyboard; Window numbers:
@ WC_CONSOLE
Console; Window numbers:
@ WC_NEWS_WINDOW
News window; Window numbers:
@ WC_HIGHSCORE
Highscore; Window numbers:
@ WC_BUY_COMPANY
Buyout company (merger); Window numbers:
@ WC_SPRITE_ALIGNER
Sprite aligner (debug); Window numbers:
@ WC_COMPANY_INFRASTRUCTURE
Company infrastructure overview; Window numbers:
@ WC_ENDSCREEN
Endscreen; Window numbers:
@ WC_COMPANY_COLOUR
Company colour selection; Window numbers:
@ WC_STATION_LIST
Station list; Window numbers:
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition window_type.h:69
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_SEND_NETWORK_MSG
Chatbox; Window numbers:
@ WC_ERRMSG
Error message; Window numbers:
@ WC_BUILD_TOOLBAR
Build toolbar; Window numbers:
Definition window_type.h:78
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_SCRIPT_SETTINGS
Script settings; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
@ WC_SCEN_LAND_GEN
Landscape generation (in Scenario Editor); Window numbers:
@ WC_SCRIPT_LIST
Scripts list; Window numbers:
@ WC_SAVE_PRESET
Save preset; Window numbers:
@ WC_SHIPS_LIST
Ships list; Window numbers:
@ WC_MESSAGE_HISTORY
News history list; Window numbers:
@ WC_CONFIRM_POPUP_QUERY
Popup with confirm question; Window numbers:
@ WC_GENERATE_LANDSCAPE
Generate landscape (newgame); Window numbers:
@ WC_CUSTOM_CURRENCY
Custom currency; Window numbers:
@ WC_MAIN_WINDOW
Main window; Window numbers:
Definition window_type.h:56
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers:
@ WC_NETWORK_WINDOW
Network window; Window numbers:
@ WC_FINANCES
Finances of a company; Window numbers:
@ WC_TEXTFILE
textfile; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_QUERY_STRING
Query string window; Window numbers:
@ WC_SMALLMAP
Small map; Window numbers:
@ WC_BOOTSTRAP
Bootstrap; Window numbers:
@ WC_SAVELOAD
Saveload window; Window numbers:
@ WC_GRF_PARAMETERS
NewGRF parameters; Window numbers:
@ WC_MODAL_PROGRESS
Progress report of landscape generation; Window numbers:
@ WC_MAIN_TOOLBAR
Main toolbar (the long bar at the top); Window numbers:
Definition window_type.h:63
@ WC_TOOLTIPS
Tooltip window; Window numbers:
@ WC_COMPANY
Company view; Window numbers:
@ WC_NETWORK_STATUS_WINDOW
Network status window; Window numbers:
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::Min) When shifting right,...
Definition zoom_func.h:22
@ Min
Minimum zoom level.