OpenTTD Source 20260206-master-g4d4e37dbf1
window.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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),
117 flags(flags),
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) {
196 if (this->window_desc.pref_sticky) this->flags.Set(WindowFlag::Sticky);
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
233 this->flags.Reset(WindowFlag::Highlighted);
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
287void Window::OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
288{
289 if (widget < 0) return;
290
291 /* Many dropdown selections depend on the position of the main toolbar,
292 * so if it doesn't exist (e.g. the end screen has appeared), just skip the instant close behaviour. */
293 if (instant_close && FindWindowById(WC_MAIN_TOOLBAR, 0) != nullptr) {
294 /* Send event for selected option if we're still
295 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
296 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
297 this->OnDropdownSelect(widget, index, click_result);
298 }
299 }
300
301 /* Raise the dropdown button */
302 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
303 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
305 } else {
306 this->RaiseWidget(widget);
307 }
308 this->SetWidgetDirty(widget);
309}
310
317{
318 return this->GetWidget<NWidgetScrollbar>(widnum);
319}
320
327{
328 return this->GetWidget<NWidgetScrollbar>(widnum);
329}
330
337{
338 auto query = this->querystrings.find(widnum);
339 return query != this->querystrings.end() ? query->second : nullptr;
340}
341
348{
349 auto query = this->querystrings.find(widnum);
350 return query != this->querystrings.end() ? query->second : nullptr;
351}
352
357{
358 for (auto &qs : this->querystrings) {
359 qs.second->text.UpdateSize();
360 }
361}
362
367/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
368{
369 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
370 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
371 }
372
373 return nullptr;
374}
375
380/* virtual */ Point Window::GetCaretPosition() const
381{
382 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
383 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
384 }
385
386 Point pt = {0, 0};
387 return pt;
388}
389
396/* virtual */ Rect Window::GetTextBoundingRect(size_t from, size_t to) const
397{
398 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
399 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
400 }
401
402 Rect r = {0, 0, 0, 0};
403 return r;
404}
405
411/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
412{
413 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
414 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
415 }
416
417 return -1;
418}
419
425{
426 if (_focused_window == w) return;
427
428 /* Don't focus a tooltip */
429 if (w != nullptr && w->window_class == WC_TOOLTIPS) return;
430
431 /* Invalidate focused widget */
432 if (_focused_window != nullptr) {
433 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
434 }
435
436 /* Remember which window was previously focused */
437 Window *old_focused = _focused_window;
438 _focused_window = w;
439
440 /* So we can inform it that it lost focus */
441 if (old_focused != nullptr) old_focused->OnFocusLost(false);
442 if (_focused_window != nullptr) _focused_window->OnFocus();
443}
444
451{
452 if (_focused_window == nullptr) return false;
453
454 /* The console does not have an edit box so a special case is needed. */
455 if (_focused_window->window_class == WC_CONSOLE) return true;
456
457 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
458}
459
465{
466 return _focused_window && _focused_window->window_class == WC_CONSOLE;
467}
468
473{
474 if (this->nested_focus != nullptr) {
476
477 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
478 this->nested_focus->SetDirty(this);
479 this->nested_focus = nullptr;
480 }
481}
482
489{
490 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
491 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
492
493 if (this->nested_focus != nullptr) {
494 /* Do nothing if widget_index is already focused. */
495 if (widget == this->nested_focus) return false;
496
497 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
498 this->nested_focus->SetDirty(this);
500 }
501
502 this->nested_focus = widget;
504 return true;
505}
506
507std::string Window::GetWidgetString([[maybe_unused]] WidgetID widget, StringID stringid) const
508{
509 if (stringid == STR_NULL) return {};
510 return GetString(stringid);
511}
512
517{
518 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxGainedFocus();
519}
520
525{
526 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
527}
528
533void Window::RaiseButtons(bool autoraise)
534{
535 for (auto &pair : this->widget_lookup) {
536 WidgetType type = pair.second->type;
537 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
538 if (wid != nullptr && ((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
539 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
540 wid->SetLowered(false);
541 wid->SetDirty(this);
542 }
543 }
544
545 /* Special widgets without widget index */
546 {
547 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
548 if (wid != nullptr) {
549 wid->SetLowered(false);
550 wid->SetDirty(this);
551 }
552 }
553}
554
559void Window::SetWidgetDirty(WidgetID widget_index) const
560{
561 /* Sometimes this function is called before the window is even fully initialized */
562 auto it = this->widget_lookup.find(widget_index);
563 if (it == std::end(this->widget_lookup)) return;
564
565 it->second->SetDirty(this);
566}
567
574{
575 if (hotkey < 0) return ES_NOT_HANDLED;
576
577 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
578 if (nw == nullptr || nw->IsDisabled()) return ES_NOT_HANDLED;
579
580 if (nw->type == WWT_EDITBOX) {
581 if (this->IsShaded()) return ES_NOT_HANDLED;
582
583 /* Focus editbox */
584 this->SetFocusedWidget(hotkey);
585 SetFocusedWindow(this);
586 } else {
587 /* Click button */
588 this->OnClick(Point(), hotkey, 1);
589 }
590 return ES_HANDLED;
591}
592
599{
600 /* Button click for this widget may already have been handled. */
601 if (this->IsWidgetLowered(widget) && this->timeout_timer == TIMEOUT_DURATION) return;
602
603 this->LowerWidget(widget);
604 this->SetTimeout();
605 this->SetWidgetDirty(widget);
606 SndClickBeep();
607}
608
609static void StartWindowDrag(Window *w);
610static void StartWindowSizing(Window *w, bool to_left);
611
619static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
620{
621 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
622 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
623
624 /* Allow dropdown close flag detection to work. */
626
627 bool focused_widget_changed = false;
628
629 /* If clicked on a window that previously did not have focus */
630 if (_focused_window != w) {
631 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
632 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
633 focused_widget_changed = true;
635 } else if (_focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU) {
636 /* The previously focused window was a dropdown menu, but the user clicked on another window that
637 * isn't focusable. Close the dropdown menu anyway. */
638 SetFocusedWindow(nullptr);
639 }
640 }
641
642 if (nw == nullptr) return; // exit if clicked outside of widgets
643
644 /* don't allow any interaction if the button has been disabled */
645 if (nw->IsDisabled()) return;
646
647 WidgetID widget_index = nw->GetIndex();
648
649 /* Clicked on a widget that is not disabled.
650 * So unless the clicked widget is the caption bar, change focus to this widget.
651 * Exception: In the OSK we always want the editbox to stay focused. */
652 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
653 /* focused_widget_changed is 'now' only true if the window this widget
654 * is in gained focus. In that case it must remain true, also if the
655 * local widget focus did not change. As such it's the logical-or of
656 * both changed states.
657 *
658 * If this is not preserved, then the OSK window would be opened when
659 * a user has the edit box focused and then click on another window and
660 * then back again on the edit box (to type some text).
661 */
662 focused_widget_changed |= w->SetFocusedWidget(widget_index);
663 }
664
665 /* Dropdown window of this widget was closed so don't process click this time. */
667
668 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
669
670 Point pt = { x, y };
671
672 switch (widget_type) {
673 case NWID_VSCROLLBAR:
674 case NWID_HSCROLLBAR:
675 ScrollbarClickHandler(w, nw, x, y);
676 break;
677
678 case WWT_EDITBOX: {
679 QueryString *query = w->GetQueryString(widget_index);
680 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
681 break;
682 }
683
684 case WWT_CLOSEBOX: // 'X'
685 w->Close();
686 return;
687
688 case WWT_CAPTION: // 'Title bar'
690 return;
691
692 case WWT_RESIZEBOX:
693 /* When the resize widget is on the left size of the window
694 * we assume that that button is used to resize to the left. */
695 StartWindowSizing(w, nw->pos_x < (w->width / 2));
696 nw->SetDirty(w);
697 return;
698
699 case WWT_DEFSIZEBOX: {
700 if (_ctrl_pressed) {
701 if (click_count > 1) {
702 w->window_desc.pref_width = 0;
704 } else {
707 }
708 } else {
709 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
710 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
711
712 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
713 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
714 /* dx and dy has to go by step.. calculate it.
715 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
716 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
717 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
718 ResizeWindow(w, dx, dy, false);
719 }
720
721 nw->SetLowered(true);
722 nw->SetDirty(w);
723 w->SetTimeout();
724 break;
725 }
726
727 case WWT_DEBUGBOX:
729 break;
730
731 case WWT_SHADEBOX:
732 nw->SetDirty(w);
733 w->SetShaded(!w->IsShaded());
734 return;
735
736 case WWT_STICKYBOX:
738 nw->SetDirty(w);
740 return;
741
742 default:
743 break;
744 }
745
746 /* Widget has no index, so the window is not interested in it. */
747 if (widget_index < 0) return;
748
749 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
750 if (w->IsWidgetHighlighted(widget_index)) {
751 w->SetWidgetHighlight(widget_index, TC_INVALID);
752 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
753 }
754
755 w->OnClick(pt, widget_index, click_count);
756}
757
764static void DispatchRightClickEvent(Window *w, int x, int y)
765{
766 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
767 if (wid == nullptr) return;
768
769 Point pt = { x, y };
770
771 /* No widget to handle, or the window is not interested in it. */
772 if (wid->GetIndex() >= 0) {
773 if (w->OnRightClick(pt, wid->GetIndex())) return;
774 }
775
776 /* Right-click close is enabled and there is a closebox. */
777 if (_settings_client.gui.right_click_wnd_close == RightClickClose::Yes && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
778 w->Close();
779 } else if (_settings_client.gui.right_click_wnd_close == RightClickClose::YesExceptSticky && !w->flags.Test(WindowFlag::Sticky) && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
780 /* Right-click close is enabled, but excluding sticky windows. */
781 w->Close();
782 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
783 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_RIGHT_CLICK);
784 }
785}
786
793static void DispatchHoverEvent(Window *w, int x, int y)
794{
795 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
796
797 /* No widget to handle */
798 if (wid == nullptr) return;
799
800 Point pt = { x, y };
801
802 /* Show the tooltip if there is any */
803 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
804 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_HOVER);
805 return;
806 }
807
808 /* Widget has no index, so the window is not interested in it. */
809 if (wid->GetIndex() < 0) return;
810
811 w->OnHover(pt, wid->GetIndex());
812}
813
821static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
822{
823 if (nwid == nullptr) return;
824
825 /* Using wheel on caption/shade-box shades or unshades the window. */
826 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
827 w->SetShaded(wheel < 0);
828 return;
829 }
830
831 /* Wheeling a vertical scrollbar. */
832 if (nwid->type == NWID_VSCROLLBAR) {
833 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
834 if (sb->GetCount() > sb->GetCapacity()) {
835 if (sb->UpdatePosition(wheel)) {
836 w->OnScrollbarScroll(nwid->GetIndex());
837 w->SetDirty();
838 }
839 }
840 return;
841 }
842
843 /* Scroll the widget attached to the scrollbar. */
844 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
845 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
846 if (sb->UpdatePosition(wheel)) {
848 w->SetDirty();
849 }
850 }
851}
852
858static bool MayBeShown(const Window *w)
859{
860 /* If we're not modal, everything is okay. */
861 if (!HasModalProgress()) return true;
862
863 switch (w->window_class) {
864 case WC_MAIN_WINDOW:
865 case WC_MODAL_PROGRESS:
867 return true;
868
869 default:
870 return false;
871 }
872}
873
886static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
887{
889 ++it;
890 for (; !it.IsEnd(); ++it) {
891 const Window *v = *it;
892 if (MayBeShown(v) &&
893 right > v->left &&
894 bottom > v->top &&
895 left < v->left + v->width &&
896 top < v->top + v->height) {
897 /* v and rectangle intersect with each other */
898 int x;
899
900 if (left < (x = v->left)) {
901 DrawOverlappedWindow(w, left, top, x, bottom);
902 DrawOverlappedWindow(w, x, top, right, bottom);
903 return;
904 }
905
906 if (right > (x = v->left + v->width)) {
907 DrawOverlappedWindow(w, left, top, x, bottom);
908 DrawOverlappedWindow(w, x, top, right, bottom);
909 return;
910 }
911
912 if (top < (x = v->top)) {
913 DrawOverlappedWindow(w, left, top, right, x);
914 DrawOverlappedWindow(w, left, x, right, bottom);
915 return;
916 }
917
918 if (bottom > (x = v->top + v->height)) {
919 DrawOverlappedWindow(w, left, top, right, x);
920 DrawOverlappedWindow(w, left, x, right, bottom);
921 return;
922 }
923
924 return;
925 }
926 }
927
928 /* Setup blitter, and dispatch a repaint event to window *wz */
929 DrawPixelInfo *dp = _cur_dpi;
930 dp->width = right - left;
931 dp->height = bottom - top;
932 dp->left = left - w->left;
933 dp->top = top - w->top;
934 dp->pitch = _screen.pitch;
935 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
936 dp->zoom = ZoomLevel::Min;
937 w->OnPaint();
938}
939
948void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
949{
950 DrawPixelInfo bk;
951 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
952
953 for (Window *w : Window::IterateFromBack()) {
954 if (MayBeShown(w) &&
955 right > w->left &&
956 bottom > w->top &&
957 left < w->left + w->width &&
958 top < w->top + w->height) {
959 /* Window w intersects with the rectangle => needs repaint */
960 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));
961 }
962 }
963}
964
970{
971 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
972}
973
981void Window::ReInit(int rx, int ry, bool reposition)
982{
983 this->SetDirty(); // Mark whole current window as dirty.
984
985 /* Save current size. */
986 int window_width = this->width * _gui_scale / this->scale;
987 int window_height = this->height * _gui_scale / this->scale;
988 this->scale = _gui_scale;
989
990 this->OnInit();
991 /* Re-initialize window smallest size. */
992 this->nested_root->SetupSmallestSize(this);
993 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
994 this->width = this->nested_root->smallest_x;
995 this->height = this->nested_root->smallest_y;
996 this->resize.step_width = this->nested_root->resize_x;
997 this->resize.step_height = this->nested_root->resize_y;
998
999 /* Resize as close to the original size + requested resize as possible. */
1000 window_width = std::max(window_width + rx, this->width);
1001 window_height = std::max(window_height + ry, this->height);
1002 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
1003 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
1004 /* dx and dy has to go by step.. calculate it.
1005 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1006 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1007 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1008
1009 if (reposition) {
1010 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1011 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1012 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1013 }
1014
1015 ResizeWindow(this, dx, dy, true, false);
1016 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1017}
1018
1024void Window::SetShaded(bool make_shaded)
1025{
1026 if (this->shade_select == nullptr) return;
1027
1028 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1029 if (this->shade_select->shown_plane != desired) {
1030 if (make_shaded) {
1031 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1032 this->unshaded_size.width = this->width;
1033 this->unshaded_size.height = this->height;
1034 this->shade_select->SetDisplayedPlane(desired);
1035 this->ReInit(0, -this->height);
1036 } else {
1037 this->shade_select->SetDisplayedPlane(desired);
1038 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1039 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1040 this->ReInit(dx, dy);
1041 }
1042 }
1043}
1044
1050Window *Window::FindChildWindow(WindowClass wc) const
1051{
1052 for (Window *v : Window::Iterate()) {
1053 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1054 }
1055
1056 return nullptr;
1057}
1058
1066{
1067 for (Window *v : Window::Iterate()) {
1068 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1069 }
1070
1071 return nullptr;
1072}
1073
1078void Window::CloseChildWindows(WindowClass wc) const
1079{
1080 Window *child = this->FindChildWindow(wc);
1081 while (child != nullptr) {
1082 child->Close();
1083 child = this->FindChildWindow(wc);
1084 }
1085}
1086
1087
1093void Window::CloseChildWindowById(WindowClass wc, WindowNumber number) const
1094{
1095 Window *child = this->FindChildWindowById(wc, number);
1096 while (child != nullptr) {
1097 child->Close();
1098 child = this->FindChildWindowById(wc, number);
1099 }
1100}
1101
1106void Window::Close([[maybe_unused]] int data)
1107{
1108 /* Don't close twice. */
1109 if (*this->z_position == nullptr) return;
1110
1111 *this->z_position = nullptr;
1112
1113 if (_thd.window_class == this->window_class &&
1114 _thd.window_number == this->window_number) {
1116 }
1117
1118 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1119 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1120
1121 /* We can't scroll the window when it's closed. */
1122 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1123
1124 /* Make sure we don't try to access non-existing query strings. */
1125 this->querystrings.clear();
1126
1127 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1128 if (_focused_window == this) {
1129 this->OnFocusLost(true);
1130 _focused_window = nullptr;
1131 }
1132
1133 this->CloseChildWindows();
1134
1135 this->SetDirty();
1136
1137 Window::closed_windows.push_back(this);
1138}
1139
1144{
1145 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1146 assert(*this->z_position == nullptr);
1147}
1148
1155Window *FindWindowById(WindowClass cls, WindowNumber number)
1156{
1157 for (Window *w : Window::Iterate()) {
1158 if (w->window_class == cls && w->window_number == number) return w;
1159 }
1160
1161 return nullptr;
1162}
1163
1170Window *FindWindowByClass(WindowClass cls)
1171{
1172 for (Window *w : Window::Iterate()) {
1173 if (w->window_class == cls) return w;
1174 }
1175
1176 return nullptr;
1177}
1178
1185{
1187 assert(w != nullptr);
1188 return w;
1189}
1190
1198void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1199{
1200 Window *w = FindWindowById(cls, number);
1201 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1202 w->Close(data);
1203 }
1204}
1205
1211void CloseWindowByClass(WindowClass cls, int data)
1212{
1213 /* Note: the container remains stable, even when deleting windows. */
1214 for (Window *w : Window::Iterate()) {
1215 if (w->window_class == cls) {
1216 w->Close(data);
1217 }
1218 }
1219}
1220
1227void CloseCompanyWindows(CompanyID id)
1228{
1229 /* Note: the container remains stable, even when deleting windows. */
1230 for (Window *w : Window::Iterate()) {
1231 if (w->owner == id) {
1232 w->Close();
1233 }
1234 }
1235
1236 /* Also delete the company specific windows that don't have a company-colour. */
1238}
1239
1247void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1248{
1249 for (Window *w : Window::Iterate()) {
1250 if (w->owner != old_owner) continue;
1251
1252 switch (w->window_class) {
1253 case WC_COMPANY_COLOUR:
1254 case WC_FINANCES:
1255 case WC_STATION_LIST:
1256 case WC_TRAINS_LIST:
1257 case WC_ROADVEH_LIST:
1258 case WC_SHIPS_LIST:
1259 case WC_AIRCRAFT_LIST:
1260 case WC_BUY_COMPANY:
1261 case WC_COMPANY:
1263 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().
1264 continue;
1265
1266 default:
1267 w->owner = new_owner;
1268 break;
1269 }
1270 }
1271}
1272
1273static void BringWindowToFront(Window *w, bool dirty = true);
1274
1283{
1284 Window *w = FindWindowById(cls, number);
1285
1286 if (w != nullptr) {
1287 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1288
1289 w->SetWhiteBorder();
1291 w->SetDirty();
1292 }
1293
1294 return w;
1295}
1296
1297static inline bool IsVitalWindow(const Window *w)
1298{
1299 switch (w->window_class) {
1300 case WC_MAIN_TOOLBAR:
1301 case WC_STATUS_BAR:
1302 case WC_NEWS_WINDOW:
1304 return true;
1305
1306 default:
1307 return false;
1308 }
1309}
1310
1319static uint GetWindowZPriority(WindowClass wc)
1320{
1321 assert(wc != WC_INVALID);
1322
1323 uint z_priority = 0;
1324
1325 switch (wc) {
1326 case WC_TOOLTIPS:
1327 ++z_priority;
1328 [[fallthrough]];
1329
1330 case WC_ERRMSG:
1332 ++z_priority;
1333 [[fallthrough]];
1334
1335 case WC_ENDSCREEN:
1336 ++z_priority;
1337 [[fallthrough]];
1338
1339 case WC_HIGHSCORE:
1340 ++z_priority;
1341 [[fallthrough]];
1342
1343 case WC_DROPDOWN_MENU:
1344 ++z_priority;
1345 [[fallthrough]];
1346
1347 case WC_MAIN_TOOLBAR:
1348 case WC_STATUS_BAR:
1349 ++z_priority;
1350 [[fallthrough]];
1351
1352 case WC_OSK:
1353 ++z_priority;
1354 [[fallthrough]];
1355
1356 case WC_QUERY_STRING:
1358 ++z_priority;
1359 [[fallthrough]];
1360
1362 case WC_MODAL_PROGRESS:
1364 case WC_SAVE_PRESET:
1365 ++z_priority;
1366 [[fallthrough]];
1367
1369 case WC_SAVELOAD:
1370 case WC_GAME_OPTIONS:
1371 case WC_CUSTOM_CURRENCY:
1372 case WC_NETWORK_WINDOW:
1373 case WC_GRF_PARAMETERS:
1374 case WC_SCRIPT_LIST:
1375 case WC_SCRIPT_SETTINGS:
1376 case WC_TEXTFILE:
1377 ++z_priority;
1378 [[fallthrough]];
1379
1380 case WC_CONSOLE:
1381 ++z_priority;
1382 [[fallthrough]];
1383
1384 case WC_NEWS_WINDOW:
1385 ++z_priority;
1386 [[fallthrough]];
1387
1388 default:
1389 ++z_priority;
1390 [[fallthrough]];
1391
1392 case WC_MAIN_WINDOW:
1393 return z_priority;
1394 }
1395}
1396
1403static void BringWindowToFront(Window *w, bool dirty)
1404{
1405 auto priority = GetWindowZPriority(w->window_class);
1406 WindowList::iterator dest = _z_windows.begin();
1407 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1408
1409 if (dest != w->z_position) {
1410 _z_windows.splice(dest, _z_windows, w->z_position);
1411 }
1412
1413 if (dirty) w->SetDirty();
1414}
1415
1424{
1425 /* Set up window properties; some of them are needed to set up smallest size below */
1426 this->window_class = this->window_desc.cls;
1427 this->SetWhiteBorder();
1428 if (this->window_desc.default_pos == WDP_CENTER) this->flags.Set(WindowFlag::Centred);
1429 this->owner = INVALID_OWNER;
1430 this->nested_focus = nullptr;
1431 this->window_number = window_number;
1432
1433 this->OnInit();
1434 /* Initialize smallest size. */
1435 this->nested_root->SetupSmallestSize(this);
1436 /* Initialize to smallest size. */
1437 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1438
1439 /* Further set up window properties,
1440 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1441 this->resize.step_width = this->nested_root->resize_x;
1442 this->resize.step_height = this->nested_root->resize_y;
1443
1444 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1445 * (so we don't interrupt typing) unless the new window has a text box. */
1446 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1447 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1448 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1449
1450 /* Insert the window into the correct location in the z-ordering. */
1451 BringWindowToFront(this, false);
1452}
1453
1461void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1462{
1463 this->left = x;
1464 this->top = y;
1465 this->width = sm_width;
1466 this->height = sm_height;
1467}
1468
1480void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1481{
1482 if (allow_resize) {
1483 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1484 def_height = std::max(def_height, this->height);
1485 /* Try to make windows smaller when our window is too small.
1486 * w->(width|height) is normally the same as min_(width|height),
1487 * but this way the GUIs can be made a little more dynamic;
1488 * one can use the same spec for multiple windows and those
1489 * can then determine the real minimum size of the window. */
1490 if (this->width != def_width || this->height != def_height) {
1491 /* Think about the overlapping toolbars when determining the minimum window size */
1492 int free_height = _screen.height;
1493 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1494 if (wt != nullptr) free_height -= wt->height;
1496 if (wt != nullptr) free_height -= wt->height;
1497
1498 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1499 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1500
1501 /* X and Y has to go by step.. calculate it.
1502 * The cast to int is necessary else x/y are implicitly cast to
1503 * unsigned int, which won't work. */
1504 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1505 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1506
1507 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1508 /* ResizeWindow() calls this->OnResize(). */
1509 } else {
1510 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1511 this->OnResize();
1512 }
1513 }
1514
1515 int nx = this->left;
1516 int ny = this->top;
1517
1518 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1519
1520 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1521 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1522 nx = std::max(nx, 0);
1523
1524 if (this->viewport != nullptr) {
1525 this->viewport->left += nx - this->left;
1526 this->viewport->top += ny - this->top;
1527 }
1528 this->left = nx;
1529 this->top = ny;
1530
1531 this->SetDirty();
1532}
1533
1546static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1547{
1548 int right = width + left;
1549 int bottom = height + top;
1550
1551 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1552
1553 /* Make sure it is not obscured by any window. */
1554 for (const Window *w : Window::Iterate()) {
1555 if (w->window_class == WC_MAIN_WINDOW) continue;
1556
1557 if (right > w->left &&
1558 w->left + w->width > left &&
1559 bottom > w->top &&
1560 w->top + w->height > top) {
1561 return false;
1562 }
1563 }
1564
1565 pos.x = left;
1566 pos.y = top;
1567 return true;
1568}
1569
1582static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1583{
1584 bool rtl = _current_text_dir == TD_RTL;
1585
1586 /* Left part of the rectangle may be at most 1/4 off-screen,
1587 * right part of the rectangle may be at most 1/2 off-screen
1588 */
1589 if (rtl) {
1590 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1591 } else {
1592 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1593 }
1594
1595 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1596 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1597
1598 /* Make sure it is not obscured by any window. */
1599 for (const Window *w : Window::Iterate()) {
1600 if (w->window_class == WC_MAIN_WINDOW) continue;
1601
1602 if (left + width > w->left &&
1603 w->left + w->width > left &&
1604 top + height > w->top &&
1605 w->top + w->height > top) {
1606 return false;
1607 }
1608 }
1609
1610 pos.x = left;
1611 pos.y = top;
1612 return true;
1613}
1614
1621static Point GetAutoPlacePosition(int width, int height)
1622{
1623 Point pt;
1624
1625 bool rtl = _current_text_dir == TD_RTL;
1626
1627 /* First attempt, try top-left of the screen */
1628 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1629 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1630 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1631
1632 /* Second attempt, try around all existing windows.
1633 * The new window must be entirely on-screen, and not overlap with an existing window.
1634 * Eight starting points are tried, two at each corner.
1635 */
1636 for (const Window *w : Window::Iterate()) {
1637 if (w->window_class == WC_MAIN_WINDOW) continue;
1638
1639 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1640 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1641 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1642 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1643 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1644 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1645 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1646 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1647 }
1648
1649 /* Third attempt, try around all existing windows.
1650 * The new window may be partly off-screen, and must not overlap with an existing window.
1651 * Only four starting points are tried.
1652 */
1653 for (const Window *w : Window::Iterate()) {
1654 if (w->window_class == WC_MAIN_WINDOW) continue;
1655
1656 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1657 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1658 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1659 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1660 }
1661
1662 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1663 * of the closebox
1664 */
1665 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1666 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1667 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1668
1669restart:
1670 for (const Window *w : Window::Iterate()) {
1671 if (w->left == left && w->top == top) {
1672 left += offset_x;
1673 top += offset_y;
1674 goto restart;
1675 }
1676 }
1677
1678 pt.x = left;
1679 pt.y = top;
1680 return pt;
1681}
1682
1690{
1691 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1692 assert(w != nullptr);
1693 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1694 return pt;
1695}
1696
1706{
1707 Point pt = GetToolbarAlignedWindowPosition(window_width);
1709 if (w != nullptr && w->top == pt.y && !_settings_client.gui.link_terraform_toolbar) {
1710 pt.x = w->left + (_current_text_dir == TD_RTL ? w->width : - window_width);
1711 }
1712 return pt;
1713}
1714
1732static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1733{
1734 Point pt;
1735 const Window *w;
1736
1737 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1738 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1739
1740 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1741 bool rtl = _current_text_dir == TD_RTL;
1742 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1743 pt.x = w->left + (rtl ? w->width - default_width : 0);
1744 pt.y = w->top + w->height;
1745 return pt;
1746 } else {
1747 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1748 * - Y position: closebox of parent + closebox of child + statusbar
1749 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1750 */
1751 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1752 if (w->top + 3 * indent_y < _screen.height) {
1753 pt.y = w->top + indent_y;
1754 int indent_close = NWidgetLeaf::closebox_dimension.width;
1755 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1756 if (_current_text_dir == TD_RTL) {
1757 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1758 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1759 } else {
1760 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1761 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1762 }
1763 }
1764 }
1765 }
1766
1767 switch (desc.default_pos) {
1768 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1769 return GetToolbarAlignedWindowPosition(default_width);
1770
1771 case WDP_AUTO: // Find a good automatic position for the window
1772 return GetAutoPlacePosition(default_width, default_height);
1773
1774 case WDP_CENTER: // Centre the window horizontally
1775 pt.x = (_screen.width - default_width) / 2;
1776 pt.y = (_screen.height - default_height) / 2;
1777 break;
1778
1779 case WDP_MANUAL:
1780 pt.x = 0;
1781 pt.y = 0;
1782 break;
1783
1784 default:
1785 NOT_REACHED();
1786 }
1787
1788 return pt;
1789}
1790
1791/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1792{
1793 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1794}
1795
1803{
1804 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1805 this->nested_root->FillWidgetLookup(this->widget_lookup);
1806}
1807
1813{
1814 this->InitializeData(window_number);
1815 this->ApplyDefaults();
1816 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1817 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1818 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1819}
1820
1826{
1827 this->CreateNestedTree();
1828 this->FinishInitNested(window_number);
1829}
1830
1836{
1837 this->z_position = _z_windows.insert(_z_windows.end(), this);
1838}
1839
1848{
1849 for (Window *w : Window::IterateFromFront()) {
1850 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1851 return w;
1852 }
1853 }
1854
1855 return nullptr;
1856}
1857
1862{
1863 IConsoleClose();
1864
1865 _focused_window = nullptr;
1866 _mouseover_last_w = nullptr;
1867 _last_scroll_window = nullptr;
1868 _scrolling_viewport = false;
1869 _mouse_hovering = false;
1870
1872 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1873 NWidgetScrollbar::InvalidateDimensionCache();
1874
1876
1878}
1879
1884{
1886
1887 for (Window *w : Window::Iterate()) w->Close();
1888
1890
1891 assert(_z_windows.empty());
1892}
1893
1898{
1901 _thd.Reset();
1902}
1903
1904static void DecreaseWindowCounters()
1905{
1906 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1907
1908 for (Window *w : Window::Iterate()) {
1909 if (_scroller_click_timeout == 0) {
1910 /* Unclick scrollbar buttons if they are pressed. */
1911 for (auto &pair : w->widget_lookup) {
1912 NWidgetBase *nwid = pair.second;
1913 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1914 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1915 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1918 sb->SetDirty(w);
1919 }
1920 }
1921 }
1922 }
1923
1924 /* Handle editboxes */
1925 for (auto &pair : w->querystrings) {
1926 pair.second->HandleEditBox(w, pair.first);
1927 }
1928
1929 w->OnMouseLoop();
1930 }
1931
1932 for (Window *w : Window::Iterate()) {
1933 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1935
1936 w->OnTimeout();
1937 w->RaiseButtons(true);
1938 }
1939 }
1940}
1941
1942static void HandlePlacePresize()
1943{
1944 if (_special_mouse_mode != WSM_PRESIZE) return;
1945
1946 Window *w = _thd.GetCallbackWnd();
1947 if (w == nullptr) return;
1948
1949 Point pt = GetTileBelowCursor();
1950 if (pt.x == -1) {
1951 _thd.selend.x = -1;
1952 return;
1953 }
1954
1955 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1956}
1957
1963{
1965
1966 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1967
1968 Window *w = _thd.GetCallbackWnd();
1969 if (w != nullptr) {
1970 /* Send an event in client coordinates. */
1971 Point pt;
1972 pt.x = _cursor.pos.x - w->left;
1973 pt.y = _cursor.pos.y - w->top;
1974 if (_left_button_down) {
1975 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1976 } else {
1977 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1978 }
1979 }
1980
1981 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1982 return ES_HANDLED;
1983}
1984
1986static void HandleMouseOver()
1987{
1988 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1989
1990 /* We changed window, put an OnMouseOver event to the last window */
1991 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1992 /* Reset mouse-over coordinates of previous window */
1993 Point pt = { -1, -1 };
1994 _mouseover_last_w->OnMouseOver(pt, 0);
1995 }
1996
1997 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1999
2000 if (w != nullptr) {
2001 /* send an event in client coordinates. */
2002 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2003 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
2004 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
2005 }
2006}
2007
2013
2024static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
2025{
2026 if (v == nullptr) return;
2027
2028 const int min_visible = rect.Height();
2029
2030 int v_bottom = v->top + v->height - 1;
2031 int v_right = v->left + v->width - 1;
2032 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2033
2034 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2035 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2036
2037 /* Vertically, the rectangle is hidden behind v. */
2038 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2039 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2040 return;
2041 }
2042 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2043 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2044 return;
2045 }
2046
2047 /* Horizontally also hidden, force movement to a safe area. */
2048 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2049 *nx = v->left - min_visible - rect.left;
2050 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2051 *nx = v_right + min_visible - rect.right;
2052 } else {
2053 *ny = safe_y;
2054 }
2055}
2056
2064static void EnsureVisibleCaption(Window *w, int nx, int ny)
2065{
2066 /* Search for the title bar rectangle. */
2067 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2068 if (caption != nullptr) {
2069 const Rect caption_rect = caption->GetCurrentRect();
2070
2071 const int min_visible = caption_rect.Height();
2072
2073 /* Make sure the window doesn't leave the screen */
2074 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2075 ny = Clamp(ny, 0, _screen.height - min_visible);
2076
2077 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2078 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2079 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2080 }
2081
2082 if (w->viewport != nullptr) {
2083 w->viewport->left += nx - w->left;
2084 w->viewport->top += ny - w->top;
2085 }
2086
2087 w->left = nx;
2088 w->top = ny;
2089}
2090
2102void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2103{
2104 if (delta_x != 0 || delta_y != 0) {
2105 if (clamp_to_screen) {
2106 /* Determine the new right/bottom position. If that is outside of the bounds of
2107 * the resolution clamp it in such a manner that it stays within the bounds. */
2108 int new_right = w->left + w->width + delta_x;
2109 int new_bottom = w->top + w->height + delta_y;
2110 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2111 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2112 }
2113
2114 w->SetDirty();
2115
2116 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);
2117 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);
2118 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2119 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2120
2121 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);
2122 w->width = w->nested_root->current_x;
2123 w->height = w->nested_root->current_y;
2124 }
2125
2126 EnsureVisibleCaption(w, w->left, w->top);
2127
2128 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2129 if (schedule_resize) {
2130 w->ScheduleResize();
2131 } else {
2132 w->OnResize();
2133 }
2134 w->SetDirty();
2135}
2136
2143{
2145 return (w == nullptr) ? 0 : w->top + w->height;
2146}
2147
2154{
2156 return (w == nullptr) ? _screen.height : w->top;
2157}
2158
2159static bool _dragging_window;
2160
2166{
2167 /* Get out immediately if no window is being dragged at all. */
2168 if (!_dragging_window) return ES_NOT_HANDLED;
2169
2170 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2171 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2172
2173 /* Otherwise find the window... */
2174 for (Window *w : Window::Iterate()) {
2176 /* Stop the dragging if the left mouse button was released */
2177 if (!_left_button_down) {
2179 break;
2180 }
2181
2182 w->SetDirty();
2183
2184 int x = _cursor.pos.x + _drag_delta.x;
2185 int y = _cursor.pos.y + _drag_delta.y;
2186 int nx = x;
2187 int ny = y;
2188
2189 if (_settings_client.gui.window_snap_radius != 0) {
2190 int hsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2191 int vsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2192 int delta;
2193
2194 for (const Window *v : Window::Iterate()) {
2195 if (v == w) continue; // Don't snap at yourself
2196
2197 if (y + w->height > v->top && y < v->top + v->height) {
2198 /* Your left border <-> other right border */
2199 delta = abs(v->left + v->width - x);
2200 if (delta <= hsnap) {
2201 nx = v->left + v->width;
2202 hsnap = delta;
2203 }
2204
2205 /* Your right border <-> other left border */
2206 delta = abs(v->left - x - w->width);
2207 if (delta <= hsnap) {
2208 nx = v->left - w->width;
2209 hsnap = delta;
2210 }
2211 }
2212
2213 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2214 /* Your left border <-> other left border */
2215 delta = abs(v->left - x);
2216 if (delta <= hsnap) {
2217 nx = v->left;
2218 hsnap = delta;
2219 }
2220
2221 /* Your right border <-> other right border */
2222 delta = abs(v->left + v->width - x - w->width);
2223 if (delta <= hsnap) {
2224 nx = v->left + v->width - w->width;
2225 hsnap = delta;
2226 }
2227 }
2228
2229 if (x + w->width > v->left && x < v->left + v->width) {
2230 /* Your top border <-> other bottom border */
2231 delta = abs(v->top + v->height - y);
2232 if (delta <= vsnap) {
2233 ny = v->top + v->height;
2234 vsnap = delta;
2235 }
2236
2237 /* Your bottom border <-> other top border */
2238 delta = abs(v->top - y - w->height);
2239 if (delta <= vsnap) {
2240 ny = v->top - w->height;
2241 vsnap = delta;
2242 }
2243 }
2244
2245 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2246 /* Your top border <-> other top border */
2247 delta = abs(v->top - y);
2248 if (delta <= vsnap) {
2249 ny = v->top;
2250 vsnap = delta;
2251 }
2252
2253 /* Your bottom border <-> other bottom border */
2254 delta = abs(v->top + v->height - y - w->height);
2255 if (delta <= vsnap) {
2256 ny = v->top + v->height - w->height;
2257 vsnap = delta;
2258 }
2259 }
2260 }
2261 }
2262
2263 EnsureVisibleCaption(w, nx, ny);
2264
2265 w->SetDirty();
2266 return ES_HANDLED;
2268 /* Stop the sizing if the left mouse button was released */
2269 if (!_left_button_down) {
2272 w->SetDirty();
2273 break;
2274 }
2275
2276 /* Compute difference in pixels between cursor position and reference point in the window.
2277 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2278 */
2279 int x, y = _cursor.pos.y - _drag_delta.y;
2281 x = _drag_delta.x - _cursor.pos.x;
2282 } else {
2283 x = _cursor.pos.x - _drag_delta.x;
2284 }
2285
2286 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2287 if (w->resize.step_width == 0) x = 0;
2288 if (w->resize.step_height == 0) y = 0;
2289
2290 /* Check the resize button won't go past the bottom of the screen */
2291 if (w->top + w->height + y > _screen.height) {
2292 y = _screen.height - w->height - w->top;
2293 }
2294
2295 /* X and Y has to go by step.. calculate it.
2296 * The cast to int is necessary else x/y are implicitly cast to
2297 * unsigned int, which won't work. */
2298 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2299 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2300
2301 /* Check that we don't go below the minimum set size */
2302 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2303 x = w->nested_root->smallest_x - w->width;
2304 }
2305 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2306 y = w->nested_root->smallest_y - w->height;
2307 }
2308
2309 /* Window already on size */
2310 if (x == 0 && y == 0) return ES_HANDLED;
2311
2312 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2313 _drag_delta.y += y;
2314 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2315 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2316 w->SetDirty();
2317 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2318 /* ResizeWindow() below ensures marking new position as dirty. */
2319 } else {
2320 _drag_delta.x += x;
2321 }
2322
2323 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2324 ResizeWindow(w, x, y);
2325 return ES_HANDLED;
2326 }
2327 }
2328
2329 _dragging_window = false;
2330 return ES_HANDLED;
2331}
2332
2338{
2341 _dragging_window = true;
2342
2343 _drag_delta.x = w->left - _cursor.pos.x;
2344 _drag_delta.y = w->top - _cursor.pos.y;
2345
2347}
2348
2354static void StartWindowSizing(Window *w, bool to_left)
2355{
2358 _dragging_window = true;
2359
2360 _drag_delta.x = _cursor.pos.x;
2361 _drag_delta.y = _cursor.pos.y;
2362
2364}
2365
2371{
2372 int i;
2374 bool rtl = false;
2375
2376 if (sb->type == NWID_HSCROLLBAR) {
2377 i = _cursor.pos.x - _cursorpos_drag_start.x;
2378 rtl = _current_text_dir == TD_RTL;
2379 } else {
2380 i = _cursor.pos.y - _cursorpos_drag_start.y;
2381 }
2382
2383 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2384 if (_scroller_click_timeout == 1) {
2385 _scroller_click_timeout = 3;
2386 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2388 w->SetDirty();
2389 }
2390 }
2391 return;
2392 }
2393
2394 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2395 int range = sb->GetCount() - sb->GetCapacity();
2396 if (range <= 0) return;
2397
2398 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2399 if (rtl) pos = range - pos;
2400 if (sb->SetPosition(pos)) {
2402 w->SetDirty();
2403 }
2404}
2405
2411{
2412 for (Window *w : Window::Iterate()) {
2413 if (w->mouse_capture_widget >= 0) {
2414 /* Abort if no button is clicked any more. */
2415 if (!_left_button_down) {
2418 return ES_HANDLED;
2419 }
2420
2421 /* Handle scrollbar internally, or dispatch click event */
2423 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2425 } else {
2426 /* If cursor hasn't moved, there is nothing to do. */
2427 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2428
2429 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2430 w->OnClick(pt, w->mouse_capture_widget, 0);
2431 }
2432 return ES_HANDLED;
2433 }
2434 }
2435
2436 return ES_NOT_HANDLED;
2437}
2438
2444{
2445 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2446
2448
2449 /* When we don't have a last scroll window we are starting to scroll.
2450 * When the last scroll window and this are not the same we went
2451 * outside of the window and should not left-mouse scroll anymore. */
2452 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2453
2454 if (_last_scroll_window == nullptr || !((_settings_client.gui.scroll_mode != ViewportScrollMode::MapLMB && _right_button_down) || scrollwheel_scrolling || (_settings_client.gui.scroll_mode == ViewportScrollMode::MapLMB && _left_button_down))) {
2455 _cursor.fix_at = false;
2456 _scrolling_viewport = false;
2457 _last_scroll_window = nullptr;
2458 return ES_NOT_HANDLED;
2459 }
2460
2461 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2462 /* If the main window is following a vehicle, then first let go of it! */
2463 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
2464 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2465 return ES_NOT_HANDLED;
2466 }
2467
2468 Point delta;
2469 if (scrollwheel_scrolling) {
2470 /* We are using scrollwheels for scrolling */
2471 /* Use the integer part for movement */
2472 delta.x = static_cast<int>(_cursor.h_wheel);
2473 delta.y = static_cast<int>(_cursor.v_wheel);
2474 /* Keep the fractional part so that subtle movement is accumulated */
2475 float temp;
2476 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2477 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2478 } else {
2480 delta.x = -_cursor.delta.x;
2481 delta.y = -_cursor.delta.y;
2482 } else {
2483 delta.x = _cursor.delta.x;
2484 delta.y = _cursor.delta.y;
2485 }
2486 }
2487
2488 /* Create a scroll-event and send it to the window */
2489 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2490
2491 _cursor.delta.x = 0;
2492 _cursor.delta.y = 0;
2493 _cursor.wheel_moved = false;
2494 return ES_HANDLED;
2495}
2496
2508{
2509 bool bring_to_front = false;
2510
2511 if (w->window_class == WC_MAIN_WINDOW ||
2512 IsVitalWindow(w) ||
2513 w->window_class == WC_TOOLTIPS ||
2515 return true;
2516 }
2517
2518 /* Use unshaded window size rather than current size for shaded windows. */
2519 int w_width = w->width;
2520 int w_height = w->height;
2521 if (w->IsShaded()) {
2522 w_width = w->unshaded_size.width;
2523 w_height = w->unshaded_size.height;
2524 }
2525
2527 ++it;
2528 for (; !it.IsEnd(); ++it) {
2529 Window *u = *it;
2530 /* A modal child will prevent the activation of the parent window */
2532 u->SetWhiteBorder();
2533 u->SetDirty();
2534 return false;
2535 }
2536
2537 if (u->window_class == WC_MAIN_WINDOW ||
2538 IsVitalWindow(u) ||
2539 u->window_class == WC_TOOLTIPS ||
2541 continue;
2542 }
2543
2544 /* Window sizes don't interfere, leave z-order alone */
2545 if (w->left + w_width <= u->left ||
2546 u->left + u->width <= w->left ||
2547 w->top + w_height <= u->top ||
2548 u->top + u->height <= w->top) {
2549 continue;
2550 }
2551
2552 bring_to_front = true;
2553 }
2554
2555 if (bring_to_front) BringWindowToFront(w);
2556 return true;
2557}
2558
2567EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2568{
2569 QueryString *query = this->GetQueryString(wid);
2570 if (query == nullptr) return ES_NOT_HANDLED;
2571
2572 int action = QueryString::ACTION_NOTHING;
2573
2574 switch (query->text.HandleKeyPress(key, keycode)) {
2575 case HKPR_EDITING:
2576 this->SetWidgetDirty(wid);
2577 this->OnEditboxChanged(wid);
2578 break;
2579
2580 case HKPR_CURSOR:
2581 this->SetWidgetDirty(wid);
2582 /* For the OSK also invalidate the parent window */
2583 if (this->window_class == WC_OSK) this->InvalidateData();
2584 break;
2585
2586 case HKPR_CONFIRM:
2587 if (this->window_class == WC_OSK) {
2588 this->OnClick(Point(), WID_OSK_OK, 1);
2589 } else if (query->ok_button >= 0) {
2590 this->OnClick(Point(), query->ok_button, 1);
2591 } else {
2592 action = query->ok_button;
2593 }
2594 break;
2595
2596 case HKPR_CANCEL:
2597 if (this->window_class == WC_OSK) {
2598 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2599 } else if (query->cancel_button >= 0) {
2600 this->OnClick(Point(), query->cancel_button, 1);
2601 } else {
2602 action = query->cancel_button;
2603 }
2604 break;
2605
2606 case HKPR_NOT_HANDLED:
2607 return ES_NOT_HANDLED;
2608
2609 default: break;
2610 }
2611
2612 switch (action) {
2614 this->UnfocusFocusedWidget();
2615 break;
2616
2618 if (query->text.GetText().empty()) {
2619 /* If already empty, unfocus instead */
2620 this->UnfocusFocusedWidget();
2621 } else {
2622 query->text.DeleteAll();
2623 this->SetWidgetDirty(wid);
2624 this->OnEditboxChanged(wid);
2625 }
2626 break;
2627
2628 default:
2629 break;
2630 }
2631
2632 return ES_HANDLED;
2633}
2634
2639void HandleToolbarHotkey(int hotkey)
2640{
2641 assert(HasModalProgress() || IsLocalCompany());
2642
2644 if (w != nullptr) {
2645 if (w->window_desc.hotkeys != nullptr) {
2646 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2647 }
2648 }
2649}
2650
2656void HandleKeypress(uint keycode, char32_t key)
2657{
2658 /* World generation is multithreaded and messes with companies.
2659 * But there is no company related window open anyway, so _current_company is not used. */
2660 assert(HasModalProgress() || IsLocalCompany());
2661
2662 /*
2663 * The Unicode standard defines an area called the private use area. Code points in this
2664 * area are reserved for private use and thus not portable between systems. For instance,
2665 * Apple defines code points for the arrow keys in this area, but these are only printable
2666 * on a system running OS X. We don't want these keys to show up in text fields and such,
2667 * and thus we have to clear the unicode character when we encounter such a key.
2668 */
2669 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2670
2671 /*
2672 * If both key and keycode is zero, we don't bother to process the event.
2673 */
2674 if (key == 0 && keycode == 0) return;
2675
2676 /* Check if the focused window has a focused editbox */
2677 if (EditBoxInGlobalFocus()) {
2678 /* All input will in this case go to the focused editbox */
2679 if (_focused_window->window_class == WC_CONSOLE) {
2680 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2681 } else {
2682 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2683 }
2684 }
2685
2686 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2687 for (Window *w : Window::IterateFromFront()) {
2688 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2689 if (w->window_desc.hotkeys != nullptr) {
2690 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2691 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2692 }
2693 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2694 }
2695
2697 /* When there is no toolbar w is null, check for that */
2698 if (w != nullptr) {
2699 if (w->window_desc.hotkeys != nullptr) {
2700 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2701 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2702 }
2703 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2704 }
2705
2706 HandleGlobalHotkeys(key, keycode);
2707}
2708
2713{
2714 /* Call the event, start with the uppermost window. */
2715 for (Window *w : Window::IterateFromFront()) {
2716 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2717 }
2718}
2719
2729/* 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)
2730{
2731 QueryString *query = this->GetQueryString(wid);
2732 if (query == nullptr) return;
2733
2734 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2735 this->SetWidgetDirty(wid);
2736 this->OnEditboxChanged(wid);
2737 }
2738}
2739
2748void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2749{
2750 if (!EditBoxInGlobalFocus()) return;
2751
2752 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2753}
2754
2759static void HandleAutoscroll()
2760{
2761 if (_game_mode == GM_MENU || HasModalProgress()) return;
2762 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
2763 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2764
2765 int x = _cursor.pos.x;
2766 int y = _cursor.pos.y;
2767 Window *w = FindWindowFromPt(x, y);
2768 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2769 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
2770
2771 Viewport *vp = IsPtInWindowViewport(w, x, y);
2772 if (vp == nullptr) return;
2773
2774 x -= vp->left;
2775 y -= vp->top;
2776
2777 /* here allows scrolling in both x and y axis */
2778 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2779 static const int SCROLLSPEED = 3;
2780 if (x - 15 < 0) {
2781 w->viewport->CancelFollow(*w);
2782 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2783 } else if (15 - (vp->width - x) > 0) {
2784 w->viewport->CancelFollow(*w);
2785 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2786 }
2787 if (y - 15 < 0) {
2788 w->viewport->CancelFollow(*w);
2789 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2790 } else if (15 - (vp->height - y) > 0) {
2791 w->viewport->CancelFollow(*w);
2792 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2793 }
2794}
2795
2796enum MouseClick : uint8_t {
2797 MC_NONE = 0,
2798 MC_LEFT,
2799 MC_RIGHT,
2800 MC_DOUBLE_LEFT,
2801 MC_HOVER,
2802};
2803
2804static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2805static constexpr int MAX_OFFSET_HOVER = 5;
2806
2808
2809const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2810
2811static void ScrollMainViewport(int x, int y)
2812{
2813 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2814 Window *w = GetMainWindow();
2815 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2816 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2817 }
2818}
2819
2829static const int8_t scrollamt[16][2] = {
2830 { 0, 0},
2831 {-2, 0},
2832 { 0, -2},
2833 {-2, -1},
2834 { 2, 0},
2835 { 0, 0},
2836 { 2, -1},
2837 { 0, -2},
2838 { 0, 2},
2839 {-2, 1},
2840 { 0, 0},
2841 {-2, 0},
2842 { 2, 1},
2843 { 0, 2},
2844 { 2, 0},
2845 { 0, 0},
2846};
2847
2848static void HandleKeyScrolling()
2849{
2850 /*
2851 * Check that any of the dirkeys is pressed and that the focused window
2852 * doesn't have an edit-box as focused widget.
2853 */
2854 if (_dirkeys && !EditBoxInGlobalFocus()) {
2855 int factor = _shift_pressed ? 50 : 10;
2856
2857 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2858 /* Key scrolling stops following a vehicle. */
2859 Window *main_window = GetMainWindow();
2860 main_window->viewport->CancelFollow(*main_window);
2861 }
2862
2863 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2864 }
2865}
2866
2867static void MouseLoop(MouseClick click, int mousewheel)
2868{
2869 /* World generation is multithreaded and messes with companies.
2870 * But there is no company related window open anyway, so _current_company is not used. */
2871 assert(HasModalProgress() || IsLocalCompany());
2872
2873 HandlePlacePresize();
2875
2876 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2877 if (HandleMouseDragDrop() == ES_HANDLED) return;
2878 if (HandleWindowDragging() == ES_HANDLED) return;
2879 if (HandleActiveWidget() == ES_HANDLED) return;
2880 if (HandleViewportScroll() == ES_HANDLED) return;
2881
2883
2884 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2885 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2886
2887 int x = _cursor.pos.x;
2888 int y = _cursor.pos.y;
2889 Window *w = FindWindowFromPt(x, y);
2890 if (w == nullptr) return;
2891
2892 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2893 Viewport *vp = IsPtInWindowViewport(w, x, y);
2894
2895 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2896 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2897
2898 if (mousewheel != 0) {
2899 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2900 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) {
2901 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2902 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2903 }
2904 }
2905
2906 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2907 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2908 }
2909
2910 if (vp != nullptr) {
2911 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2912 _scrolling_viewport = true;
2913 _cursor.fix_at = true;
2914 return;
2915 }
2916
2917 switch (click) {
2918 case MC_DOUBLE_LEFT:
2919 case MC_LEFT:
2920 if (HandleViewportClicked(*vp, x, y)) return;
2922 _settings_client.gui.scroll_mode == ViewportScrollMode::MapLMB) {
2923 _scrolling_viewport = true;
2924 _cursor.fix_at = false;
2925 return;
2926 }
2927 break;
2928
2929 case MC_RIGHT:
2931 _settings_client.gui.scroll_mode != ViewportScrollMode::MapLMB) {
2932 _scrolling_viewport = true;
2933 _cursor.fix_at = (_settings_client.gui.scroll_mode == ViewportScrollMode::ViewportRMBFixed ||
2935 DispatchRightClickEvent(w, x - w->left, y - w->top);
2936 return;
2937 }
2938 break;
2939
2940 default:
2941 break;
2942 }
2943 }
2944
2945 switch (click) {
2946 case MC_LEFT:
2947 case MC_DOUBLE_LEFT:
2948 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2949 return;
2950
2951 default:
2952 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2953 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2954 * Simulate a right button click so we can get started. */
2955 [[fallthrough]];
2956
2957 case MC_RIGHT:
2958 DispatchRightClickEvent(w, x - w->left, y - w->top);
2959 return;
2960
2961 case MC_HOVER:
2962 DispatchHoverEvent(w, x - w->left, y - w->top);
2963 break;
2964 }
2965
2966 /* We're not doing anything with 2D scrolling, so reset the value. */
2967 _cursor.h_wheel = 0.0f;
2968 _cursor.v_wheel = 0.0f;
2969 _cursor.wheel_moved = false;
2970}
2971
2976{
2977 /* World generation is multithreaded and messes with companies.
2978 * But there is no company related window open anyway, so _current_company is not used. */
2979 assert(HasModalProgress() || IsLocalCompany());
2980
2981 static std::chrono::steady_clock::time_point double_click_time = {};
2982 static Point double_click_pos = {0, 0};
2983
2984 /* Mouse event? */
2985 MouseClick click = MC_NONE;
2987 click = MC_LEFT;
2988 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2989 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2990 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2991 click = MC_DOUBLE_LEFT;
2992 }
2993 double_click_time = std::chrono::steady_clock::now();
2994 double_click_pos = _cursor.pos;
2995 _left_button_clicked = true;
2996 } else if (_right_button_clicked) {
2997 _right_button_clicked = false;
2998 click = MC_RIGHT;
2999 }
3000
3001 int mousewheel = 0;
3002 if (_cursor.wheel) {
3003 mousewheel = _cursor.wheel;
3004 _cursor.wheel = 0;
3005 }
3006
3007 static std::chrono::steady_clock::time_point hover_time = {};
3008 static Point hover_pos = {0, 0};
3009
3010 if (_settings_client.gui.hover_delay_ms > 0) {
3011 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
3012 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
3013 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
3014 hover_pos = _cursor.pos;
3015 hover_time = std::chrono::steady_clock::now();
3016 _mouse_hovering = false;
3017 } else if (!_mouse_hovering) {
3018 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
3019 click = MC_HOVER;
3020 _mouse_hovering = true;
3021 hover_time = std::chrono::steady_clock::now();
3022 }
3023 }
3024 }
3025
3026 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
3027 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
3029 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
3030 _newgrf_debug_sprite_picker.sprites.clear();
3031 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3033 } else {
3034 MouseLoop(click, mousewheel);
3035 }
3036
3037 /* We have moved the mouse the required distance,
3038 * no need to move it at any later time. */
3039 _cursor.delta.x = 0;
3040 _cursor.delta.y = 0;
3041}
3042
3046static void CheckSoftLimit()
3047{
3048 if (_settings_client.gui.window_soft_limit == 0) return;
3049
3050 for (;;) {
3051 uint deletable_count = 0;
3052 Window *last_deletable = nullptr;
3053 for (Window *w : Window::IterateFromFront()) {
3054 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3055
3056 last_deletable = w;
3057 deletable_count++;
3058 }
3059
3060 /* We've not reached the soft limit yet. */
3061 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3062
3063 assert(last_deletable != nullptr);
3064 last_deletable->Close();
3065 }
3066}
3067
3072{
3073 /* World generation is multithreaded and messes with companies.
3074 * But there is no company related window open anyway, so _current_company is not used. */
3075 assert(HasModalProgress() || IsLocalCompany());
3076
3078
3079 /* Process scheduled window deletion. */
3081
3082 /* HandleMouseEvents was already called for this tick */
3084}
3085
3086static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3087
3088bool CanContinueRealtimeTick()
3089{
3090 auto now = std::chrono::steady_clock::now();
3091 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3092}
3093
3098{
3099 _realtime_tick_start = std::chrono::steady_clock::now();
3100 for (Window *w : Window::Iterate()) {
3101 w->OnRealtimeTick(delta_ms);
3102 }
3103}
3104
3106static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3107 extern int _caret_timer;
3108 _caret_timer += 3;
3109 CursorTick();
3110
3111 HandleKeyScrolling();
3113 DecreaseWindowCounters();
3114});
3115
3119});
3120
3122static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3123 if (_network_dedicated) return;
3124
3125 for (Window *w : Window::Iterate()) {
3128 w->SetDirty();
3129 }
3130 }
3131});
3132
3137{
3138 static auto last_time = std::chrono::steady_clock::now();
3139 auto now = std::chrono::steady_clock::now();
3140 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3141
3142 if (delta_ms.count() == 0) return;
3143
3144 last_time = now;
3145
3148
3150
3152 CallWindowRealtimeTickEvent(delta_ms.count());
3153
3154 /* Process invalidations before anything else. */
3155 for (Window *w : Window::Iterate()) {
3159 }
3160
3161 /* Skip the actual drawing on dedicated servers without screen.
3162 * But still empty the invalidation queues above. */
3163 if (_network_dedicated) return;
3164
3166
3167 for (Window *w : Window::Iterate()) {
3168 /* Update viewport only if window is not shaded. */
3169 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3170 }
3172 /* Redraw mouse cursor in case it was hidden */
3173 DrawMouseCursor();
3174
3175 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3176 /* We are done with the last draw-frame, so we know what sprites we
3177 * clicked on. Reset the picker mode and invalidate the window. */
3178 _newgrf_debug_sprite_picker.mode = SPM_NONE;
3180 }
3181}
3182
3188void SetWindowDirty(WindowClass cls, WindowNumber number)
3189{
3190 for (const Window *w : Window::Iterate()) {
3191 if (w->window_class == cls && w->window_number == number) {
3192 w->SetDirty();
3193 return;
3194 }
3195 }
3196}
3197
3204void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
3205{
3206 for (const Window *w : Window::Iterate()) {
3207 if (w->window_class == cls && w->window_number == number) {
3208 w->SetWidgetDirty(widget_index);
3209 return;
3210 }
3211 }
3212}
3213
3218void SetWindowClassesDirty(WindowClass cls)
3219{
3220 for (const Window *w : Window::Iterate()) {
3221 if (w->window_class == cls) w->SetDirty();
3222 }
3223}
3224
3229{
3230 this->scheduled_resize = true;
3231}
3232
3237{
3238 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3239 while (this->scheduled_resize) {
3240 this->scheduled_resize = false;
3241 this->OnResize();
3242 }
3243}
3244
3250void Window::InvalidateData(int data, bool gui_scope)
3251{
3252 this->SetDirty();
3253 if (!gui_scope) {
3254 /* Schedule GUI-scope invalidation for next redraw. */
3255 this->scheduled_invalidation_data.push_back(data);
3256 }
3257 this->OnInvalidateData(data, gui_scope);
3258}
3259
3264{
3265 for (int data : this->scheduled_invalidation_data) {
3266 if (this->window_class == WC_INVALID) break;
3267 this->OnInvalidateData(data, true);
3268 }
3269 this->scheduled_invalidation_data.clear();
3270}
3271
3276{
3277 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3278
3279 for (const auto &pair : this->widget_lookup) {
3280 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3281 }
3282}
3283
3310void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3311{
3312 for (Window *w : Window::Iterate()) {
3313 if (w->window_class == cls && w->window_number == number) {
3314 w->InvalidateData(data, gui_scope);
3315 return;
3316 }
3317 }
3318}
3319
3328void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3329{
3330 for (Window *w : Window::Iterate()) {
3331 if (w->window_class == cls) {
3332 w->InvalidateData(data, gui_scope);
3333 }
3334 }
3335}
3336
3341{
3342 for (Window *w : Window::Iterate()) {
3343 w->OnGameTick();
3344 }
3345}
3346
3354{
3355 /* Note: the container remains stable, even when deleting windows. */
3356 for (Window *w : Window::Iterate()) {
3358 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3359
3360 w->Close();
3361 }
3362 }
3363}
3364
3373{
3374 /* Note: the container remains stable, even when closing windows. */
3375 for (Window *w : Window::Iterate()) {
3377 w->Close();
3378 }
3379 }
3380}
3381
3386{
3388 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3389 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3390 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3391}
3392
3398{
3399 /* Note: the container remains stable, even when deleting windows. */
3400 for (Window *w : Window::Iterate()) {
3402 w->Close();
3403 }
3404 }
3405
3406 for (const Window *w : Window::Iterate()) w->SetDirty();
3407}
3408
3415
3416void ReInitWindow(Window *w, bool zoom_changed)
3417{
3418 if (w == nullptr) return;
3419 if (zoom_changed) {
3420 w->nested_root->AdjustPaddingForZoom();
3422 }
3423 w->ReInit();
3424}
3425
3427void ReInitAllWindows(bool zoom_changed)
3428{
3430 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3431 NWidgetScrollbar::InvalidateDimensionCache();
3432
3434
3435 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3436 * so EnsureVisibleCaption uses the updated size information. */
3437 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3438 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3439 for (Window *w : Window::Iterate()) {
3440 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3441 ReInitWindow(w, zoom_changed);
3442 }
3443
3446
3447 /* Make sure essential parts of all windows are visible */
3448 RelocateAllWindows(_screen.width, _screen.height);
3450}
3451
3459static int PositionWindow(Window *w, WindowClass clss, int setting)
3460{
3461 if (w == nullptr || w->window_class != clss) {
3462 w = FindWindowById(clss, 0);
3463 }
3464 if (w == nullptr) return 0;
3465
3466 int old_left = w->left;
3467 switch (setting) {
3468 case 1: w->left = (_screen.width - w->width) / 2; break;
3469 case 2: w->left = _screen.width - w->width; break;
3470 default: w->left = 0; break;
3471 }
3472 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3473 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3474 return w->left;
3475}
3476
3483{
3484 Debug(misc, 5, "Repositioning Main Toolbar...");
3485 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
3486}
3487
3494{
3495 Debug(misc, 5, "Repositioning statusbar...");
3496 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
3497}
3498
3505{
3506 Debug(misc, 5, "Repositioning news message...");
3507 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
3508}
3509
3516{
3517 Debug(misc, 5, "Repositioning network chat window...");
3518 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
3519}
3520
3521
3528{
3529 for (const Window *w : Window::Iterate()) {
3530 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3531 w->viewport->follow_vehicle = to_index;
3532 w->SetDirty();
3533 }
3534 }
3535}
3536
3537
3543void RelocateAllWindows(int neww, int newh)
3544{
3546
3547 /* Reposition toolbar then status bar before other all windows. */
3548 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3549 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3550 wt->left = PositionMainToolbar(wt);
3551 }
3552
3553 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3554 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3555 ws->top = newh - ws->height;
3556 ws->left = PositionStatusbar(ws);
3557 }
3558
3559 for (Window *w : Window::Iterate()) {
3560 int left, top;
3561 /* XXX - this probably needs something more sane. For example specifying
3562 * in a 'backup'-desc that the window should always be centered. */
3563 switch (w->window_class) {
3564 case WC_MAIN_WINDOW:
3565 case WC_BOOTSTRAP:
3566 case WC_HIGHSCORE:
3567 case WC_ENDSCREEN:
3568 ResizeWindow(w, neww, newh);
3569 continue;
3570
3571 case WC_MAIN_TOOLBAR:
3572 case WC_STATUS_BAR:
3573 continue;
3574
3575 case WC_NEWS_WINDOW:
3576 top = newh - w->height;
3577 left = PositionNewsMessage(w);
3578 break;
3579
3581 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3582
3583 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3584 left = PositionNetworkChatWindow(w);
3585 break;
3586
3587 case WC_CONSOLE:
3588 IConsoleResize(w);
3589 continue;
3590
3591 default: {
3592 if (w->flags.Test(WindowFlag::Centred)) {
3593 top = (newh - w->height) >> 1;
3594 left = (neww - w->width) >> 1;
3595 break;
3596 }
3597
3598 left = w->left;
3599 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3600 if (left < 0) left = 0;
3601
3602 top = w->top;
3603 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3604 break;
3605 }
3606 }
3607
3608 EnsureVisibleCaption(w, left, top);
3609 }
3610}
3611
3616void PickerWindowBase::Close([[maybe_unused]] int data)
3617{
3619 this->Window::Close();
3620}
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...
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 bool IsHighlighted() const
Whether the widget is currently highlighted or not.
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).
virtual void SetHighlighted(TextColour highlight_colour)
Highlight the widget or not.
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:1261
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1234
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:2643
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.
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:3616
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:3394
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:969
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:1521
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1457
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1554
Hotkey related functions.
Types related to reading/writing '*.ini' files.
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:406
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:694
bool _networking
are we in networking mode?
Definition network.cpp:66
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:69
Basic functions/variables used all over the place.
void NetworkDrawChatMessage()
Draw the chat message-box.
void NetworkReInitChatBoxSize()
Initialize all font-dependent chat box sizes.
void NetworkUndrawChatMessage()
Hide the chatbox.
Network functions used by other parts of OpenTTD.
Functions/types related to NewGRF debugging.
NewGrfDebugSpritePicker _newgrf_debug_sprite_picker
The sprite picker.
Functions related to news.
void InitNewsItemStructs()
Initialize the news-items data structures.
Definition news_gui.cpp:714
@ WID_OSK_CANCEL
Cancel key.
Definition osk_widget.h:17
@ WID_OSK_OK
Ok key.
Definition osk_widget.h:18
Functions related to modal progress.
bool HasModalProgress()
Check if we are currently in a modal progress state.
Definition progress.h:17
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
void IniLoadWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Load a WindowDesc from config.
Definition settings.cpp:870
void IniSaveWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Save a WindowDesc to config.
Definition settings.cpp:881
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.
@ MapRMBFixed
Map moves with mouse movement on holding right mouse button, cursor position is fixed.
@ ViewportRMBFixed
Viewport moves with mouse movement on holding right mouse button, cursor position is fixed.
@ MapLMB
Map moves with mouse movement on holding left mouse button, cursor moves.
@ ScrollMap
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...
T y
Y coordinate.
T x
X coordinate.
Data about how and where to blit pixels.
Definition gfx_type.h:157
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:184
static Vehicle * Get(auto 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:852
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:794
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:822
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 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
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 std::source_location source_location
Source location of this definition.
Definition window_gui.h:177
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.
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:367
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:981
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1106
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:795
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1812
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:1461
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:3250
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:937
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition window.cpp:1143
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:559
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:507
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:747
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition window_gui.h:871
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:721
virtual void OnDropdownSelect(WidgetID widget, int index, int click_result)
A dropdown option associated to this window has been selected.
Definition window_gui.h:772
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition window.cpp:3263
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1078
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:472
virtual void OnMouseLoop()
Called for every mouse loop run, which is at least once per (game) tick.
Definition window_gui.h:742
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition window.cpp:1024
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:3228
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:708
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1802
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:752
virtual void OnMouseWheel(int wheel, WidgetID widget)
The mouse wheel has been turned.
Definition window_gui.h:736
AllWindows< true > IterateFromFront
Iterate all windows in Z order from front to back.
Definition window_gui.h:938
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1093
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:488
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:524
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:533
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1791
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:380
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1480
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:287
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:2729
WindowIterator< false > IteratorToFront
Iterate in Z order towards front.
Definition window_gui.h:914
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:1050
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:780
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition window.cpp:356
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:336
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:411
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:1423
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:729
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1835
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:986
virtual bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition window_gui.h:694
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:3236
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2567
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:701
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:598
virtual void OnResize()
Called after the window got resized.
Definition window_gui.h:764
Window * FindChildWindowById(WindowClass wc, WindowNumber number) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1065
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:516
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1825
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h:757
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:316
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3275
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:573
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:396
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:855
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:936
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.
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
Base of all video drivers.
Viewport * IsPtInWindowViewport(const Window *w, int x, int y)
Is a xy position inside the viewport of the window?
Definition viewport.cpp:408
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
Update the viewport position being displayed.
Functions related to (drawing on) viewports.
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:80
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
Returns the index for the widget located at the given position relative to the window.
Definition widget.cpp:274
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:250
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:35
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:74
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:63
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:75
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX).
Definition widget_type.h:54
@ DropdownClosed
Dropdown menu of the dropdown widget has closed.
@ ScrollbarDown
Down-button is lowered bit.
@ ScrollbarUp
Up-button is lowered bit.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ ST_RESIZE
Resize the nested widget tree.
@ ST_SMALLEST
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
int PositionStatusbar(Window *w)
(Re)position statusbar window at the screen.
Definition window.cpp:3493
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:2024
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2159
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:3397
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:1198
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:1732
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1184
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:858
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1227
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2712
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:886
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:3071
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3136
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3482
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3353
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:948
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2102
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:1582
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3515
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2354
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:1621
PreventHideDirection
Direction for moving the window.
Definition window.cpp:2009
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:2011
@ PHD_UP
Above v is a safe position.
Definition window.cpp:2010
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2759
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:2639
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3459
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:424
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:1546
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2804
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1170
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3527
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:2443
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:821
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:619
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1847
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3385
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:2142
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:1403
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3427
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:2064
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1211
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2975
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:1689
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2165
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3504
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2153
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:793
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:1247
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2656
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3218
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:2805
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:464
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1897
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1319
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:450
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:3310
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3410
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:1282
Point AlignInitialConstructionToolbar(int window_width)
Compute the position of the construction toolbars.
Definition window.cpp:1705
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3543
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:2370
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:2337
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3340
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2410
static void DispatchRightClickEvent(Window *w, int x, int y)
Dispatch right mouse-button click in window.
Definition window.cpp:764
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:2829
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2507
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:3097
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:1962
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3372
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1986
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:3046
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1155
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1883
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1861
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:3204
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:2748
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3188
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:3328
Window functions not directly related to making/drawing windows.
@ Construction
This window is used for construction; close it whenever changing company.
Definition window_gui.h:153
@ NoClose
This window can't be interactively closed.
Definition window_gui.h:156
@ NoFocus
This window won't get focus/make any other window lose focus when click.
Definition window_gui.h:155
@ Modal
The window is a modal child of some other window, meaning the parent is 'inactive'.
Definition window_gui.h:154
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:424
@ SizingLeft
Window is being resized towards the left.
Definition window_gui.h:231
@ DisableVpScroll
Window does not do autoscroll,.
Definition window_gui.h:234
@ Highlighted
Window has a widget that has a highlight.
Definition window_gui.h:236
@ Centred
Window is centered and shall stay centered after ReInit.
Definition window_gui.h:237
@ Dragging
Window is being dragged.
Definition window_gui.h:229
@ SizingRight
Window is being resized towards the right.
Definition window_gui.h:230
@ WhiteBorder
Window white border counter bit mask.
Definition window_gui.h:235
@ Timeout
Window timeout counter.
Definition window_gui.h:227
@ Sticky
Window is made sticky by user.
Definition window_gui.h:233
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
@ 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.
Definition zoom_type.h:23