OpenTTD Source 20260218-master-g2123fca5ea
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
166static bool DescSorter(WindowDesc * const &a, WindowDesc * const &b)
167{
168 return a->ini_key < b->ini_key;
169}
170
175{
176 /* Sort the stuff to get a nice ini file on first write */
177 std::sort(_window_descs->begin(), _window_descs->end(), DescSorter);
178
179 IniFile ini;
181 for (WindowDesc *wd : *_window_descs) {
182 if (wd->ini_key.empty()) continue;
183 IniSaveWindowSettings(ini, wd->ini_key, wd);
184 }
186}
187
192{
193 if (this->nested_root != nullptr && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != nullptr) {
194 if (this->window_desc.pref_sticky) this->flags.Set(WindowFlag::Sticky);
195 } else {
196 /* There is no stickybox; clear the preference in case someone tried to be funny */
197 this->window_desc.pref_sticky = false;
198 }
199}
200
210int Window::GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height) const
211{
212 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
213 if (line_height < 0) line_height = wid->resize_y;
214 if (clickpos < wid->pos_y + padding) return INT_MAX;
215 return (clickpos - wid->pos_y - padding) / line_height;
216}
217
222{
223 for (auto &pair : this->widget_lookup) {
224 NWidgetBase *nwid = pair.second;
225 if (nwid->IsHighlighted()) {
226 nwid->SetHighlighted(TC_INVALID);
227 nwid->SetDirty(this);
228 }
229 }
230
231 this->flags.Reset(WindowFlag::Highlighted);
232}
233
239void Window::SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
240{
241 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
242 if (nwid == nullptr) return;
243
244 nwid->SetHighlighted(highlighted_colour);
245 nwid->SetDirty(this);
246
247 if (highlighted_colour != TC_INVALID) {
248 /* If we set a highlight, the window has a highlight */
250 } else {
251 /* If we disable a highlight, check all widgets if anyone still has a highlight */
252 bool valid = false;
253 for (const auto &pair : this->widget_lookup) {
254 nwid = pair.second;
255 if (!nwid->IsHighlighted()) continue;
256
257 valid = true;
258 }
259 /* If nobody has a highlight, disable the flag on the window */
260 if (!valid) this->flags.Reset(WindowFlag::Highlighted);
261 }
262}
263
269bool Window::IsWidgetHighlighted(WidgetID widget_index) const
270{
271 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
272 if (nwid == nullptr) return false;
273
274 return nwid->IsHighlighted();
275}
276
285void Window::OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
286{
287 if (widget < 0) return;
288
289 /* Many dropdown selections depend on the position of the main toolbar,
290 * so if it doesn't exist (e.g. the end screen has appeared), just skip the instant close behaviour. */
291 if (instant_close && FindWindowById(WC_MAIN_TOOLBAR, 0) != nullptr) {
292 /* Send event for selected option if we're still
293 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
294 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
295 this->OnDropdownSelect(widget, index, click_result);
296 }
297 }
298
299 /* Raise the dropdown button */
300 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
301 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
303 } else {
304 this->RaiseWidget(widget);
305 }
306 this->SetWidgetDirty(widget);
307}
308
315{
316 return this->GetWidget<NWidgetScrollbar>(widnum);
317}
318
325{
326 return this->GetWidget<NWidgetScrollbar>(widnum);
327}
328
335{
336 auto query = this->querystrings.find(widnum);
337 return query != this->querystrings.end() ? query->second : nullptr;
338}
339
346{
347 auto query = this->querystrings.find(widnum);
348 return query != this->querystrings.end() ? query->second : nullptr;
349}
350
355{
356 for (auto &qs : this->querystrings) {
357 qs.second->text.UpdateSize();
358 }
359}
360
365/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
366{
367 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
368 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
369 }
370
371 return nullptr;
372}
373
378/* virtual */ Point Window::GetCaretPosition() const
379{
380 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
381 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
382 }
383
384 Point pt = {0, 0};
385 return pt;
386}
387
394/* virtual */ Rect Window::GetTextBoundingRect(size_t from, size_t to) const
395{
396 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
397 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
398 }
399
400 Rect r = {0, 0, 0, 0};
401 return r;
402}
403
409/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
410{
411 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
412 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
413 }
414
415 return -1;
416}
417
423{
424 if (_focused_window == w) return;
425
426 /* Don't focus a tooltip */
427 if (w != nullptr && w->window_class == WC_TOOLTIPS) return;
428
429 /* Invalidate focused widget */
430 if (_focused_window != nullptr) {
431 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
432 }
433
434 /* Remember which window was previously focused */
435 Window *old_focused = _focused_window;
436 _focused_window = w;
437
438 /* So we can inform it that it lost focus */
439 if (old_focused != nullptr) old_focused->OnFocusLost(false);
440 if (_focused_window != nullptr) _focused_window->OnFocus();
441}
442
449{
450 if (_focused_window == nullptr) return false;
451
452 /* The console does not have an edit box so a special case is needed. */
453 if (_focused_window->window_class == WC_CONSOLE) return true;
454
455 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
456}
457
463{
464 return _focused_window && _focused_window->window_class == WC_CONSOLE;
465}
466
471{
472 if (this->nested_focus != nullptr) {
474
475 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
476 this->nested_focus->SetDirty(this);
477 this->nested_focus = nullptr;
478 }
479}
480
487{
488 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
489 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
490
491 if (this->nested_focus != nullptr) {
492 /* Do nothing if widget_index is already focused. */
493 if (widget == this->nested_focus) return false;
494
495 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
496 this->nested_focus->SetDirty(this);
498 }
499
500 this->nested_focus = widget;
502 return true;
503}
504
505std::string Window::GetWidgetString([[maybe_unused]] WidgetID widget, StringID stringid) const
506{
507 if (stringid == STR_NULL) return {};
508 return GetString(stringid);
509}
510
515{
516 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxGainedFocus();
517}
518
523{
524 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
525}
526
531void Window::RaiseButtons(bool autoraise)
532{
533 for (auto &pair : this->widget_lookup) {
534 WidgetType type = pair.second->type;
535 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
536 if (wid != nullptr && ((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
537 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
538 wid->SetLowered(false);
539 wid->SetDirty(this);
540 }
541 }
542
543 /* Special widgets without widget index */
544 {
545 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
546 if (wid != nullptr) {
547 wid->SetLowered(false);
548 wid->SetDirty(this);
549 }
550 }
551}
552
557void Window::SetWidgetDirty(WidgetID widget_index) const
558{
559 /* Sometimes this function is called before the window is even fully initialized */
560 auto it = this->widget_lookup.find(widget_index);
561 if (it == std::end(this->widget_lookup)) return;
562
563 it->second->SetDirty(this);
564}
565
572{
573 if (hotkey < 0) return ES_NOT_HANDLED;
574
575 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
576 if (nw == nullptr || nw->IsDisabled()) return ES_NOT_HANDLED;
577
578 if (nw->type == WWT_EDITBOX) {
579 if (this->IsShaded()) return ES_NOT_HANDLED;
580
581 /* Focus editbox */
582 this->SetFocusedWidget(hotkey);
583 SetFocusedWindow(this);
584 } else {
585 /* Click button */
586 this->OnClick(Point(), hotkey, 1);
587 }
588 return ES_HANDLED;
589}
590
597{
598 /* Button click for this widget may already have been handled. */
599 if (this->IsWidgetLowered(widget) && this->timeout_timer == TIMEOUT_DURATION) return;
600
601 this->LowerWidget(widget);
602 this->SetTimeout();
603 this->SetWidgetDirty(widget);
604 SndClickBeep();
605}
606
607static void StartWindowDrag(Window *w);
608static void StartWindowSizing(Window *w, bool to_left);
609
617static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
618{
619 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
620 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
621
622 /* Allow dropdown close flag detection to work. */
624
625 bool focused_widget_changed = false;
626
627 /* If clicked on a window that previously did not have focus */
628 if (_focused_window != w) {
629 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
630 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
631 focused_widget_changed = true;
633 } else if (_focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU) {
634 /* The previously focused window was a dropdown menu, but the user clicked on another window that
635 * isn't focusable. Close the dropdown menu anyway. */
636 SetFocusedWindow(nullptr);
637 }
638 }
639
640 if (nw == nullptr) return; // exit if clicked outside of widgets
641
642 /* don't allow any interaction if the button has been disabled */
643 if (nw->IsDisabled()) return;
644
645 WidgetID widget_index = nw->GetIndex();
646
647 /* Clicked on a widget that is not disabled.
648 * So unless the clicked widget is the caption bar, change focus to this widget.
649 * Exception: In the OSK we always want the editbox to stay focused. */
650 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
651 /* focused_widget_changed is 'now' only true if the window this widget
652 * is in gained focus. In that case it must remain true, also if the
653 * local widget focus did not change. As such it's the logical-or of
654 * both changed states.
655 *
656 * If this is not preserved, then the OSK window would be opened when
657 * a user has the edit box focused and then click on another window and
658 * then back again on the edit box (to type some text).
659 */
660 focused_widget_changed |= w->SetFocusedWidget(widget_index);
661 }
662
663 /* Dropdown window of this widget was closed so don't process click this time. */
665
666 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
667
668 Point pt = { x, y };
669
670 switch (widget_type) {
671 case NWID_VSCROLLBAR:
672 case NWID_HSCROLLBAR:
673 ScrollbarClickHandler(w, nw, x, y);
674 break;
675
676 case WWT_EDITBOX: {
677 QueryString *query = w->GetQueryString(widget_index);
678 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
679 break;
680 }
681
682 case WWT_CLOSEBOX: // 'X'
683 w->Close();
684 return;
685
686 case WWT_CAPTION: // 'Title bar'
688 return;
689
690 case WWT_RESIZEBOX:
691 /* When the resize widget is on the left size of the window
692 * we assume that that button is used to resize to the left. */
693 StartWindowSizing(w, nw->pos_x < (w->width / 2));
694 nw->SetDirty(w);
695 return;
696
697 case WWT_DEFSIZEBOX: {
698 if (_ctrl_pressed) {
699 if (click_count > 1) {
700 w->window_desc.pref_width = 0;
702 } else {
705 }
706 } else {
707 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
708 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
709
710 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
711 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
712 /* dx and dy has to go by step.. calculate it.
713 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
714 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
715 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
716 ResizeWindow(w, dx, dy, false);
717 }
718
719 nw->SetLowered(true);
720 nw->SetDirty(w);
721 w->SetTimeout();
722 break;
723 }
724
725 case WWT_DEBUGBOX:
727 break;
728
729 case WWT_SHADEBOX:
730 nw->SetDirty(w);
731 w->SetShaded(!w->IsShaded());
732 return;
733
734 case WWT_STICKYBOX:
736 nw->SetDirty(w);
738 return;
739
740 default:
741 break;
742 }
743
744 /* Widget has no index, so the window is not interested in it. */
745 if (widget_index < 0) return;
746
747 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
748 if (w->IsWidgetHighlighted(widget_index)) {
749 w->SetWidgetHighlight(widget_index, TC_INVALID);
750 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
751 }
752
753 w->OnClick(pt, widget_index, click_count);
754}
755
762static void DispatchRightClickEvent(Window *w, int x, int y)
763{
764 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
765 if (wid == nullptr) return;
766
767 Point pt = { x, y };
768
769 /* No widget to handle, or the window is not interested in it. */
770 if (wid->GetIndex() >= 0) {
771 if (w->OnRightClick(pt, wid->GetIndex())) return;
772 }
773
774 /* Right-click close is enabled and there is a closebox. */
775 if (_settings_client.gui.right_click_wnd_close == RightClickClose::Yes && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
776 w->Close();
777 } else if (_settings_client.gui.right_click_wnd_close == RightClickClose::YesExceptSticky && !w->flags.Test(WindowFlag::Sticky) && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
778 /* Right-click close is enabled, but excluding sticky windows. */
779 w->Close();
780 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
781 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_RIGHT_CLICK);
782 }
783}
784
791static void DispatchHoverEvent(Window *w, int x, int y)
792{
793 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
794
795 /* No widget to handle */
796 if (wid == nullptr) return;
797
798 Point pt = { x, y };
799
800 /* Show the tooltip if there is any */
801 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
802 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_HOVER);
803 return;
804 }
805
806 /* Widget has no index, so the window is not interested in it. */
807 if (wid->GetIndex() < 0) return;
808
809 w->OnHover(pt, wid->GetIndex());
810}
811
819static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
820{
821 if (nwid == nullptr) return;
822
823 /* Using wheel on caption/shade-box shades or unshades the window. */
824 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
825 w->SetShaded(wheel < 0);
826 return;
827 }
828
829 /* Wheeling a vertical scrollbar. */
830 if (nwid->type == NWID_VSCROLLBAR) {
831 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
832 if (sb->GetCount() > sb->GetCapacity()) {
833 if (sb->UpdatePosition(wheel)) {
834 w->OnScrollbarScroll(nwid->GetIndex());
835 w->SetDirty();
836 }
837 }
838 return;
839 }
840
841 /* Scroll the widget attached to the scrollbar. */
842 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
843 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
844 if (sb->UpdatePosition(wheel)) {
846 w->SetDirty();
847 }
848 }
849}
850
856static bool MayBeShown(const Window *w)
857{
858 /* If we're not modal, everything is okay. */
859 if (!HasModalProgress()) return true;
860
861 switch (w->window_class) {
862 case WC_MAIN_WINDOW:
863 case WC_MODAL_PROGRESS:
865 return true;
866
867 default:
868 return false;
869 }
870}
871
884static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
885{
887 ++it;
888 for (; !it.IsEnd(); ++it) {
889 const Window *v = *it;
890 if (MayBeShown(v) &&
891 right > v->left &&
892 bottom > v->top &&
893 left < v->left + v->width &&
894 top < v->top + v->height) {
895 /* v and rectangle intersect with each other */
896 int x;
897
898 if (left < (x = v->left)) {
899 DrawOverlappedWindow(w, left, top, x, bottom);
900 DrawOverlappedWindow(w, x, top, right, bottom);
901 return;
902 }
903
904 if (right > (x = v->left + v->width)) {
905 DrawOverlappedWindow(w, left, top, x, bottom);
906 DrawOverlappedWindow(w, x, top, right, bottom);
907 return;
908 }
909
910 if (top < (x = v->top)) {
911 DrawOverlappedWindow(w, left, top, right, x);
912 DrawOverlappedWindow(w, left, x, right, bottom);
913 return;
914 }
915
916 if (bottom > (x = v->top + v->height)) {
917 DrawOverlappedWindow(w, left, top, right, x);
918 DrawOverlappedWindow(w, left, x, right, bottom);
919 return;
920 }
921
922 return;
923 }
924 }
925
926 /* Setup blitter, and dispatch a repaint event to window *wz */
927 DrawPixelInfo *dp = _cur_dpi;
928 dp->width = right - left;
929 dp->height = bottom - top;
930 dp->left = left - w->left;
931 dp->top = top - w->top;
932 dp->pitch = _screen.pitch;
933 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
934 dp->zoom = ZoomLevel::Min;
935 w->OnPaint();
936}
937
946void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
947{
948 DrawPixelInfo bk;
949 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
950
951 for (Window *w : Window::IterateFromBack()) {
952 if (MayBeShown(w) &&
953 right > w->left &&
954 bottom > w->top &&
955 left < w->left + w->width &&
956 top < w->top + w->height) {
957 /* Window w intersects with the rectangle => needs repaint */
958 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));
959 }
960 }
961}
962
968{
969 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
970}
971
979void Window::ReInit(int rx, int ry, bool reposition)
980{
981 this->SetDirty(); // Mark whole current window as dirty.
982
983 /* Save current size. */
984 int window_width = this->width * _gui_scale / this->scale;
985 int window_height = this->height * _gui_scale / this->scale;
986 this->scale = _gui_scale;
987
988 this->OnInit();
989 /* Re-initialize window smallest size. */
990 this->nested_root->SetupSmallestSize(this);
991 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
992 this->width = this->nested_root->smallest_x;
993 this->height = this->nested_root->smallest_y;
994 this->resize.step_width = this->nested_root->resize_x;
995 this->resize.step_height = this->nested_root->resize_y;
996
997 /* Resize as close to the original size + requested resize as possible. */
998 window_width = std::max(window_width + rx, this->width);
999 window_height = std::max(window_height + ry, this->height);
1000 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
1001 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
1002 /* dx and dy has to go by step.. calculate it.
1003 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1004 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1005 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1006
1007 if (reposition) {
1008 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1009 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1010 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1011 }
1012
1013 ResizeWindow(this, dx, dy, true, false);
1014 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1015}
1016
1022void Window::SetShaded(bool make_shaded)
1023{
1024 if (this->shade_select == nullptr) return;
1025
1026 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1027 if (this->shade_select->shown_plane != desired) {
1028 if (make_shaded) {
1029 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1030 this->unshaded_size.width = this->width;
1031 this->unshaded_size.height = this->height;
1032 this->shade_select->SetDisplayedPlane(desired);
1033 this->ReInit(0, -this->height);
1034 } else {
1035 this->shade_select->SetDisplayedPlane(desired);
1036 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1037 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1038 this->ReInit(dx, dy);
1039 }
1040 }
1041}
1042
1048Window *Window::FindChildWindow(WindowClass wc) const
1049{
1050 for (Window *v : Window::Iterate()) {
1051 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1052 }
1053
1054 return nullptr;
1055}
1056
1064{
1065 for (Window *v : Window::Iterate()) {
1066 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1067 }
1068
1069 return nullptr;
1070}
1071
1076void Window::CloseChildWindows(WindowClass wc) const
1077{
1078 Window *child = this->FindChildWindow(wc);
1079 while (child != nullptr) {
1080 child->Close();
1081 child = this->FindChildWindow(wc);
1082 }
1083}
1084
1085
1091void Window::CloseChildWindowById(WindowClass wc, WindowNumber number) const
1092{
1093 Window *child = this->FindChildWindowById(wc, number);
1094 while (child != nullptr) {
1095 child->Close();
1096 child = this->FindChildWindowById(wc, number);
1097 }
1098}
1099
1104void Window::Close([[maybe_unused]] int data)
1105{
1106 /* Don't close twice. */
1107 if (*this->z_position == nullptr) return;
1108
1109 *this->z_position = nullptr;
1110
1111 if (_thd.window_class == this->window_class &&
1112 _thd.window_number == this->window_number) {
1114 }
1115
1116 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1117 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1118
1119 /* We can't scroll the window when it's closed. */
1120 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1121
1122 /* Make sure we don't try to access non-existing query strings. */
1123 this->querystrings.clear();
1124
1125 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1126 if (_focused_window == this) {
1127 this->OnFocusLost(true);
1128 _focused_window = nullptr;
1129 }
1130
1131 this->CloseChildWindows();
1132
1133 this->SetDirty();
1134
1135 Window::closed_windows.push_back(this);
1136}
1137
1142{
1143 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1144 assert(*this->z_position == nullptr);
1145}
1146
1153Window *FindWindowById(WindowClass cls, WindowNumber number)
1154{
1155 for (Window *w : Window::Iterate()) {
1156 if (w->window_class == cls && w->window_number == number) return w;
1157 }
1158
1159 return nullptr;
1160}
1161
1168Window *FindWindowByClass(WindowClass cls)
1169{
1170 for (Window *w : Window::Iterate()) {
1171 if (w->window_class == cls) return w;
1172 }
1173
1174 return nullptr;
1175}
1176
1183{
1185 assert(w != nullptr);
1186 return w;
1187}
1188
1196void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1197{
1198 Window *w = FindWindowById(cls, number);
1199 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1200 w->Close(data);
1201 }
1202}
1203
1209void CloseWindowByClass(WindowClass cls, int data)
1210{
1211 /* Note: the container remains stable, even when deleting windows. */
1212 for (Window *w : Window::Iterate()) {
1213 if (w->window_class == cls) {
1214 w->Close(data);
1215 }
1216 }
1217}
1218
1225void CloseCompanyWindows(CompanyID id)
1226{
1227 /* Note: the container remains stable, even when deleting windows. */
1228 for (Window *w : Window::Iterate()) {
1229 if (w->owner == id) {
1230 w->Close();
1231 }
1232 }
1233
1234 /* Also delete the company specific windows that don't have a company-colour. */
1236}
1237
1245void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1246{
1247 for (Window *w : Window::Iterate()) {
1248 if (w->owner != old_owner) continue;
1249
1250 switch (w->window_class) {
1251 case WC_COMPANY_COLOUR:
1252 case WC_FINANCES:
1253 case WC_STATION_LIST:
1254 case WC_TRAINS_LIST:
1255 case WC_ROADVEH_LIST:
1256 case WC_SHIPS_LIST:
1257 case WC_AIRCRAFT_LIST:
1258 case WC_BUY_COMPANY:
1259 case WC_COMPANY:
1261 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().
1262 continue;
1263
1264 default:
1265 w->owner = new_owner;
1266 break;
1267 }
1268 }
1269}
1270
1271static void BringWindowToFront(Window *w, bool dirty = true);
1272
1281{
1282 Window *w = FindWindowById(cls, number);
1283
1284 if (w != nullptr) {
1285 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1286
1287 w->SetWhiteBorder();
1289 w->SetDirty();
1290 }
1291
1292 return w;
1293}
1294
1295static inline bool IsVitalWindow(const Window *w)
1296{
1297 switch (w->window_class) {
1298 case WC_MAIN_TOOLBAR:
1299 case WC_STATUS_BAR:
1300 case WC_NEWS_WINDOW:
1302 return true;
1303
1304 default:
1305 return false;
1306 }
1307}
1308
1317static uint GetWindowZPriority(WindowClass wc)
1318{
1319 assert(wc != WC_INVALID);
1320
1321 uint z_priority = 0;
1322
1323 switch (wc) {
1324 case WC_TOOLTIPS:
1325 ++z_priority;
1326 [[fallthrough]];
1327
1328 case WC_ERRMSG:
1330 ++z_priority;
1331 [[fallthrough]];
1332
1333 case WC_ENDSCREEN:
1334 ++z_priority;
1335 [[fallthrough]];
1336
1337 case WC_HIGHSCORE:
1338 ++z_priority;
1339 [[fallthrough]];
1340
1341 case WC_DROPDOWN_MENU:
1342 ++z_priority;
1343 [[fallthrough]];
1344
1345 case WC_MAIN_TOOLBAR:
1346 case WC_STATUS_BAR:
1347 ++z_priority;
1348 [[fallthrough]];
1349
1350 case WC_OSK:
1351 ++z_priority;
1352 [[fallthrough]];
1353
1354 case WC_QUERY_STRING:
1356 ++z_priority;
1357 [[fallthrough]];
1358
1360 case WC_MODAL_PROGRESS:
1362 case WC_SAVE_PRESET:
1363 ++z_priority;
1364 [[fallthrough]];
1365
1367 case WC_SAVELOAD:
1368 case WC_GAME_OPTIONS:
1369 case WC_CUSTOM_CURRENCY:
1370 case WC_NETWORK_WINDOW:
1371 case WC_GRF_PARAMETERS:
1372 case WC_SCRIPT_LIST:
1373 case WC_SCRIPT_SETTINGS:
1374 case WC_TEXTFILE:
1375 ++z_priority;
1376 [[fallthrough]];
1377
1378 case WC_CONSOLE:
1379 ++z_priority;
1380 [[fallthrough]];
1381
1382 case WC_NEWS_WINDOW:
1383 ++z_priority;
1384 [[fallthrough]];
1385
1386 default:
1387 ++z_priority;
1388 [[fallthrough]];
1389
1390 case WC_MAIN_WINDOW:
1391 return z_priority;
1392 }
1393}
1394
1401static void BringWindowToFront(Window *w, bool dirty)
1402{
1403 auto priority = GetWindowZPriority(w->window_class);
1404 WindowList::iterator dest = _z_windows.begin();
1405 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1406
1407 if (dest != w->z_position) {
1408 _z_windows.splice(dest, _z_windows, w->z_position);
1409 }
1410
1411 if (dirty) w->SetDirty();
1412}
1413
1421{
1422 /* Set up window properties; some of them are needed to set up smallest size below */
1423 this->window_class = this->window_desc.cls;
1424 this->SetWhiteBorder();
1425 if (this->window_desc.default_pos == WDP_CENTER) this->flags.Set(WindowFlag::Centred);
1426 this->owner = INVALID_OWNER;
1427 this->nested_focus = nullptr;
1428 this->window_number = window_number;
1429
1430 this->OnInit();
1431 /* Initialize smallest size. */
1432 this->nested_root->SetupSmallestSize(this);
1433 /* Initialize to smallest size. */
1434 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1435
1436 /* Further set up window properties,
1437 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1438 this->resize.step_width = this->nested_root->resize_x;
1439 this->resize.step_height = this->nested_root->resize_y;
1440
1441 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1442 * (so we don't interrupt typing) unless the new window has a text box. */
1443 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1444 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1445 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1446
1447 /* Insert the window into the correct location in the z-ordering. */
1448 BringWindowToFront(this, false);
1449}
1450
1458void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1459{
1460 this->left = x;
1461 this->top = y;
1462 this->width = sm_width;
1463 this->height = sm_height;
1464}
1465
1477void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1478{
1479 if (allow_resize) {
1480 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1481 def_height = std::max(def_height, this->height);
1482 /* Try to make windows smaller when our window is too small.
1483 * w->(width|height) is normally the same as min_(width|height),
1484 * but this way the GUIs can be made a little more dynamic;
1485 * one can use the same spec for multiple windows and those
1486 * can then determine the real minimum size of the window. */
1487 if (this->width != def_width || this->height != def_height) {
1488 /* Think about the overlapping toolbars when determining the minimum window size */
1489 int free_height = _screen.height;
1490 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1491 if (wt != nullptr) free_height -= wt->height;
1493 if (wt != nullptr) free_height -= wt->height;
1494
1495 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1496 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1497
1498 /* X and Y has to go by step.. calculate it.
1499 * The cast to int is necessary else x/y are implicitly cast to
1500 * unsigned int, which won't work. */
1501 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1502 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1503
1504 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1505 /* ResizeWindow() calls this->OnResize(). */
1506 } else {
1507 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1508 this->OnResize();
1509 }
1510 }
1511
1512 int nx = this->left;
1513 int ny = this->top;
1514
1515 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1516
1517 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1518 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1519 nx = std::max(nx, 0);
1520
1521 if (this->viewport != nullptr) {
1522 this->viewport->left += nx - this->left;
1523 this->viewport->top += ny - this->top;
1524 }
1525 this->left = nx;
1526 this->top = ny;
1527
1528 this->SetDirty();
1529}
1530
1543static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1544{
1545 int right = width + left;
1546 int bottom = height + top;
1547
1548 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1549
1550 /* Make sure it is not obscured by any window. */
1551 for (const Window *w : Window::Iterate()) {
1552 if (w->window_class == WC_MAIN_WINDOW) continue;
1553
1554 if (right > w->left &&
1555 w->left + w->width > left &&
1556 bottom > w->top &&
1557 w->top + w->height > top) {
1558 return false;
1559 }
1560 }
1561
1562 pos.x = left;
1563 pos.y = top;
1564 return true;
1565}
1566
1579static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1580{
1581 bool rtl = _current_text_dir == TD_RTL;
1582
1583 /* Left part of the rectangle may be at most 1/4 off-screen,
1584 * right part of the rectangle may be at most 1/2 off-screen
1585 */
1586 if (rtl) {
1587 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1588 } else {
1589 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1590 }
1591
1592 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1593 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1594
1595 /* Make sure it is not obscured by any window. */
1596 for (const Window *w : Window::Iterate()) {
1597 if (w->window_class == WC_MAIN_WINDOW) continue;
1598
1599 if (left + width > w->left &&
1600 w->left + w->width > left &&
1601 top + height > w->top &&
1602 w->top + w->height > top) {
1603 return false;
1604 }
1605 }
1606
1607 pos.x = left;
1608 pos.y = top;
1609 return true;
1610}
1611
1618static Point GetAutoPlacePosition(int width, int height)
1619{
1620 Point pt;
1621
1622 bool rtl = _current_text_dir == TD_RTL;
1623
1624 /* First attempt, try top-left of the screen */
1625 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1626 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1627 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1628
1629 /* Second attempt, try around all existing windows.
1630 * The new window must be entirely on-screen, and not overlap with an existing window.
1631 * Eight starting points are tried, two at each corner.
1632 */
1633 for (const Window *w : Window::Iterate()) {
1634 if (w->window_class == WC_MAIN_WINDOW) continue;
1635
1636 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1637 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1638 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1639 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1640 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1641 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1642 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1643 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1644 }
1645
1646 /* Third attempt, try around all existing windows.
1647 * The new window may be partly off-screen, and must not overlap with an existing window.
1648 * Only four starting points are tried.
1649 */
1650 for (const Window *w : Window::Iterate()) {
1651 if (w->window_class == WC_MAIN_WINDOW) continue;
1652
1653 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1654 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1655 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1656 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1657 }
1658
1659 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1660 * of the closebox
1661 */
1662 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1663 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1664 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1665
1666restart:
1667 for (const Window *w : Window::Iterate()) {
1668 if (w->left == left && w->top == top) {
1669 left += offset_x;
1670 top += offset_y;
1671 goto restart;
1672 }
1673 }
1674
1675 pt.x = left;
1676 pt.y = top;
1677 return pt;
1678}
1679
1687{
1688 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1689 assert(w != nullptr);
1690 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1691 return pt;
1692}
1693
1703{
1704 Point pt = GetToolbarAlignedWindowPosition(window_width);
1706 if (w != nullptr && w->top == pt.y && !_settings_client.gui.link_terraform_toolbar) {
1707 pt.x = w->left + (_current_text_dir == TD_RTL ? w->width : - window_width);
1708 }
1709 return pt;
1710}
1711
1729static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1730{
1731 Point pt;
1732 const Window *w;
1733
1734 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1735 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1736
1737 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1738 bool rtl = _current_text_dir == TD_RTL;
1739 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1740 pt.x = w->left + (rtl ? w->width - default_width : 0);
1741 pt.y = w->top + w->height;
1742 return pt;
1743 } else {
1744 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1745 * - Y position: closebox of parent + closebox of child + statusbar
1746 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1747 */
1748 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1749 if (w->top + 3 * indent_y < _screen.height) {
1750 pt.y = w->top + indent_y;
1751 int indent_close = NWidgetLeaf::closebox_dimension.width;
1752 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1753 if (_current_text_dir == TD_RTL) {
1754 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1755 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1756 } else {
1757 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1758 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1759 }
1760 }
1761 }
1762 }
1763
1764 switch (desc.default_pos) {
1765 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1766 return GetToolbarAlignedWindowPosition(default_width);
1767
1768 case WDP_AUTO: // Find a good automatic position for the window
1769 return GetAutoPlacePosition(default_width, default_height);
1770
1771 case WDP_CENTER: // Centre the window horizontally
1772 pt.x = (_screen.width - default_width) / 2;
1773 pt.y = (_screen.height - default_height) / 2;
1774 break;
1775
1776 case WDP_MANUAL:
1777 pt.x = 0;
1778 pt.y = 0;
1779 break;
1780
1781 default:
1782 NOT_REACHED();
1783 }
1784
1785 return pt;
1786}
1787
1788/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1789{
1790 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1791}
1792
1800{
1801 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1802 this->nested_root->FillWidgetLookup(this->widget_lookup);
1803}
1804
1810{
1811 this->InitializeData(window_number);
1812 this->ApplyDefaults();
1813 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1814 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1815 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1816}
1817
1823{
1824 this->CreateNestedTree();
1825 this->FinishInitNested(window_number);
1826}
1827
1833{
1834 this->z_position = _z_windows.insert(_z_windows.end(), this);
1835}
1836
1845{
1846 for (Window *w : Window::IterateFromFront()) {
1847 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1848 return w;
1849 }
1850 }
1851
1852 return nullptr;
1853}
1854
1859{
1860 IConsoleClose();
1861
1862 _focused_window = nullptr;
1863 _mouseover_last_w = nullptr;
1864 _last_scroll_window = nullptr;
1865 _scrolling_viewport = false;
1866 _mouse_hovering = false;
1867
1869 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1870 NWidgetScrollbar::InvalidateDimensionCache();
1871
1873
1875}
1876
1881{
1883
1884 for (Window *w : Window::Iterate()) w->Close();
1885
1887
1888 assert(_z_windows.empty());
1889}
1890
1895{
1898 _thd.Reset();
1899}
1900
1901static void DecreaseWindowCounters()
1902{
1903 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1904
1905 for (Window *w : Window::Iterate()) {
1906 if (_scroller_click_timeout == 0) {
1907 /* Unclick scrollbar buttons if they are pressed. */
1908 for (auto &pair : w->widget_lookup) {
1909 NWidgetBase *nwid = pair.second;
1910 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1911 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1912 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1915 sb->SetDirty(w);
1916 }
1917 }
1918 }
1919 }
1920
1921 /* Handle editboxes */
1922 for (auto &pair : w->querystrings) {
1923 pair.second->HandleEditBox(w, pair.first);
1924 }
1925
1926 w->OnMouseLoop();
1927 }
1928
1929 for (Window *w : Window::Iterate()) {
1930 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1932
1933 w->OnTimeout();
1934 w->RaiseButtons(true);
1935 }
1936 }
1937}
1938
1939static void HandlePlacePresize()
1940{
1941 if (_special_mouse_mode != WSM_PRESIZE) return;
1942
1943 Window *w = _thd.GetCallbackWnd();
1944 if (w == nullptr) return;
1945
1946 Point pt = GetTileBelowCursor();
1947 if (pt.x == -1) {
1948 _thd.selend.x = -1;
1949 return;
1950 }
1951
1952 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1953}
1954
1960{
1962
1963 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1964
1965 Window *w = _thd.GetCallbackWnd();
1966 if (w != nullptr) {
1967 /* Send an event in client coordinates. */
1968 Point pt;
1969 pt.x = _cursor.pos.x - w->left;
1970 pt.y = _cursor.pos.y - w->top;
1971 if (_left_button_down) {
1972 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1973 } else {
1974 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1975 }
1976 }
1977
1978 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1979 return ES_HANDLED;
1980}
1981
1983static void HandleMouseOver()
1984{
1985 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1986
1987 /* We changed window, put an OnMouseOver event to the last window */
1988 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1989 /* Reset mouse-over coordinates of previous window */
1990 Point pt = { -1, -1 };
1991 _mouseover_last_w->OnMouseOver(pt, 0);
1992 }
1993
1994 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1996
1997 if (w != nullptr) {
1998 /* send an event in client coordinates. */
1999 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2000 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
2001 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
2002 }
2003}
2004
2010
2021static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
2022{
2023 if (v == nullptr) return;
2024
2025 const int min_visible = rect.Height();
2026
2027 int v_bottom = v->top + v->height - 1;
2028 int v_right = v->left + v->width - 1;
2029 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2030
2031 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2032 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2033
2034 /* Vertically, the rectangle is hidden behind v. */
2035 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2036 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2037 return;
2038 }
2039 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2040 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2041 return;
2042 }
2043
2044 /* Horizontally also hidden, force movement to a safe area. */
2045 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2046 *nx = v->left - min_visible - rect.left;
2047 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2048 *nx = v_right + min_visible - rect.right;
2049 } else {
2050 *ny = safe_y;
2051 }
2052}
2053
2061static void EnsureVisibleCaption(Window *w, int nx, int ny)
2062{
2063 /* Search for the title bar rectangle. */
2064 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2065 if (caption != nullptr) {
2066 const Rect caption_rect = caption->GetCurrentRect();
2067
2068 const int min_visible = caption_rect.Height();
2069
2070 /* Make sure the window doesn't leave the screen */
2071 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2072 ny = Clamp(ny, 0, _screen.height - min_visible);
2073
2074 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2075 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2076 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2077 }
2078
2079 if (w->viewport != nullptr) {
2080 w->viewport->left += nx - w->left;
2081 w->viewport->top += ny - w->top;
2082 }
2083
2084 w->left = nx;
2085 w->top = ny;
2086}
2087
2099void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2100{
2101 if (delta_x != 0 || delta_y != 0) {
2102 if (clamp_to_screen) {
2103 /* Determine the new right/bottom position. If that is outside of the bounds of
2104 * the resolution clamp it in such a manner that it stays within the bounds. */
2105 int new_right = w->left + w->width + delta_x;
2106 int new_bottom = w->top + w->height + delta_y;
2107 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2108 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2109 }
2110
2111 w->SetDirty();
2112
2113 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);
2114 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);
2115 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2116 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2117
2118 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);
2119 w->width = w->nested_root->current_x;
2120 w->height = w->nested_root->current_y;
2121 }
2122
2123 EnsureVisibleCaption(w, w->left, w->top);
2124
2125 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2126 if (schedule_resize) {
2127 w->ScheduleResize();
2128 } else {
2129 w->OnResize();
2130 }
2131 w->SetDirty();
2132}
2133
2140{
2142 return (w == nullptr) ? 0 : w->top + w->height;
2143}
2144
2151{
2153 return (w == nullptr) ? _screen.height : w->top;
2154}
2155
2156static bool _dragging_window;
2157
2163{
2164 /* Get out immediately if no window is being dragged at all. */
2165 if (!_dragging_window) return ES_NOT_HANDLED;
2166
2167 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2168 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2169
2170 /* Otherwise find the window... */
2171 for (Window *w : Window::Iterate()) {
2173 /* Stop the dragging if the left mouse button was released */
2174 if (!_left_button_down) {
2176 break;
2177 }
2178
2179 w->SetDirty();
2180
2181 int x = _cursor.pos.x + _drag_delta.x;
2182 int y = _cursor.pos.y + _drag_delta.y;
2183 int nx = x;
2184 int ny = y;
2185
2186 if (_settings_client.gui.window_snap_radius != 0) {
2187 int hsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2188 int vsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2189 int delta;
2190
2191 for (const Window *v : Window::Iterate()) {
2192 if (v == w) continue; // Don't snap at yourself
2193
2194 if (y + w->height > v->top && y < v->top + v->height) {
2195 /* Your left border <-> other right border */
2196 delta = abs(v->left + v->width - x);
2197 if (delta <= hsnap) {
2198 nx = v->left + v->width;
2199 hsnap = delta;
2200 }
2201
2202 /* Your right border <-> other left border */
2203 delta = abs(v->left - x - w->width);
2204 if (delta <= hsnap) {
2205 nx = v->left - w->width;
2206 hsnap = delta;
2207 }
2208 }
2209
2210 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2211 /* Your left border <-> other left border */
2212 delta = abs(v->left - x);
2213 if (delta <= hsnap) {
2214 nx = v->left;
2215 hsnap = delta;
2216 }
2217
2218 /* Your right border <-> other right border */
2219 delta = abs(v->left + v->width - x - w->width);
2220 if (delta <= hsnap) {
2221 nx = v->left + v->width - w->width;
2222 hsnap = delta;
2223 }
2224 }
2225
2226 if (x + w->width > v->left && x < v->left + v->width) {
2227 /* Your top border <-> other bottom border */
2228 delta = abs(v->top + v->height - y);
2229 if (delta <= vsnap) {
2230 ny = v->top + v->height;
2231 vsnap = delta;
2232 }
2233
2234 /* Your bottom border <-> other top border */
2235 delta = abs(v->top - y - w->height);
2236 if (delta <= vsnap) {
2237 ny = v->top - w->height;
2238 vsnap = delta;
2239 }
2240 }
2241
2242 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2243 /* Your top border <-> other top border */
2244 delta = abs(v->top - y);
2245 if (delta <= vsnap) {
2246 ny = v->top;
2247 vsnap = delta;
2248 }
2249
2250 /* Your bottom border <-> other bottom border */
2251 delta = abs(v->top + v->height - y - w->height);
2252 if (delta <= vsnap) {
2253 ny = v->top + v->height - w->height;
2254 vsnap = delta;
2255 }
2256 }
2257 }
2258 }
2259
2260 EnsureVisibleCaption(w, nx, ny);
2261
2262 w->SetDirty();
2263 return ES_HANDLED;
2265 /* Stop the sizing if the left mouse button was released */
2266 if (!_left_button_down) {
2269 w->SetDirty();
2270 break;
2271 }
2272
2273 /* Compute difference in pixels between cursor position and reference point in the window.
2274 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2275 */
2276 int x, y = _cursor.pos.y - _drag_delta.y;
2278 x = _drag_delta.x - _cursor.pos.x;
2279 } else {
2280 x = _cursor.pos.x - _drag_delta.x;
2281 }
2282
2283 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2284 if (w->resize.step_width == 0) x = 0;
2285 if (w->resize.step_height == 0) y = 0;
2286
2287 /* Check the resize button won't go past the bottom of the screen */
2288 if (w->top + w->height + y > _screen.height) {
2289 y = _screen.height - w->height - w->top;
2290 }
2291
2292 /* X and Y has to go by step.. calculate it.
2293 * The cast to int is necessary else x/y are implicitly cast to
2294 * unsigned int, which won't work. */
2295 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2296 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2297
2298 /* Check that we don't go below the minimum set size */
2299 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2300 x = w->nested_root->smallest_x - w->width;
2301 }
2302 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2303 y = w->nested_root->smallest_y - w->height;
2304 }
2305
2306 /* Window already on size */
2307 if (x == 0 && y == 0) return ES_HANDLED;
2308
2309 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2310 _drag_delta.y += y;
2311 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2312 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2313 w->SetDirty();
2314 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2315 /* ResizeWindow() below ensures marking new position as dirty. */
2316 } else {
2317 _drag_delta.x += x;
2318 }
2319
2320 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2321 ResizeWindow(w, x, y);
2322 return ES_HANDLED;
2323 }
2324 }
2325
2326 _dragging_window = false;
2327 return ES_HANDLED;
2328}
2329
2335{
2338 _dragging_window = true;
2339
2340 _drag_delta.x = w->left - _cursor.pos.x;
2341 _drag_delta.y = w->top - _cursor.pos.y;
2342
2344}
2345
2351static void StartWindowSizing(Window *w, bool to_left)
2352{
2355 _dragging_window = true;
2356
2357 _drag_delta.x = _cursor.pos.x;
2358 _drag_delta.y = _cursor.pos.y;
2359
2361}
2362
2368{
2369 int i;
2371 bool rtl = false;
2372
2373 if (sb->type == NWID_HSCROLLBAR) {
2374 i = _cursor.pos.x - _cursorpos_drag_start.x;
2375 rtl = _current_text_dir == TD_RTL;
2376 } else {
2377 i = _cursor.pos.y - _cursorpos_drag_start.y;
2378 }
2379
2380 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2381 if (_scroller_click_timeout == 1) {
2382 _scroller_click_timeout = 3;
2383 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2385 w->SetDirty();
2386 }
2387 }
2388 return;
2389 }
2390
2391 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2392 int range = sb->GetCount() - sb->GetCapacity();
2393 if (range <= 0) return;
2394
2395 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2396 if (rtl) pos = range - pos;
2397 if (sb->SetPosition(pos)) {
2399 w->SetDirty();
2400 }
2401}
2402
2408{
2409 for (Window *w : Window::Iterate()) {
2410 if (w->mouse_capture_widget >= 0) {
2411 /* Abort if no button is clicked any more. */
2412 if (!_left_button_down) {
2415 return ES_HANDLED;
2416 }
2417
2418 /* Handle scrollbar internally, or dispatch click event */
2420 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2422 } else {
2423 /* If cursor hasn't moved, there is nothing to do. */
2424 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2425
2426 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2427 w->OnClick(pt, w->mouse_capture_widget, 0);
2428 }
2429 return ES_HANDLED;
2430 }
2431 }
2432
2433 return ES_NOT_HANDLED;
2434}
2435
2441{
2442 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2443
2445
2446 /* When we don't have a last scroll window we are starting to scroll.
2447 * When the last scroll window and this are not the same we went
2448 * outside of the window and should not left-mouse scroll anymore. */
2449 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2450
2451 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))) {
2452 _cursor.fix_at = false;
2453 _scrolling_viewport = false;
2454 _last_scroll_window = nullptr;
2455 return ES_NOT_HANDLED;
2456 }
2457
2458 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2459 /* If the main window is following a vehicle, then first let go of it! */
2460 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
2461 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2462 return ES_NOT_HANDLED;
2463 }
2464
2465 Point delta;
2466 if (scrollwheel_scrolling) {
2467 /* We are using scrollwheels for scrolling */
2468 /* Use the integer part for movement */
2469 delta.x = static_cast<int>(_cursor.h_wheel);
2470 delta.y = static_cast<int>(_cursor.v_wheel);
2471 /* Keep the fractional part so that subtle movement is accumulated */
2472 float temp;
2473 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2474 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2475 } else {
2477 delta.x = -_cursor.delta.x;
2478 delta.y = -_cursor.delta.y;
2479 } else {
2480 delta.x = _cursor.delta.x;
2481 delta.y = _cursor.delta.y;
2482 }
2483 }
2484
2485 /* Create a scroll-event and send it to the window */
2486 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2487
2488 _cursor.delta.x = 0;
2489 _cursor.delta.y = 0;
2490 _cursor.wheel_moved = false;
2491 return ES_HANDLED;
2492}
2493
2505{
2506 bool bring_to_front = false;
2507
2508 if (w->window_class == WC_MAIN_WINDOW ||
2509 IsVitalWindow(w) ||
2510 w->window_class == WC_TOOLTIPS ||
2512 return true;
2513 }
2514
2515 /* Use unshaded window size rather than current size for shaded windows. */
2516 int w_width = w->width;
2517 int w_height = w->height;
2518 if (w->IsShaded()) {
2519 w_width = w->unshaded_size.width;
2520 w_height = w->unshaded_size.height;
2521 }
2522
2524 ++it;
2525 for (; !it.IsEnd(); ++it) {
2526 Window *u = *it;
2527 /* A modal child will prevent the activation of the parent window */
2529 u->SetWhiteBorder();
2530 u->SetDirty();
2531 return false;
2532 }
2533
2534 if (u->window_class == WC_MAIN_WINDOW ||
2535 IsVitalWindow(u) ||
2536 u->window_class == WC_TOOLTIPS ||
2538 continue;
2539 }
2540
2541 /* Window sizes don't interfere, leave z-order alone */
2542 if (w->left + w_width <= u->left ||
2543 u->left + u->width <= w->left ||
2544 w->top + w_height <= u->top ||
2545 u->top + u->height <= w->top) {
2546 continue;
2547 }
2548
2549 bring_to_front = true;
2550 }
2551
2552 if (bring_to_front) BringWindowToFront(w);
2553 return true;
2554}
2555
2564EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2565{
2566 QueryString *query = this->GetQueryString(wid);
2567 if (query == nullptr) return ES_NOT_HANDLED;
2568
2569 int action = QueryString::ACTION_NOTHING;
2570
2571 switch (query->text.HandleKeyPress(key, keycode)) {
2572 case HKPR_EDITING:
2573 this->SetWidgetDirty(wid);
2574 this->OnEditboxChanged(wid);
2575 break;
2576
2577 case HKPR_CURSOR:
2578 this->SetWidgetDirty(wid);
2579 /* For the OSK also invalidate the parent window */
2580 if (this->window_class == WC_OSK) this->InvalidateData();
2581 break;
2582
2583 case HKPR_CONFIRM:
2584 if (this->window_class == WC_OSK) {
2585 this->OnClick(Point(), WID_OSK_OK, 1);
2586 } else if (query->ok_button >= 0) {
2587 this->OnClick(Point(), query->ok_button, 1);
2588 } else {
2589 action = query->ok_button;
2590 }
2591 break;
2592
2593 case HKPR_CANCEL:
2594 if (this->window_class == WC_OSK) {
2595 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2596 } else if (query->cancel_button >= 0) {
2597 this->OnClick(Point(), query->cancel_button, 1);
2598 } else {
2599 action = query->cancel_button;
2600 }
2601 break;
2602
2603 case HKPR_NOT_HANDLED:
2604 return ES_NOT_HANDLED;
2605
2606 default: break;
2607 }
2608
2609 switch (action) {
2611 this->UnfocusFocusedWidget();
2612 break;
2613
2615 if (query->text.GetText().empty()) {
2616 /* If already empty, unfocus instead */
2617 this->UnfocusFocusedWidget();
2618 } else {
2619 query->text.DeleteAll();
2620 this->SetWidgetDirty(wid);
2621 this->OnEditboxChanged(wid);
2622 }
2623 break;
2624
2625 default:
2626 break;
2627 }
2628
2629 return ES_HANDLED;
2630}
2631
2636void HandleToolbarHotkey(int hotkey)
2637{
2638 assert(HasModalProgress() || IsLocalCompany());
2639
2641 if (w != nullptr) {
2642 if (w->window_desc.hotkeys != nullptr) {
2643 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2644 }
2645 }
2646}
2647
2653void HandleKeypress(uint keycode, char32_t key)
2654{
2655 /* World generation is multithreaded and messes with companies.
2656 * But there is no company related window open anyway, so _current_company is not used. */
2657 assert(HasModalProgress() || IsLocalCompany());
2658
2659 /*
2660 * The Unicode standard defines an area called the private use area. Code points in this
2661 * area are reserved for private use and thus not portable between systems. For instance,
2662 * Apple defines code points for the arrow keys in this area, but these are only printable
2663 * on a system running OS X. We don't want these keys to show up in text fields and such,
2664 * and thus we have to clear the unicode character when we encounter such a key.
2665 */
2666 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2667
2668 /*
2669 * If both key and keycode is zero, we don't bother to process the event.
2670 */
2671 if (key == 0 && keycode == 0) return;
2672
2673 /* Check if the focused window has a focused editbox */
2674 if (EditBoxInGlobalFocus()) {
2675 /* All input will in this case go to the focused editbox */
2676 if (_focused_window->window_class == WC_CONSOLE) {
2677 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2678 } else {
2679 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2680 }
2681 }
2682
2683 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2684 for (Window *w : Window::IterateFromFront()) {
2685 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2686 if (w->window_desc.hotkeys != nullptr) {
2687 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2688 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2689 }
2690 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2691 }
2692
2694 /* When there is no toolbar w is null, check for that */
2695 if (w != nullptr) {
2696 if (w->window_desc.hotkeys != nullptr) {
2697 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2698 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2699 }
2700 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2701 }
2702
2703 HandleGlobalHotkeys(key, keycode);
2704}
2705
2710{
2711 /* Call the event, start with the uppermost window. */
2712 for (Window *w : Window::IterateFromFront()) {
2713 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2714 }
2715}
2716
2726/* 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)
2727{
2728 QueryString *query = this->GetQueryString(wid);
2729 if (query == nullptr) return;
2730
2731 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2732 this->SetWidgetDirty(wid);
2733 this->OnEditboxChanged(wid);
2734 }
2735}
2736
2745void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2746{
2747 if (!EditBoxInGlobalFocus()) return;
2748
2749 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2750}
2751
2756static void HandleAutoscroll()
2757{
2758 if (_game_mode == GM_MENU || HasModalProgress()) return;
2759 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
2760 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2761
2762 int x = _cursor.pos.x;
2763 int y = _cursor.pos.y;
2764 Window *w = FindWindowFromPt(x, y);
2765 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2766 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
2767
2768 Viewport *vp = IsPtInWindowViewport(w, x, y);
2769 if (vp == nullptr) return;
2770
2771 x -= vp->left;
2772 y -= vp->top;
2773
2774 /* here allows scrolling in both x and y axis */
2775 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2776 static const int SCROLLSPEED = 3;
2777 if (x - 15 < 0) {
2778 w->viewport->CancelFollow(*w);
2779 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2780 } else if (15 - (vp->width - x) > 0) {
2781 w->viewport->CancelFollow(*w);
2782 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2783 }
2784 if (y - 15 < 0) {
2785 w->viewport->CancelFollow(*w);
2786 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2787 } else if (15 - (vp->height - y) > 0) {
2788 w->viewport->CancelFollow(*w);
2789 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2790 }
2791}
2792
2793enum MouseClick : uint8_t {
2794 MC_NONE = 0,
2795 MC_LEFT,
2796 MC_RIGHT,
2797 MC_DOUBLE_LEFT,
2798 MC_HOVER,
2799};
2800
2801static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2802static constexpr int MAX_OFFSET_HOVER = 5;
2803
2805
2806const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2807
2808static void ScrollMainViewport(int x, int y)
2809{
2810 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2811 Window *w = GetMainWindow();
2812 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2813 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2814 }
2815}
2816
2826static const int8_t scrollamt[16][2] = {
2827 { 0, 0},
2828 {-2, 0},
2829 { 0, -2},
2830 {-2, -1},
2831 { 2, 0},
2832 { 0, 0},
2833 { 2, -1},
2834 { 0, -2},
2835 { 0, 2},
2836 {-2, 1},
2837 { 0, 0},
2838 {-2, 0},
2839 { 2, 1},
2840 { 0, 2},
2841 { 2, 0},
2842 { 0, 0},
2843};
2844
2845static void HandleKeyScrolling()
2846{
2847 /*
2848 * Check that any of the dirkeys is pressed and that the focused window
2849 * doesn't have an edit-box as focused widget.
2850 */
2851 if (_dirkeys && !EditBoxInGlobalFocus()) {
2852 int factor = _shift_pressed ? 50 : 10;
2853
2854 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2855 /* Key scrolling stops following a vehicle. */
2856 Window *main_window = GetMainWindow();
2857 main_window->viewport->CancelFollow(*main_window);
2858 }
2859
2860 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2861 }
2862}
2863
2864static void MouseLoop(MouseClick click, int mousewheel)
2865{
2866 /* World generation is multithreaded and messes with companies.
2867 * But there is no company related window open anyway, so _current_company is not used. */
2868 assert(HasModalProgress() || IsLocalCompany());
2869
2870 HandlePlacePresize();
2872
2873 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2874 if (HandleMouseDragDrop() == ES_HANDLED) return;
2875 if (HandleWindowDragging() == ES_HANDLED) return;
2876 if (HandleActiveWidget() == ES_HANDLED) return;
2877 if (HandleViewportScroll() == ES_HANDLED) return;
2878
2880
2881 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2882 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2883
2884 int x = _cursor.pos.x;
2885 int y = _cursor.pos.y;
2886 Window *w = FindWindowFromPt(x, y);
2887 if (w == nullptr) return;
2888
2889 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2890 Viewport *vp = IsPtInWindowViewport(w, x, y);
2891
2892 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2893 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2894
2895 if (mousewheel != 0) {
2896 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2897 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) {
2898 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2899 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2900 }
2901 }
2902
2903 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2904 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2905 }
2906
2907 if (vp != nullptr) {
2908 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2909 _scrolling_viewport = true;
2910 _cursor.fix_at = true;
2911 return;
2912 }
2913
2914 switch (click) {
2915 case MC_DOUBLE_LEFT:
2916 case MC_LEFT:
2917 if (HandleViewportClicked(*vp, x, y)) return;
2919 _settings_client.gui.scroll_mode == ViewportScrollMode::MapLMB) {
2920 _scrolling_viewport = true;
2921 _cursor.fix_at = false;
2922 return;
2923 }
2924 break;
2925
2926 case MC_RIGHT:
2928 _settings_client.gui.scroll_mode != ViewportScrollMode::MapLMB) {
2929 _scrolling_viewport = true;
2930 _cursor.fix_at = (_settings_client.gui.scroll_mode == ViewportScrollMode::ViewportRMBFixed ||
2932 DispatchRightClickEvent(w, x - w->left, y - w->top);
2933 return;
2934 }
2935 break;
2936
2937 default:
2938 break;
2939 }
2940 }
2941
2942 switch (click) {
2943 case MC_LEFT:
2944 case MC_DOUBLE_LEFT:
2945 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2946 return;
2947
2948 default:
2949 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2950 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2951 * Simulate a right button click so we can get started. */
2952 [[fallthrough]];
2953
2954 case MC_RIGHT:
2955 DispatchRightClickEvent(w, x - w->left, y - w->top);
2956 return;
2957
2958 case MC_HOVER:
2959 DispatchHoverEvent(w, x - w->left, y - w->top);
2960 break;
2961 }
2962
2963 /* We're not doing anything with 2D scrolling, so reset the value. */
2964 _cursor.h_wheel = 0.0f;
2965 _cursor.v_wheel = 0.0f;
2966 _cursor.wheel_moved = false;
2967}
2968
2973{
2974 /* World generation is multithreaded and messes with companies.
2975 * But there is no company related window open anyway, so _current_company is not used. */
2976 assert(HasModalProgress() || IsLocalCompany());
2977
2978 static std::chrono::steady_clock::time_point double_click_time = {};
2979 static Point double_click_pos = {0, 0};
2980
2981 /* Mouse event? */
2982 MouseClick click = MC_NONE;
2984 click = MC_LEFT;
2985 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2986 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2987 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2988 click = MC_DOUBLE_LEFT;
2989 }
2990 double_click_time = std::chrono::steady_clock::now();
2991 double_click_pos = _cursor.pos;
2992 _left_button_clicked = true;
2993 } else if (_right_button_clicked) {
2994 _right_button_clicked = false;
2995 click = MC_RIGHT;
2996 }
2997
2998 int mousewheel = 0;
2999 if (_cursor.wheel) {
3000 mousewheel = _cursor.wheel;
3001 _cursor.wheel = 0;
3002 }
3003
3004 static std::chrono::steady_clock::time_point hover_time = {};
3005 static Point hover_pos = {0, 0};
3006
3007 if (_settings_client.gui.hover_delay_ms > 0) {
3008 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
3009 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
3010 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
3011 hover_pos = _cursor.pos;
3012 hover_time = std::chrono::steady_clock::now();
3013 _mouse_hovering = false;
3014 } else if (!_mouse_hovering) {
3015 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
3016 click = MC_HOVER;
3017 _mouse_hovering = true;
3018 hover_time = std::chrono::steady_clock::now();
3019 }
3020 }
3021 }
3022
3023 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
3024 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
3026 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
3027 _newgrf_debug_sprite_picker.sprites.clear();
3028 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3030 } else {
3031 MouseLoop(click, mousewheel);
3032 }
3033
3034 /* We have moved the mouse the required distance,
3035 * no need to move it at any later time. */
3036 _cursor.delta.x = 0;
3037 _cursor.delta.y = 0;
3038}
3039
3043static void CheckSoftLimit()
3044{
3045 if (_settings_client.gui.window_soft_limit == 0) return;
3046
3047 for (;;) {
3048 uint deletable_count = 0;
3049 Window *last_deletable = nullptr;
3050 for (Window *w : Window::IterateFromFront()) {
3051 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3052
3053 last_deletable = w;
3054 deletable_count++;
3055 }
3056
3057 /* We've not reached the soft limit yet. */
3058 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3059
3060 assert(last_deletable != nullptr);
3061 last_deletable->Close();
3062 }
3063}
3064
3069{
3070 /* World generation is multithreaded and messes with companies.
3071 * But there is no company related window open anyway, so _current_company is not used. */
3072 assert(HasModalProgress() || IsLocalCompany());
3073
3075
3076 /* Process scheduled window deletion. */
3078
3079 /* HandleMouseEvents was already called for this tick */
3081}
3082
3083static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3084
3085bool CanContinueRealtimeTick()
3086{
3087 auto now = std::chrono::steady_clock::now();
3088 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3089}
3090
3095{
3096 _realtime_tick_start = std::chrono::steady_clock::now();
3097 for (Window *w : Window::Iterate()) {
3098 w->OnRealtimeTick(delta_ms);
3099 }
3100}
3101
3103static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3104 extern int _caret_timer;
3105 _caret_timer += 3;
3106 CursorTick();
3107
3108 HandleKeyScrolling();
3110 DecreaseWindowCounters();
3111});
3112
3116});
3117
3119static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3120 if (_network_dedicated) return;
3121
3122 for (Window *w : Window::Iterate()) {
3125 w->SetDirty();
3126 }
3127 }
3128});
3129
3134{
3135 static auto last_time = std::chrono::steady_clock::now();
3136 auto now = std::chrono::steady_clock::now();
3137 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3138
3139 if (delta_ms.count() == 0) return;
3140
3141 last_time = now;
3142
3145
3147
3149 CallWindowRealtimeTickEvent(delta_ms.count());
3150
3151 /* Process invalidations before anything else. */
3152 for (Window *w : Window::Iterate()) {
3156 }
3157
3158 /* Skip the actual drawing on dedicated servers without screen.
3159 * But still empty the invalidation queues above. */
3160 if (_network_dedicated) return;
3161
3163
3164 for (Window *w : Window::Iterate()) {
3165 /* Update viewport only if window is not shaded. */
3166 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3167 }
3169 /* Redraw mouse cursor in case it was hidden */
3170 DrawMouseCursor();
3171
3172 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3173 /* We are done with the last draw-frame, so we know what sprites we
3174 * clicked on. Reset the picker mode and invalidate the window. */
3175 _newgrf_debug_sprite_picker.mode = SPM_NONE;
3177 }
3178}
3179
3185void SetWindowDirty(WindowClass cls, WindowNumber number)
3186{
3187 for (const Window *w : Window::Iterate()) {
3188 if (w->window_class == cls && w->window_number == number) {
3189 w->SetDirty();
3190 return;
3191 }
3192 }
3193}
3194
3201void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
3202{
3203 for (const Window *w : Window::Iterate()) {
3204 if (w->window_class == cls && w->window_number == number) {
3205 w->SetWidgetDirty(widget_index);
3206 return;
3207 }
3208 }
3209}
3210
3215void SetWindowClassesDirty(WindowClass cls)
3216{
3217 for (const Window *w : Window::Iterate()) {
3218 if (w->window_class == cls) w->SetDirty();
3219 }
3220}
3221
3226{
3227 this->scheduled_resize = true;
3228}
3229
3234{
3235 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3236 while (this->scheduled_resize) {
3237 this->scheduled_resize = false;
3238 this->OnResize();
3239 }
3240}
3241
3247void Window::InvalidateData(int data, bool gui_scope)
3248{
3249 this->SetDirty();
3250 if (!gui_scope) {
3251 /* Schedule GUI-scope invalidation for next redraw. */
3252 this->scheduled_invalidation_data.push_back(data);
3253 }
3254 this->OnInvalidateData(data, gui_scope);
3255}
3256
3261{
3262 for (int data : this->scheduled_invalidation_data) {
3263 if (this->window_class == WC_INVALID) break;
3264 this->OnInvalidateData(data, true);
3265 }
3266 this->scheduled_invalidation_data.clear();
3267}
3268
3273{
3274 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3275
3276 for (const auto &pair : this->widget_lookup) {
3277 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3278 }
3279}
3280
3307void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3308{
3309 for (Window *w : Window::Iterate()) {
3310 if (w->window_class == cls && w->window_number == number) {
3311 w->InvalidateData(data, gui_scope);
3312 return;
3313 }
3314 }
3315}
3316
3325void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3326{
3327 for (Window *w : Window::Iterate()) {
3328 if (w->window_class == cls) {
3329 w->InvalidateData(data, gui_scope);
3330 }
3331 }
3332}
3333
3338{
3339 for (Window *w : Window::Iterate()) {
3340 w->OnGameTick();
3341 }
3342}
3343
3351{
3352 /* Note: the container remains stable, even when deleting windows. */
3353 for (Window *w : Window::Iterate()) {
3355 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3356
3357 w->Close();
3358 }
3359 }
3360}
3361
3370{
3371 /* Note: the container remains stable, even when closing windows. */
3372 for (Window *w : Window::Iterate()) {
3374 w->Close();
3375 }
3376 }
3377}
3378
3383{
3385 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3386 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3387 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3388}
3389
3395{
3396 /* Note: the container remains stable, even when deleting windows. */
3397 for (Window *w : Window::Iterate()) {
3399 w->Close();
3400 }
3401 }
3402
3403 for (const Window *w : Window::Iterate()) w->SetDirty();
3404}
3405
3412
3413void ReInitWindow(Window *w, bool zoom_changed)
3414{
3415 if (w == nullptr) return;
3416 if (zoom_changed) {
3417 w->nested_root->AdjustPaddingForZoom();
3419 }
3420 w->ReInit();
3421}
3422
3424void ReInitAllWindows(bool zoom_changed)
3425{
3427 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3428 NWidgetScrollbar::InvalidateDimensionCache();
3429
3431
3432 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3433 * so EnsureVisibleCaption uses the updated size information. */
3434 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3435 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3436 for (Window *w : Window::Iterate()) {
3437 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3438 ReInitWindow(w, zoom_changed);
3439 }
3440
3443
3444 /* Make sure essential parts of all windows are visible */
3445 RelocateAllWindows(_screen.width, _screen.height);
3447}
3448
3456static int PositionWindow(Window *w, WindowClass clss, int setting)
3457{
3458 if (w == nullptr || w->window_class != clss) {
3459 w = FindWindowById(clss, 0);
3460 }
3461 if (w == nullptr) return 0;
3462
3463 int old_left = w->left;
3464 switch (setting) {
3465 case 1: w->left = (_screen.width - w->width) / 2; break;
3466 case 2: w->left = _screen.width - w->width; break;
3467 default: w->left = 0; break;
3468 }
3469 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3470 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3471 return w->left;
3472}
3473
3480{
3481 Debug(misc, 5, "Repositioning Main Toolbar...");
3482 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
3483}
3484
3491{
3492 Debug(misc, 5, "Repositioning statusbar...");
3493 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
3494}
3495
3502{
3503 Debug(misc, 5, "Repositioning news message...");
3504 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
3505}
3506
3513{
3514 Debug(misc, 5, "Repositioning network chat window...");
3515 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
3516}
3517
3518
3525{
3526 for (const Window *w : Window::Iterate()) {
3527 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3528 w->viewport->follow_vehicle = to_index;
3529 w->SetDirty();
3530 }
3531 }
3532}
3533
3534
3540void RelocateAllWindows(int neww, int newh)
3541{
3543
3544 /* Reposition toolbar then status bar before other all windows. */
3545 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3546 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3547 wt->left = PositionMainToolbar(wt);
3548 }
3549
3550 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3551 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3552 ws->top = newh - ws->height;
3553 ws->left = PositionStatusbar(ws);
3554 }
3555
3556 for (Window *w : Window::Iterate()) {
3557 int left, top;
3558 /* XXX - this probably needs something more sane. For example specifying
3559 * in a 'backup'-desc that the window should always be centered. */
3560 switch (w->window_class) {
3561 case WC_MAIN_WINDOW:
3562 case WC_BOOTSTRAP:
3563 case WC_HIGHSCORE:
3564 case WC_ENDSCREEN:
3565 ResizeWindow(w, neww, newh);
3566 continue;
3567
3568 case WC_MAIN_TOOLBAR:
3569 case WC_STATUS_BAR:
3570 continue;
3571
3572 case WC_NEWS_WINDOW:
3573 top = newh - w->height;
3574 left = PositionNewsMessage(w);
3575 break;
3576
3578 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3579
3580 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3581 left = PositionNetworkChatWindow(w);
3582 break;
3583
3584 case WC_CONSOLE:
3585 IConsoleResize(w);
3586 continue;
3587
3588 default: {
3589 if (w->flags.Test(WindowFlag::Centred)) {
3590 top = (newh - w->height) >> 1;
3591 left = (neww - w->width) >> 1;
3592 break;
3593 }
3594
3595 left = w->left;
3596 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3597 if (left < 0) left = 0;
3598
3599 top = w->top;
3600 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3601 break;
3602 }
3603 }
3604
3605 EnsureVisibleCaption(w, left, top);
3606 }
3607}
3608
3613void PickerWindowBase::Close([[maybe_unused]] int data)
3614{
3616 this->Window::Close();
3617}
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:138
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 the 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:3613
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:967
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:409
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:695
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:720
@ 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:853
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:795
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:823
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:174
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:365
void SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
Sets the highlighted status of a widget.
Definition window.cpp:239
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:979
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1104
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:795
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1809
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
virtual void ApplyDefaults()
Read default values from WindowDesc configuration an apply them to the window.
Definition window.cpp:191
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:1458
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:3247
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:1141
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:470
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:557
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:505
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:3260
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1076
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:470
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:1022
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:3225
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:1799
WindowDesc & window_desc
Window description.
Definition window_gui.h:300
WindowClass window_class
Window class.
Definition window_gui.h:302
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
Definition window_gui.h: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:1091
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:486
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:522
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:531
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1788
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:378
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1477
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:285
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:2726
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:1048
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:354
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:334
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:409
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:1420
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:1832
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:210
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:3233
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2564
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:596
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:1063
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:514
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1822
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h: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:314
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3272
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:571
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:394
bool IsWidgetHighlighted(WidgetID widget_index) const
Gets the highlighted status of a widget.
Definition window.cpp:269
void DisableAllWidgetHighlight()
Disable the highlighted status of all widgets.
Definition window.cpp:221
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
@ 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:3490
static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
Do not allow hiding of the rectangle with base coordinates nx and ny behind window v.
Definition window.cpp:2021
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2156
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:3394
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:1196
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:1729
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1182
static Point _drag_delta
delta between mouse cursor and upper left corner of dragged window
Definition window.cpp:55
static bool MayBeShown(const Window *w)
Returns whether a window may be shown or not.
Definition window.cpp:856
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1225
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2709
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:884
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:3068
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3133
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3479
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3350
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:946
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2099
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:1579
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3512
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2351
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:1618
PreventHideDirection
Direction for moving the window.
Definition window.cpp:2006
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:2008
@ PHD_UP
Above v is a safe position.
Definition window.cpp:2007
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2756
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:2636
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3456
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:422
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:1543
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2801
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1168
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3524
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:2440
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:819
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:617
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1844
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3382
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:2139
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:1401
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3424
static void EnsureVisibleCaption(Window *w, int nx, int ny)
Make sure at least a part of the caption bar is still visible by moving the window if necessary.
Definition window.cpp:2061
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1209
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2972
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:1686
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2162
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3501
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2150
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:791
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:1245
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2653
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3215
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:2802
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:462
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1894
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1317
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:448
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:3307
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3407
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:166
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1280
Point AlignInitialConstructionToolbar(int window_width)
Compute the position of the construction toolbars.
Definition window.cpp:1702
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3540
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:2367
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:2334
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3337
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2407
std::vector< WindowDesc * > * _window_descs
List of all WindowDescs.
Definition window.cpp:102
static void DispatchRightClickEvent(Window *w, int x, int y)
Dispatch right mouse-button click in window.
Definition window.cpp:762
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:2826
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2504
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:3094
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:96
static EventState HandleMouseDragDrop()
Handle dragging and dropping in mouse dragging mode (WSM_DRAGDROP).
Definition window.cpp:1959
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3369
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1983
static const IntervalTimer< TimerWindow > window_interval(std::chrono::milliseconds(30), [](auto) { extern int _caret_timer;_caret_timer+=3;CursorTick();HandleKeyScrolling();HandleAutoscroll();DecreaseWindowCounters();})
Update various of window-related information on a regular interval.
static Window * _mouseover_last_w
Window of the last OnMouseOver event.
Definition window.cpp:56
static void CheckSoftLimit()
Check the soft limit of deletable (non vital, non sticky) windows.
Definition window.cpp:3043
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1153
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1880
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1858
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting).
Definition window.cpp:3201
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:2745
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3185
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:3325
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:422
@ 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