OpenTTD Source 20260401-master-g3efaeb0eea
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
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
120WindowDesc::WindowDesc(WindowPosition def_pos, std::string_view ini_key, int16_t def_width_trad, int16_t def_height_trad,
121 WindowClass window_class, WindowClass parent_class, WindowDefaultFlags flags,
122 const std::span<const NWidgetPart> nwid_parts, HotkeyList *hotkeys,
123 const std::source_location location) :
124 source_location(location),
125 default_pos(def_pos),
126 cls(window_class),
127 parent_cls(parent_class),
129 flags(flags),
132 default_width_trad(def_width_trad),
133 default_height_trad(def_height_trad)
134{
135 if (_window_descs == nullptr) _window_descs = new std::vector<WindowDesc*>();
136 _window_descs->push_back(this);
137}
138
141{
142 _window_descs->erase(std::ranges::find(*_window_descs, this));
143}
144
151{
152 return this->pref_width != 0 ? this->pref_width : ScaleGUITrad(this->default_width_trad);
153}
154
161{
162 return this->pref_height != 0 ? this->pref_height : ScaleGUITrad(this->default_height_trad);
163}
164
169{
170 IniFile ini;
172 for (WindowDesc *wd : *_window_descs) {
173 if (wd->ini_key.empty()) continue;
174 IniLoadWindowSettings(ini, wd->ini_key, wd);
175 }
176}
177
179static bool DescSorter(WindowDesc * const &a, WindowDesc * const &b)
180{
181 return a->ini_key < b->ini_key;
182}
183
188{
189 /* Sort the stuff to get a nice ini file on first write */
190 std::sort(_window_descs->begin(), _window_descs->end(), DescSorter);
191
192 IniFile ini;
194 for (WindowDesc *wd : *_window_descs) {
195 if (wd->ini_key.empty()) continue;
196 IniSaveWindowSettings(ini, wd->ini_key, wd);
197 }
199}
200
205{
206 if (this->nested_root != nullptr && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != nullptr) {
207 if (this->window_desc.pref_sticky) this->flags.Set(WindowFlag::Sticky);
208 } else {
209 /* There is no stickybox; clear the preference in case someone tried to be funny */
210 this->window_desc.pref_sticky = false;
211 }
212}
213
223int Window::GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height) const
224{
225 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
226 if (line_height < 0) line_height = wid->resize_y;
227 if (clickpos < wid->pos_y + padding) return INT_MAX;
228 return (clickpos - wid->pos_y - padding) / line_height;
229}
230
235{
236 for (auto &pair : this->widget_lookup) {
237 NWidgetBase *nwid = pair.second;
238 if (nwid->IsHighlighted()) {
239 nwid->SetHighlighted(TC_INVALID);
240 nwid->SetDirty(this);
241 }
242 }
243
244 this->flags.Reset(WindowFlag::Highlighted);
245}
246
252void Window::SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
253{
254 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
255 if (nwid == nullptr) return;
256
257 nwid->SetHighlighted(highlighted_colour);
258 nwid->SetDirty(this);
259
260 if (highlighted_colour != TC_INVALID) {
261 /* If we set a highlight, the window has a highlight */
263 } else {
264 /* If we disable a highlight, check all widgets if anyone still has a highlight */
265 bool valid = false;
266 for (const auto &pair : this->widget_lookup) {
267 nwid = pair.second;
268 if (!nwid->IsHighlighted()) continue;
269
270 valid = true;
271 }
272 /* If nobody has a highlight, disable the flag on the window */
273 if (!valid) this->flags.Reset(WindowFlag::Highlighted);
274 }
275}
276
282bool Window::IsWidgetHighlighted(WidgetID widget_index) const
283{
284 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
285 if (nwid == nullptr) return false;
286
287 return nwid->IsHighlighted();
288}
289
298void Window::OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
299{
300 if (widget < 0) return;
301
302 /* Many dropdown selections depend on the position of the main toolbar,
303 * so if it doesn't exist (e.g. the end screen has appeared), just skip the instant close behaviour. */
304 if (instant_close && FindWindowById(WC_MAIN_TOOLBAR, 0) != nullptr) {
305 /* Send event for selected option if we're still
306 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
307 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
308 this->OnDropdownSelect(widget, index, click_result);
309 }
310 }
311
312 /* Raise the dropdown button */
313 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
314 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
316 } else {
317 this->RaiseWidget(widget);
318 }
319 this->SetWidgetDirty(widget);
320}
321
328{
329 return this->GetWidget<NWidgetScrollbar>(widnum);
330}
331
338{
339 return this->GetWidget<NWidgetScrollbar>(widnum);
340}
341
348{
349 auto query = this->querystrings.find(widnum);
350 return query != this->querystrings.end() ? query->second : nullptr;
351}
352
359{
360 auto query = this->querystrings.find(widnum);
361 return query != this->querystrings.end() ? query->second : nullptr;
362}
363
368{
369 for (auto &qs : this->querystrings) {
370 qs.second->text.UpdateSize();
371 }
372}
373
378/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
379{
380 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
381 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
382 }
383
384 return nullptr;
385}
386
391/* virtual */ Point Window::GetCaretPosition() const
392{
393 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
394 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
395 }
396
397 Point pt = {0, 0};
398 return pt;
399}
400
407/* virtual */ Rect Window::GetTextBoundingRect(size_t from, size_t to) const
408{
409 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
410 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
411 }
412
413 Rect r = {0, 0, 0, 0};
414 return r;
415}
416
422/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
423{
424 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
425 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
426 }
427
428 return -1;
429}
430
436{
437 if (_focused_window == w) return;
438
439 /* Don't focus a tooltip */
440 if (w != nullptr && w->window_class == WC_TOOLTIPS) return;
441
442 /* Invalidate focused widget */
443 if (_focused_window != nullptr) {
444 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
445 }
446
447 /* Remember which window was previously focused */
448 Window *old_focused = _focused_window;
449 _focused_window = w;
450
451 /* So we can inform it that it lost focus */
452 if (old_focused != nullptr) old_focused->OnFocusLost(false);
453 if (_focused_window != nullptr) _focused_window->OnFocus();
454}
455
462{
463 if (_focused_window == nullptr) return false;
464
465 /* The console does not have an edit box so a special case is needed. */
466 if (_focused_window->window_class == WC_CONSOLE) return true;
467
468 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
469}
470
476{
477 return _focused_window && _focused_window->window_class == WC_CONSOLE;
478}
479
484{
485 if (this->nested_focus != nullptr) {
487
488 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
489 this->nested_focus->SetDirty(this);
490 this->nested_focus = nullptr;
491 }
492}
493
500{
501 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
502 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
503
504 if (this->nested_focus != nullptr) {
505 /* Do nothing if widget_index is already focused. */
506 if (widget == this->nested_focus) return false;
507
508 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
509 this->nested_focus->SetDirty(this);
511 }
512
513 this->nested_focus = widget;
515 return true;
516}
517
518std::string Window::GetWidgetString([[maybe_unused]] WidgetID widget, StringID stringid) const
519{
520 if (stringid == STR_NULL) return {};
521 return GetString(stringid);
522}
523
528{
529 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxGainedFocus();
530}
531
536{
537 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
538}
539
544void Window::RaiseButtons(bool autoraise)
545{
546 for (auto &pair : this->widget_lookup) {
547 WidgetType type = pair.second->type;
548 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
549 if (wid != nullptr && ((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
550 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
551 wid->SetLowered(false);
552 wid->SetDirty(this);
553 }
554 }
555
556 /* Special widgets without widget index */
557 {
558 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
559 if (wid != nullptr) {
560 wid->SetLowered(false);
561 wid->SetDirty(this);
562 }
563 }
564}
565
570void Window::SetWidgetDirty(WidgetID widget_index) const
571{
572 /* Sometimes this function is called before the window is even fully initialized */
573 auto it = this->widget_lookup.find(widget_index);
574 if (it == std::end(this->widget_lookup)) return;
575
576 it->second->SetDirty(this);
577}
578
585{
586 if (hotkey < 0) return ES_NOT_HANDLED;
587
588 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
589 if (nw == nullptr || nw->IsDisabled()) return ES_NOT_HANDLED;
590
591 if (nw->type == WWT_EDITBOX) {
592 if (this->IsShaded()) return ES_NOT_HANDLED;
593
594 /* Focus editbox */
595 this->SetFocusedWidget(hotkey);
596 SetFocusedWindow(this);
597 } else {
598 /* Click button */
599 this->OnClick(Point(), hotkey, 1);
600 }
601 return ES_HANDLED;
602}
603
610{
611 /* Button click for this widget may already have been handled. */
612 if (this->IsWidgetLowered(widget) && this->timeout_timer == TIMEOUT_DURATION) return;
613
614 this->LowerWidget(widget);
615 this->SetTimeout();
616 this->SetWidgetDirty(widget);
617 SndClickBeep();
618}
619
620static void StartWindowDrag(Window *w);
621static void StartWindowSizing(Window *w, bool to_left);
622
630static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
631{
632 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
633 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
634
635 /* Allow dropdown close flag detection to work. */
637
638 bool focused_widget_changed = false;
639
640 /* If clicked on a window that previously did not have focus */
641 if (_focused_window != w) {
642 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
643 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
644 focused_widget_changed = true;
646 } else if (_focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU) {
647 /* The previously focused window was a dropdown menu, but the user clicked on another window that
648 * isn't focusable. Close the dropdown menu anyway. */
649 SetFocusedWindow(nullptr);
650 }
651 }
652
653 if (nw == nullptr) return; // exit if clicked outside of widgets
654
655 /* don't allow any interaction if the button has been disabled */
656 if (nw->IsDisabled()) return;
657
658 WidgetID widget_index = nw->GetIndex();
659
660 /* Clicked on a widget that is not disabled.
661 * So unless the clicked widget is the caption bar, change focus to this widget.
662 * Exception: In the OSK we always want the editbox to stay focused. */
663 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
664 /* focused_widget_changed is 'now' only true if the window this widget
665 * is in gained focus. In that case it must remain true, also if the
666 * local widget focus did not change. As such it's the logical-or of
667 * both changed states.
668 *
669 * If this is not preserved, then the OSK window would be opened when
670 * a user has the edit box focused and then click on another window and
671 * then back again on the edit box (to type some text).
672 */
673 focused_widget_changed |= w->SetFocusedWidget(widget_index);
674 }
675
676 /* Dropdown window of this widget was closed so don't process click this time. */
678
679 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
680
681 Point pt = { x, y };
682
683 switch (widget_type) {
684 case NWID_VSCROLLBAR:
685 case NWID_HSCROLLBAR:
686 ScrollbarClickHandler(w, nw, x, y);
687 break;
688
689 case WWT_EDITBOX: {
690 QueryString *query = w->GetQueryString(widget_index);
691 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
692 break;
693 }
694
695 case WWT_CLOSEBOX: // 'X'
696 w->Close();
697 return;
698
699 case WWT_CAPTION: // 'Title bar'
701 return;
702
703 case WWT_RESIZEBOX:
704 /* When the resize widget is on the left size of the window
705 * we assume that that button is used to resize to the left. */
706 StartWindowSizing(w, nw->pos_x < (w->width / 2));
707 nw->SetDirty(w);
708 return;
709
710 case WWT_DEFSIZEBOX: {
711 if (_ctrl_pressed) {
712 if (click_count > 1) {
713 w->window_desc.pref_width = 0;
715 } else {
718 }
719 } else {
720 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
721 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
722
723 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
724 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
725 /* dx and dy has to go by step.. calculate it.
726 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
727 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
728 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
729 ResizeWindow(w, dx, dy, false);
730 }
731
732 nw->SetLowered(true);
733 nw->SetDirty(w);
734 w->SetTimeout();
735 break;
736 }
737
738 case WWT_DEBUGBOX:
740 break;
741
742 case WWT_SHADEBOX:
743 nw->SetDirty(w);
744 w->SetShaded(!w->IsShaded());
745 return;
746
747 case WWT_STICKYBOX:
749 nw->SetDirty(w);
751 return;
752
753 default:
754 break;
755 }
756
757 /* Widget has no index, so the window is not interested in it. */
758 if (widget_index < 0) return;
759
760 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
761 if (w->IsWidgetHighlighted(widget_index)) {
762 w->SetWidgetHighlight(widget_index, TC_INVALID);
763 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
764 }
765
766 w->OnClick(pt, widget_index, click_count);
767}
768
775static void DispatchRightClickEvent(Window *w, int x, int y)
776{
777 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
778 if (wid == nullptr) return;
779
780 Point pt = { x, y };
781
782 /* No widget to handle, or the window is not interested in it. */
783 if (wid->GetIndex() >= 0) {
784 if (w->OnRightClick(pt, wid->GetIndex())) return;
785 }
786
787 /* Right-click close is enabled and there is a closebox. */
788 if (_settings_client.gui.right_click_wnd_close == RightClickClose::Yes && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
789 w->Close();
790 } else if (_settings_client.gui.right_click_wnd_close == RightClickClose::YesExceptSticky && !w->flags.Test(WindowFlag::Sticky) && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
791 /* Right-click close is enabled, but excluding sticky windows. */
792 w->Close();
793 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TCC_RIGHT_CLICK) && wid->GetToolTip() != STR_NULL) {
794 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_RIGHT_CLICK);
795 }
796}
797
804static void DispatchHoverEvent(Window *w, int x, int y)
805{
806 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
807
808 /* No widget to handle */
809 if (wid == nullptr) return;
810
811 Point pt = { x, y };
812
813 /* Show the tooltip if there is any */
814 if (!w->OnTooltip(pt, wid->GetIndex(), TCC_HOVER) && wid->GetToolTip() != STR_NULL) {
815 GuiShowTooltips(w, GetEncodedString(wid->GetToolTip()), TCC_HOVER);
816 return;
817 }
818
819 /* Widget has no index, so the window is not interested in it. */
820 if (wid->GetIndex() < 0) return;
821
822 w->OnHover(pt, wid->GetIndex());
823}
824
832static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
833{
834 if (nwid == nullptr) return;
835
836 /* Using wheel on caption/shade-box shades or unshades the window. */
837 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
838 w->SetShaded(wheel < 0);
839 return;
840 }
841
842 /* Wheeling a vertical scrollbar. */
843 if (nwid->type == NWID_VSCROLLBAR) {
844 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
845 if (sb->GetCount() > sb->GetCapacity()) {
846 if (sb->UpdatePosition(wheel)) {
847 w->OnScrollbarScroll(nwid->GetIndex());
848 w->SetDirty();
849 }
850 }
851 return;
852 }
853
854 /* Scroll the widget attached to the scrollbar. */
855 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
856 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
857 if (sb->UpdatePosition(wheel)) {
859 w->SetDirty();
860 }
861 }
862}
863
869static bool MayBeShown(const Window *w)
870{
871 /* If we're not modal, everything is okay. */
872 if (!HasModalProgress()) return true;
873
874 switch (w->window_class) {
875 case WC_MAIN_WINDOW:
876 case WC_MODAL_PROGRESS:
878 return true;
879
880 default:
881 return false;
882 }
883}
884
897static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
898{
900 ++it;
901 for (; !it.IsEnd(); ++it) {
902 const Window *v = *it;
903 if (MayBeShown(v) &&
904 right > v->left &&
905 bottom > v->top &&
906 left < v->left + v->width &&
907 top < v->top + v->height) {
908 /* v and rectangle intersect with each other */
909 int x;
910
911 if (left < (x = v->left)) {
912 DrawOverlappedWindow(w, left, top, x, bottom);
913 DrawOverlappedWindow(w, x, top, right, bottom);
914 return;
915 }
916
917 if (right > (x = v->left + v->width)) {
918 DrawOverlappedWindow(w, left, top, x, bottom);
919 DrawOverlappedWindow(w, x, top, right, bottom);
920 return;
921 }
922
923 if (top < (x = v->top)) {
924 DrawOverlappedWindow(w, left, top, right, x);
925 DrawOverlappedWindow(w, left, x, right, bottom);
926 return;
927 }
928
929 if (bottom > (x = v->top + v->height)) {
930 DrawOverlappedWindow(w, left, top, right, x);
931 DrawOverlappedWindow(w, left, x, right, bottom);
932 return;
933 }
934
935 return;
936 }
937 }
938
939 /* Setup blitter, and dispatch a repaint event to window *wz */
940 DrawPixelInfo *dp = _cur_dpi;
941 dp->width = right - left;
942 dp->height = bottom - top;
943 dp->left = left - w->left;
944 dp->top = top - w->top;
945 dp->pitch = _screen.pitch;
946 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
947 dp->zoom = ZoomLevel::Min;
948 w->OnPaint();
949}
950
959void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
960{
961 DrawPixelInfo bk;
962 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
963
964 for (Window *w : Window::IterateFromBack()) {
965 if (MayBeShown(w) &&
966 right > w->left &&
967 bottom > w->top &&
968 left < w->left + w->width &&
969 top < w->top + w->height) {
970 /* Window w intersects with the rectangle => needs repaint */
971 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));
972 }
973 }
974}
975
981{
982 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
983}
984
992void Window::ReInit(int rx, int ry, bool reposition)
993{
994 this->SetDirty(); // Mark whole current window as dirty.
995
996 /* Save current size. */
997 int window_width = this->width * _gui_scale / this->scale;
998 int window_height = this->height * _gui_scale / this->scale;
999 this->scale = _gui_scale;
1000
1001 this->OnInit();
1002 /* Re-initialize window smallest size. */
1003 this->nested_root->SetupSmallestSize(this);
1004 this->nested_root->AssignSizePosition(SizingType::Smallest, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1005 this->width = this->nested_root->smallest_x;
1006 this->height = this->nested_root->smallest_y;
1007 this->resize.step_width = this->nested_root->resize_x;
1008 this->resize.step_height = this->nested_root->resize_y;
1009
1010 /* Resize as close to the original size + requested resize as possible. */
1011 window_width = std::max(window_width + rx, this->width);
1012 window_height = std::max(window_height + ry, this->height);
1013 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
1014 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
1015 /* dx and dy has to go by step.. calculate it.
1016 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1017 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1018 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1019
1020 if (reposition) {
1021 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1022 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1023 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1024 }
1025
1026 ResizeWindow(this, dx, dy, true, false);
1027 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1028}
1029
1035void Window::SetShaded(bool make_shaded)
1036{
1037 if (this->shade_select == nullptr) return;
1038
1039 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1040 if (this->shade_select->shown_plane != desired) {
1041 if (make_shaded) {
1042 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1043 this->unshaded_size.width = this->width;
1044 this->unshaded_size.height = this->height;
1045 this->shade_select->SetDisplayedPlane(desired);
1046 this->ReInit(0, -this->height);
1047 } else {
1048 this->shade_select->SetDisplayedPlane(desired);
1049 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1050 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1051 this->ReInit(dx, dy);
1052 }
1053 }
1054}
1055
1061Window *Window::FindChildWindow(WindowClass wc) const
1062{
1063 for (Window *v : Window::Iterate()) {
1064 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1065 }
1066
1067 return nullptr;
1068}
1069
1077{
1078 for (Window *v : Window::Iterate()) {
1079 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1080 }
1081
1082 return nullptr;
1083}
1084
1089void Window::CloseChildWindows(WindowClass wc) const
1090{
1091 Window *child = this->FindChildWindow(wc);
1092 while (child != nullptr) {
1093 child->Close();
1094 child = this->FindChildWindow(wc);
1095 }
1096}
1097
1098
1104void Window::CloseChildWindowById(WindowClass wc, WindowNumber number) const
1105{
1106 Window *child = this->FindChildWindowById(wc, number);
1107 while (child != nullptr) {
1108 child->Close();
1109 child = this->FindChildWindowById(wc, number);
1110 }
1111}
1112
1117void Window::Close([[maybe_unused]] int data)
1118{
1119 /* Don't close twice. */
1120 if (*this->z_position == nullptr) return;
1121
1122 *this->z_position = nullptr;
1123
1124 if (_thd.window_class == this->window_class &&
1125 _thd.window_number == this->window_number) {
1127 }
1128
1129 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1130 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1131
1132 /* We can't scroll the window when it's closed. */
1133 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1134
1135 /* Make sure we don't try to access non-existing query strings. */
1136 this->querystrings.clear();
1137
1138 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1139 if (_focused_window == this) {
1140 this->OnFocusLost(true);
1141 _focused_window = nullptr;
1142 }
1143
1144 this->CloseChildWindows();
1145
1146 this->SetDirty();
1147
1148 Window::closed_windows.push_back(this);
1149}
1150
1155{
1156 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1157 assert(*this->z_position == nullptr);
1158}
1159
1166Window *FindWindowById(WindowClass cls, WindowNumber number)
1167{
1168 for (Window *w : Window::Iterate()) {
1169 if (w->window_class == cls && w->window_number == number) return w;
1170 }
1171
1172 return nullptr;
1173}
1174
1181Window *FindWindowByClass(WindowClass cls)
1182{
1183 for (Window *w : Window::Iterate()) {
1184 if (w->window_class == cls) return w;
1185 }
1186
1187 return nullptr;
1188}
1189
1196{
1198 assert(w != nullptr);
1199 return w;
1200}
1201
1209void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1210{
1211 Window *w = FindWindowById(cls, number);
1212 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1213 w->Close(data);
1214 }
1215}
1216
1222void CloseWindowByClass(WindowClass cls, int data)
1223{
1224 /* Note: the container remains stable, even when deleting windows. */
1225 for (Window *w : Window::Iterate()) {
1226 if (w->window_class == cls) {
1227 w->Close(data);
1228 }
1229 }
1230}
1231
1238void CloseCompanyWindows(CompanyID id)
1239{
1240 /* Note: the container remains stable, even when deleting windows. */
1241 for (Window *w : Window::Iterate()) {
1242 if (w->owner == id) {
1243 w->Close();
1244 }
1245 }
1246
1247 /* Also delete the company specific windows that don't have a company-colour. */
1249}
1250
1258void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1259{
1260 for (Window *w : Window::Iterate()) {
1261 if (w->owner != old_owner) continue;
1262
1263 switch (w->window_class) {
1264 case WC_COMPANY_COLOUR:
1265 case WC_FINANCES:
1266 case WC_STATION_LIST:
1267 case WC_TRAINS_LIST:
1268 case WC_ROADVEH_LIST:
1269 case WC_SHIPS_LIST:
1270 case WC_AIRCRAFT_LIST:
1271 case WC_BUY_COMPANY:
1272 case WC_COMPANY:
1274 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().
1275 continue;
1276
1277 default:
1278 w->owner = new_owner;
1279 break;
1280 }
1281 }
1282}
1283
1284static void BringWindowToFront(Window *w, bool dirty = true);
1285
1294{
1295 Window *w = FindWindowById(cls, number);
1296
1297 if (w != nullptr) {
1298 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1299
1300 w->SetWhiteBorder();
1302 w->SetDirty();
1303 }
1304
1305 return w;
1306}
1307
1308static inline bool IsVitalWindow(const Window *w)
1309{
1310 switch (w->window_class) {
1311 case WC_MAIN_TOOLBAR:
1312 case WC_STATUS_BAR:
1313 case WC_NEWS_WINDOW:
1315 return true;
1316
1317 default:
1318 return false;
1319 }
1320}
1321
1330static uint GetWindowZPriority(WindowClass wc)
1331{
1332 assert(wc != WC_INVALID);
1333
1334 uint z_priority = 0;
1335
1336 switch (wc) {
1337 case WC_TOOLTIPS:
1338 ++z_priority;
1339 [[fallthrough]];
1340
1341 case WC_ERRMSG:
1343 ++z_priority;
1344 [[fallthrough]];
1345
1346 case WC_ENDSCREEN:
1347 ++z_priority;
1348 [[fallthrough]];
1349
1350 case WC_HIGHSCORE:
1351 ++z_priority;
1352 [[fallthrough]];
1353
1354 case WC_DROPDOWN_MENU:
1355 ++z_priority;
1356 [[fallthrough]];
1357
1358 case WC_MAIN_TOOLBAR:
1359 case WC_STATUS_BAR:
1360 ++z_priority;
1361 [[fallthrough]];
1362
1363 case WC_OSK:
1364 ++z_priority;
1365 [[fallthrough]];
1366
1367 case WC_QUERY_STRING:
1369 ++z_priority;
1370 [[fallthrough]];
1371
1373 case WC_MODAL_PROGRESS:
1375 case WC_SAVE_PRESET:
1376 ++z_priority;
1377 [[fallthrough]];
1378
1380 case WC_SAVELOAD:
1381 case WC_GAME_OPTIONS:
1382 case WC_CUSTOM_CURRENCY:
1383 case WC_NETWORK_WINDOW:
1384 case WC_GRF_PARAMETERS:
1385 case WC_SCRIPT_LIST:
1386 case WC_SCRIPT_SETTINGS:
1387 case WC_TEXTFILE:
1388 ++z_priority;
1389 [[fallthrough]];
1390
1391 case WC_CONSOLE:
1392 ++z_priority;
1393 [[fallthrough]];
1394
1395 case WC_NEWS_WINDOW:
1396 ++z_priority;
1397 [[fallthrough]];
1398
1399 default:
1400 ++z_priority;
1401 [[fallthrough]];
1402
1403 case WC_MAIN_WINDOW:
1404 return z_priority;
1405 }
1406}
1407
1414static void BringWindowToFront(Window *w, bool dirty)
1415{
1416 auto priority = GetWindowZPriority(w->window_class);
1417 WindowList::iterator dest = _z_windows.begin();
1418 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1419
1420 if (dest != w->z_position) {
1421 _z_windows.splice(dest, _z_windows, w->z_position);
1422 }
1423
1424 if (dirty) w->SetDirty();
1425}
1426
1434{
1435 /* Set up window properties; some of them are needed to set up smallest size below */
1436 this->window_class = this->window_desc.cls;
1437 this->SetWhiteBorder();
1438 if (this->window_desc.default_pos == WDP_CENTER) this->flags.Set(WindowFlag::Centred);
1439 this->owner = INVALID_OWNER;
1440 this->nested_focus = nullptr;
1441 this->window_number = window_number;
1442
1443 this->OnInit();
1444 /* Initialize smallest size. */
1445 this->nested_root->SetupSmallestSize(this);
1446 /* Initialize to smallest size. */
1447 this->nested_root->AssignSizePosition(SizingType::Smallest, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1448
1449 /* Further set up window properties,
1450 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1451 this->resize.step_width = this->nested_root->resize_x;
1452 this->resize.step_height = this->nested_root->resize_y;
1453
1454 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1455 * (so we don't interrupt typing) unless the new window has a text box. */
1456 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1457 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1458 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1459
1460 /* Insert the window into the correct location in the z-ordering. */
1461 BringWindowToFront(this, false);
1462}
1463
1471void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1472{
1473 this->left = x;
1474 this->top = y;
1475 this->width = sm_width;
1476 this->height = sm_height;
1477}
1478
1490void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1491{
1492 if (allow_resize) {
1493 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1494 def_height = std::max(def_height, this->height);
1495 /* Try to make windows smaller when our window is too small.
1496 * w->(width|height) is normally the same as min_(width|height),
1497 * but this way the GUIs can be made a little more dynamic;
1498 * one can use the same spec for multiple windows and those
1499 * can then determine the real minimum size of the window. */
1500 if (this->width != def_width || this->height != def_height) {
1501 /* Think about the overlapping toolbars when determining the minimum window size */
1502 int free_height = _screen.height;
1503 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1504 if (wt != nullptr) free_height -= wt->height;
1506 if (wt != nullptr) free_height -= wt->height;
1507
1508 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1509 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1510
1511 /* X and Y has to go by step.. calculate it.
1512 * The cast to int is necessary else x/y are implicitly cast to
1513 * unsigned int, which won't work. */
1514 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1515 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1516
1517 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1518 /* ResizeWindow() calls this->OnResize(). */
1519 } else {
1520 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1521 this->OnResize();
1522 }
1523 }
1524
1525 int nx = this->left;
1526 int ny = this->top;
1527
1528 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1529
1530 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1531 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1532 nx = std::max(nx, 0);
1533
1534 if (this->viewport != nullptr) {
1535 this->viewport->left += nx - this->left;
1536 this->viewport->top += ny - this->top;
1537 }
1538 this->left = nx;
1539 this->top = ny;
1540
1541 this->SetDirty();
1542}
1543
1556static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1557{
1558 int right = width + left;
1559 int bottom = height + top;
1560
1561 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1562
1563 /* Make sure it is not obscured by any window. */
1564 for (const Window *w : Window::Iterate()) {
1565 if (w->window_class == WC_MAIN_WINDOW) continue;
1566
1567 if (right > w->left &&
1568 w->left + w->width > left &&
1569 bottom > w->top &&
1570 w->top + w->height > top) {
1571 return false;
1572 }
1573 }
1574
1575 pos.x = left;
1576 pos.y = top;
1577 return true;
1578}
1579
1592static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1593{
1594 bool rtl = _current_text_dir == TD_RTL;
1595
1596 /* Left part of the rectangle may be at most 1/4 off-screen,
1597 * right part of the rectangle may be at most 1/2 off-screen
1598 */
1599 if (rtl) {
1600 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1601 } else {
1602 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1603 }
1604
1605 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1606 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1607
1608 /* Make sure it is not obscured by any window. */
1609 for (const Window *w : Window::Iterate()) {
1610 if (w->window_class == WC_MAIN_WINDOW) continue;
1611
1612 if (left + width > w->left &&
1613 w->left + w->width > left &&
1614 top + height > w->top &&
1615 w->top + w->height > top) {
1616 return false;
1617 }
1618 }
1619
1620 pos.x = left;
1621 pos.y = top;
1622 return true;
1623}
1624
1631static Point GetAutoPlacePosition(int width, int height)
1632{
1633 Point pt;
1634
1635 bool rtl = _current_text_dir == TD_RTL;
1636
1637 /* First attempt, try top-left of the screen */
1638 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1639 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1640 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1641
1642 /* Second attempt, try around all existing windows.
1643 * The new window must be entirely on-screen, and not overlap with an existing window.
1644 * Eight starting points are tried, two at each corner.
1645 */
1646 for (const Window *w : Window::Iterate()) {
1647 if (w->window_class == WC_MAIN_WINDOW) continue;
1648
1649 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1650 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1651 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1652 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1653 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1654 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1655 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1656 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1657 }
1658
1659 /* Third attempt, try around all existing windows.
1660 * The new window may be partly off-screen, and must not overlap with an existing window.
1661 * Only four starting points are tried.
1662 */
1663 for (const Window *w : Window::Iterate()) {
1664 if (w->window_class == WC_MAIN_WINDOW) continue;
1665
1666 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1667 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1668 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1669 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1670 }
1671
1672 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1673 * of the closebox
1674 */
1675 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1676 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1677 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1678
1679restart:
1680 for (const Window *w : Window::Iterate()) {
1681 if (w->left == left && w->top == top) {
1682 left += offset_x;
1683 top += offset_y;
1684 goto restart;
1685 }
1686 }
1687
1688 pt.x = left;
1689 pt.y = top;
1690 return pt;
1691}
1692
1700{
1701 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1702 assert(w != nullptr);
1703 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1704 return pt;
1705}
1706
1716{
1717 Point pt = GetToolbarAlignedWindowPosition(window_width);
1719 if (w != nullptr && w->top == pt.y && !_settings_client.gui.link_terraform_toolbar) {
1720 pt.x = w->left + (_current_text_dir == TD_RTL ? w->width : - window_width);
1721 }
1722 return pt;
1723}
1724
1742static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1743{
1744 Point pt;
1745 const Window *w;
1746
1747 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1748 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1749
1750 if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1751 bool rtl = _current_text_dir == TD_RTL;
1752 if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1753 pt.x = w->left + (rtl ? w->width - default_width : 0);
1754 pt.y = w->top + w->height;
1755 return pt;
1756 } else {
1757 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1758 * - Y position: closebox of parent + closebox of child + statusbar
1759 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1760 */
1761 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1762 if (w->top + 3 * indent_y < _screen.height) {
1763 pt.y = w->top + indent_y;
1764 int indent_close = NWidgetLeaf::closebox_dimension.width;
1765 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1766 if (_current_text_dir == TD_RTL) {
1767 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1768 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1769 } else {
1770 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1771 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1772 }
1773 }
1774 }
1775 }
1776
1777 switch (desc.default_pos) {
1778 case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1779 return GetToolbarAlignedWindowPosition(default_width);
1780
1781 case WDP_AUTO: // Find a good automatic position for the window
1782 return GetAutoPlacePosition(default_width, default_height);
1783
1784 case WDP_CENTER: // Centre the window horizontally
1785 pt.x = (_screen.width - default_width) / 2;
1786 pt.y = (_screen.height - default_height) / 2;
1787 break;
1788
1789 case WDP_MANUAL:
1790 pt.x = 0;
1791 pt.y = 0;
1792 break;
1793
1794 default:
1795 NOT_REACHED();
1796 }
1797
1798 return pt;
1799}
1800
1801/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1802{
1803 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1804}
1805
1813{
1814 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1815 this->nested_root->FillWidgetLookup(this->widget_lookup);
1816}
1817
1823{
1824 this->nested_root->AdjustPaddingForZoom();
1825 this->InitializeData(window_number);
1826 this->ApplyDefaults();
1827 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1828 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1829 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1830}
1831
1837{
1838 this->CreateNestedTree();
1839 this->FinishInitNested(window_number);
1840}
1841
1847{
1848 this->z_position = _z_windows.insert(_z_windows.end(), this);
1849}
1850
1859{
1860 for (Window *w : Window::IterateFromFront()) {
1861 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1862 return w;
1863 }
1864 }
1865
1866 return nullptr;
1867}
1868
1873{
1874 IConsoleClose();
1875
1876 _focused_window = nullptr;
1877 _mouseover_last_w = nullptr;
1878 _last_scroll_window = nullptr;
1879 _scrolling_viewport = false;
1880 _mouse_hovering = false;
1881
1883 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1884 NWidgetScrollbar::InvalidateDimensionCache();
1885
1887
1889}
1890
1895{
1897
1898 for (Window *w : Window::Iterate()) w->Close();
1899
1901
1902 assert(_z_windows.empty());
1903}
1904
1909{
1912 _thd.Reset();
1913}
1914
1915static void DecreaseWindowCounters()
1916{
1917 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1918
1919 for (Window *w : Window::Iterate()) {
1920 if (_scroller_click_timeout == 0) {
1921 /* Unclick scrollbar buttons if they are pressed. */
1922 for (auto &pair : w->widget_lookup) {
1923 NWidgetBase *nwid = pair.second;
1924 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1925 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1926 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1929 sb->SetDirty(w);
1930 }
1931 }
1932 }
1933 }
1934
1935 /* Handle editboxes */
1936 for (auto &pair : w->querystrings) {
1937 pair.second->HandleEditBox(w, pair.first);
1938 }
1939
1940 w->OnMouseLoop();
1941 }
1942
1943 for (Window *w : Window::Iterate()) {
1944 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1946
1947 w->OnTimeout();
1948 w->RaiseButtons(true);
1949 }
1950 }
1951}
1952
1953static void HandlePlacePresize()
1954{
1955 if (_special_mouse_mode != WSM_PRESIZE) return;
1956
1957 Window *w = _thd.GetCallbackWnd();
1958 if (w == nullptr) return;
1959
1960 Point pt = GetTileBelowCursor();
1961 if (pt.x == -1) {
1962 _thd.selend.x = -1;
1963 return;
1964 }
1965
1966 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1967}
1968
1974{
1976
1977 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1978
1979 Window *w = _thd.GetCallbackWnd();
1980 if (w != nullptr) {
1981 /* Send an event in client coordinates. */
1982 Point pt;
1983 pt.x = _cursor.pos.x - w->left;
1984 pt.y = _cursor.pos.y - w->top;
1985 if (_left_button_down) {
1986 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1987 } else {
1988 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1989 }
1990 }
1991
1992 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1993 return ES_HANDLED;
1994}
1995
1997static void HandleMouseOver()
1998{
1999 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2000
2001 /* We changed window, put an OnMouseOver event to the last window */
2002 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
2003 /* Reset mouse-over coordinates of previous window */
2004 Point pt = { -1, -1 };
2005 _mouseover_last_w->OnMouseOver(pt, 0);
2006 }
2007
2008 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
2010
2011 if (w != nullptr) {
2012 /* send an event in client coordinates. */
2013 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2014 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
2015 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
2016 }
2017}
2018
2024
2035static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
2036{
2037 if (v == nullptr) return;
2038
2039 const int min_visible = rect.Height();
2040
2041 int v_bottom = v->top + v->height - 1;
2042 int v_right = v->left + v->width - 1;
2043 int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2044
2045 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2046 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2047
2048 /* Vertically, the rectangle is hidden behind v. */
2049 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2050 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2051 return;
2052 }
2053 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2054 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2055 return;
2056 }
2057
2058 /* Horizontally also hidden, force movement to a safe area. */
2059 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2060 *nx = v->left - min_visible - rect.left;
2061 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2062 *nx = v_right + min_visible - rect.right;
2063 } else {
2064 *ny = safe_y;
2065 }
2066}
2067
2075static void EnsureVisibleCaption(Window *w, int nx, int ny)
2076{
2077 /* Search for the title bar rectangle. */
2078 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2079 if (caption != nullptr) {
2080 const Rect caption_rect = caption->GetCurrentRect();
2081
2082 const int min_visible = caption_rect.Height();
2083
2084 /* Make sure the window doesn't leave the screen */
2085 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2086 ny = Clamp(ny, 0, _screen.height - min_visible);
2087
2088 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2089 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2090 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2091 }
2092
2093 if (w->viewport != nullptr) {
2094 w->viewport->left += nx - w->left;
2095 w->viewport->top += ny - w->top;
2096 }
2097
2098 w->left = nx;
2099 w->top = ny;
2100}
2101
2113void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2114{
2115 if (delta_x != 0 || delta_y != 0) {
2116 if (clamp_to_screen) {
2117 /* Determine the new right/bottom position. If that is outside of the bounds of
2118 * the resolution clamp it in such a manner that it stays within the bounds. */
2119 int new_right = w->left + w->width + delta_x;
2120 int new_bottom = w->top + w->height + delta_y;
2121 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2122 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2123 }
2124
2125 w->SetDirty();
2126
2127 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);
2128 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);
2129 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2130 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2131
2132 w->nested_root->AssignSizePosition(SizingType::Resize, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, _current_text_dir == TD_RTL);
2133 w->width = w->nested_root->current_x;
2134 w->height = w->nested_root->current_y;
2135 }
2136
2137 EnsureVisibleCaption(w, w->left, w->top);
2138
2139 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2140 if (schedule_resize) {
2141 w->ScheduleResize();
2142 } else {
2143 w->OnResize();
2144 }
2145 w->SetDirty();
2146}
2147
2154{
2156 return (w == nullptr) ? 0 : w->top + w->height;
2157}
2158
2165{
2167 return (w == nullptr) ? _screen.height : w->top;
2168}
2169
2170static bool _dragging_window;
2171
2177{
2178 /* Get out immediately if no window is being dragged at all. */
2179 if (!_dragging_window) return ES_NOT_HANDLED;
2180
2181 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2182 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2183
2184 /* Otherwise find the window... */
2185 for (Window *w : Window::Iterate()) {
2187 /* Stop the dragging if the left mouse button was released */
2188 if (!_left_button_down) {
2190 break;
2191 }
2192
2193 w->SetDirty();
2194
2195 int x = _cursor.pos.x + _drag_delta.x;
2196 int y = _cursor.pos.y + _drag_delta.y;
2197 int nx = x;
2198 int ny = y;
2199
2200 if (_settings_client.gui.window_snap_radius != 0) {
2201 int hsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2202 int vsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2203 int delta;
2204
2205 for (const Window *v : Window::Iterate()) {
2206 if (v == w) continue; // Don't snap at yourself
2207
2208 if (y + w->height > v->top && y < v->top + v->height) {
2209 /* Your left border <-> other right border */
2210 delta = abs(v->left + v->width - x);
2211 if (delta <= hsnap) {
2212 nx = v->left + v->width;
2213 hsnap = delta;
2214 }
2215
2216 /* Your right border <-> other left border */
2217 delta = abs(v->left - x - w->width);
2218 if (delta <= hsnap) {
2219 nx = v->left - w->width;
2220 hsnap = delta;
2221 }
2222 }
2223
2224 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2225 /* Your left border <-> other left border */
2226 delta = abs(v->left - x);
2227 if (delta <= hsnap) {
2228 nx = v->left;
2229 hsnap = delta;
2230 }
2231
2232 /* Your right border <-> other right border */
2233 delta = abs(v->left + v->width - x - w->width);
2234 if (delta <= hsnap) {
2235 nx = v->left + v->width - w->width;
2236 hsnap = delta;
2237 }
2238 }
2239
2240 if (x + w->width > v->left && x < v->left + v->width) {
2241 /* Your top border <-> other bottom border */
2242 delta = abs(v->top + v->height - y);
2243 if (delta <= vsnap) {
2244 ny = v->top + v->height;
2245 vsnap = delta;
2246 }
2247
2248 /* Your bottom border <-> other top border */
2249 delta = abs(v->top - y - w->height);
2250 if (delta <= vsnap) {
2251 ny = v->top - w->height;
2252 vsnap = delta;
2253 }
2254 }
2255
2256 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2257 /* Your top border <-> other top border */
2258 delta = abs(v->top - y);
2259 if (delta <= vsnap) {
2260 ny = v->top;
2261 vsnap = delta;
2262 }
2263
2264 /* Your bottom border <-> other bottom border */
2265 delta = abs(v->top + v->height - y - w->height);
2266 if (delta <= vsnap) {
2267 ny = v->top + v->height - w->height;
2268 vsnap = delta;
2269 }
2270 }
2271 }
2272 }
2273
2274 EnsureVisibleCaption(w, nx, ny);
2275
2276 w->SetDirty();
2277 return ES_HANDLED;
2279 /* Stop the sizing if the left mouse button was released */
2280 if (!_left_button_down) {
2283 w->SetDirty();
2284 break;
2285 }
2286
2287 /* Compute difference in pixels between cursor position and reference point in the window.
2288 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2289 */
2290 int x, y = _cursor.pos.y - _drag_delta.y;
2292 x = _drag_delta.x - _cursor.pos.x;
2293 } else {
2294 x = _cursor.pos.x - _drag_delta.x;
2295 }
2296
2297 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2298 if (w->resize.step_width == 0) x = 0;
2299 if (w->resize.step_height == 0) y = 0;
2300
2301 /* Check the resize button won't go past the bottom of the screen */
2302 if (w->top + w->height + y > _screen.height) {
2303 y = _screen.height - w->height - w->top;
2304 }
2305
2306 /* X and Y has to go by step.. calculate it.
2307 * The cast to int is necessary else x/y are implicitly cast to
2308 * unsigned int, which won't work. */
2309 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2310 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2311
2312 /* Check that we don't go below the minimum set size */
2313 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2314 x = w->nested_root->smallest_x - w->width;
2315 }
2316 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2317 y = w->nested_root->smallest_y - w->height;
2318 }
2319
2320 /* Window already on size */
2321 if (x == 0 && y == 0) return ES_HANDLED;
2322
2323 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2324 _drag_delta.y += y;
2325 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2326 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2327 w->SetDirty();
2328 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2329 /* ResizeWindow() below ensures marking new position as dirty. */
2330 } else {
2331 _drag_delta.x += x;
2332 }
2333
2334 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2335 ResizeWindow(w, x, y);
2336 return ES_HANDLED;
2337 }
2338 }
2339
2340 _dragging_window = false;
2341 return ES_HANDLED;
2342}
2343
2349{
2352 _dragging_window = true;
2353
2354 _drag_delta.x = w->left - _cursor.pos.x;
2355 _drag_delta.y = w->top - _cursor.pos.y;
2356
2358}
2359
2365static void StartWindowSizing(Window *w, bool to_left)
2366{
2369 _dragging_window = true;
2370
2371 _drag_delta.x = _cursor.pos.x;
2372 _drag_delta.y = _cursor.pos.y;
2373
2375}
2376
2382{
2383 int i;
2385 bool rtl = false;
2386
2387 if (sb->type == NWID_HSCROLLBAR) {
2388 i = _cursor.pos.x - _cursorpos_drag_start.x;
2389 rtl = _current_text_dir == TD_RTL;
2390 } else {
2391 i = _cursor.pos.y - _cursorpos_drag_start.y;
2392 }
2393
2394 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2395 if (_scroller_click_timeout == 1) {
2396 _scroller_click_timeout = 3;
2397 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2399 w->SetDirty();
2400 }
2401 }
2402 return;
2403 }
2404
2405 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2406 int range = sb->GetCount() - sb->GetCapacity();
2407 if (range <= 0) return;
2408
2409 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2410 if (rtl) pos = range - pos;
2411 if (sb->SetPosition(pos)) {
2413 w->SetDirty();
2414 }
2415}
2416
2422{
2423 for (Window *w : Window::Iterate()) {
2424 if (w->mouse_capture_widget >= 0) {
2425 /* Abort if no button is clicked any more. */
2426 if (!_left_button_down) {
2429 return ES_HANDLED;
2430 }
2431
2432 /* Handle scrollbar internally, or dispatch click event */
2434 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2436 } else {
2437 /* If cursor hasn't moved, there is nothing to do. */
2438 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2439
2440 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2441 w->OnClick(pt, w->mouse_capture_widget, 0);
2442 }
2443 return ES_HANDLED;
2444 }
2445 }
2446
2447 return ES_NOT_HANDLED;
2448}
2449
2455{
2456 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2457
2459
2460 /* When we don't have a last scroll window we are starting to scroll.
2461 * When the last scroll window and this are not the same we went
2462 * outside of the window and should not left-mouse scroll anymore. */
2463 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2464
2465 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))) {
2466 _cursor.fix_at = false;
2467 _scrolling_viewport = false;
2468 _last_scroll_window = nullptr;
2469 return ES_NOT_HANDLED;
2470 }
2471
2472 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2473 /* If the main window is following a vehicle, then first let go of it! */
2474 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
2475 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2476 return ES_NOT_HANDLED;
2477 }
2478
2479 Point delta;
2480 if (scrollwheel_scrolling) {
2481 /* We are using scrollwheels for scrolling */
2482 /* Use the integer part for movement */
2483 delta.x = static_cast<int>(_cursor.h_wheel);
2484 delta.y = static_cast<int>(_cursor.v_wheel);
2485 /* Keep the fractional part so that subtle movement is accumulated */
2486 float temp;
2487 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2488 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2489 } else {
2491 delta.x = -_cursor.delta.x;
2492 delta.y = -_cursor.delta.y;
2493 } else {
2494 delta.x = _cursor.delta.x;
2495 delta.y = _cursor.delta.y;
2496 }
2497 }
2498
2499 /* Create a scroll-event and send it to the window */
2500 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2501
2502 _cursor.delta.x = 0;
2503 _cursor.delta.y = 0;
2504 _cursor.wheel_moved = false;
2505 return ES_HANDLED;
2506}
2507
2519{
2520 bool bring_to_front = false;
2521
2522 if (w->window_class == WC_MAIN_WINDOW ||
2523 IsVitalWindow(w) ||
2524 w->window_class == WC_TOOLTIPS ||
2526 return true;
2527 }
2528
2529 /* Use unshaded window size rather than current size for shaded windows. */
2530 int w_width = w->width;
2531 int w_height = w->height;
2532 if (w->IsShaded()) {
2533 w_width = w->unshaded_size.width;
2534 w_height = w->unshaded_size.height;
2535 }
2536
2538 ++it;
2539 for (; !it.IsEnd(); ++it) {
2540 Window *u = *it;
2541 /* A modal child will prevent the activation of the parent window */
2543 u->SetWhiteBorder();
2544 u->SetDirty();
2545 return false;
2546 }
2547
2548 if (u->window_class == WC_MAIN_WINDOW ||
2549 IsVitalWindow(u) ||
2550 u->window_class == WC_TOOLTIPS ||
2552 continue;
2553 }
2554
2555 /* Window sizes don't interfere, leave z-order alone */
2556 if (w->left + w_width <= u->left ||
2557 u->left + u->width <= w->left ||
2558 w->top + w_height <= u->top ||
2559 u->top + u->height <= w->top) {
2560 continue;
2561 }
2562
2563 bring_to_front = true;
2564 }
2565
2566 if (bring_to_front) BringWindowToFront(w);
2567 return true;
2568}
2569
2578EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2579{
2580 QueryString *query = this->GetQueryString(wid);
2581 if (query == nullptr) return ES_NOT_HANDLED;
2582
2583 int action = QueryString::ACTION_NOTHING;
2584
2585 switch (query->text.HandleKeyPress(key, keycode)) {
2586 case HKPR_EDITING:
2587 this->SetWidgetDirty(wid);
2588 this->OnEditboxChanged(wid);
2589 break;
2590
2591 case HKPR_CURSOR:
2592 this->SetWidgetDirty(wid);
2593 /* For the OSK also invalidate the parent window */
2594 if (this->window_class == WC_OSK) this->InvalidateData();
2595 break;
2596
2597 case HKPR_CONFIRM:
2598 if (this->window_class == WC_OSK) {
2599 this->OnClick(Point(), WID_OSK_OK, 1);
2600 } else if (query->ok_button >= 0) {
2601 this->OnClick(Point(), query->ok_button, 1);
2602 } else {
2603 action = query->ok_button;
2604 }
2605 break;
2606
2607 case HKPR_CANCEL:
2608 if (this->window_class == WC_OSK) {
2609 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2610 } else if (query->cancel_button >= 0) {
2611 this->OnClick(Point(), query->cancel_button, 1);
2612 } else {
2613 action = query->cancel_button;
2614 }
2615 break;
2616
2617 case HKPR_NOT_HANDLED:
2618 return ES_NOT_HANDLED;
2619
2620 default: break;
2621 }
2622
2623 switch (action) {
2625 this->UnfocusFocusedWidget();
2626 break;
2627
2629 if (query->text.GetText().empty()) {
2630 /* If already empty, unfocus instead */
2631 this->UnfocusFocusedWidget();
2632 } else {
2633 query->text.DeleteAll();
2634 this->SetWidgetDirty(wid);
2635 this->OnEditboxChanged(wid);
2636 }
2637 break;
2638
2639 default:
2640 break;
2641 }
2642
2643 return ES_HANDLED;
2644}
2645
2650void HandleToolbarHotkey(int hotkey)
2651{
2652 assert(HasModalProgress() || IsLocalCompany());
2653
2655 if (w != nullptr) {
2656 if (w->window_desc.hotkeys != nullptr) {
2657 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2658 }
2659 }
2660}
2661
2667void HandleKeypress(uint keycode, char32_t key)
2668{
2669 /* World generation is multithreaded and messes with companies.
2670 * But there is no company related window open anyway, so _current_company is not used. */
2671 assert(HasModalProgress() || IsLocalCompany());
2672
2673 /*
2674 * The Unicode standard defines an area called the private use area. Code points in this
2675 * area are reserved for private use and thus not portable between systems. For instance,
2676 * Apple defines code points for the arrow keys in this area, but these are only printable
2677 * on a system running OS X. We don't want these keys to show up in text fields and such,
2678 * and thus we have to clear the unicode character when we encounter such a key.
2679 */
2680 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2681
2682 /*
2683 * If both key and keycode is zero, we don't bother to process the event.
2684 */
2685 if (key == 0 && keycode == 0) return;
2686
2687 /* Check if the focused window has a focused editbox */
2688 if (EditBoxInGlobalFocus()) {
2689 /* All input will in this case go to the focused editbox */
2690 if (_focused_window->window_class == WC_CONSOLE) {
2691 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2692 } else {
2693 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == ES_HANDLED) return;
2694 }
2695 }
2696
2697 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2698 for (Window *w : Window::IterateFromFront()) {
2699 if (w->window_class == WC_MAIN_TOOLBAR) continue;
2700 if (w->window_desc.hotkeys != nullptr) {
2701 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2702 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2703 }
2704 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2705 }
2706
2708 /* When there is no toolbar w is null, check for that */
2709 if (w != nullptr) {
2710 if (w->window_desc.hotkeys != nullptr) {
2711 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2712 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2713 }
2714 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2715 }
2716
2717 HandleGlobalHotkeys(key, keycode);
2718}
2719
2724{
2725 /* Call the event, start with the uppermost window. */
2726 for (Window *w : Window::IterateFromFront()) {
2727 if (w->OnCTRLStateChange() == ES_HANDLED) return;
2728 }
2729}
2730
2740/* 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)
2741{
2742 QueryString *query = this->GetQueryString(wid);
2743 if (query == nullptr) return;
2744
2745 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2746 this->SetWidgetDirty(wid);
2747 this->OnEditboxChanged(wid);
2748 }
2749}
2750
2759void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2760{
2761 if (!EditBoxInGlobalFocus()) return;
2762
2763 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2764}
2765
2770static void HandleAutoscroll()
2771{
2772 if (_game_mode == GM_MENU || HasModalProgress()) return;
2773 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
2774 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2775
2776 int x = _cursor.pos.x;
2777 int y = _cursor.pos.y;
2778 Window *w = FindWindowFromPt(x, y);
2779 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2780 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
2781
2782 Viewport *vp = IsPtInWindowViewport(w, x, y);
2783 if (vp == nullptr) return;
2784
2785 x -= vp->left;
2786 y -= vp->top;
2787
2788 /* here allows scrolling in both x and y axis */
2789 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2790 static const int SCROLLSPEED = 3;
2791 if (x - 15 < 0) {
2792 w->viewport->CancelFollow(*w);
2793 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2794 } else if (15 - (vp->width - x) > 0) {
2795 w->viewport->CancelFollow(*w);
2796 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2797 }
2798 if (y - 15 < 0) {
2799 w->viewport->CancelFollow(*w);
2800 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2801 } else if (15 - (vp->height - y) > 0) {
2802 w->viewport->CancelFollow(*w);
2803 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2804 }
2805}
2806
2807enum MouseClick : uint8_t {
2808 MC_NONE = 0,
2809 MC_LEFT,
2810 MC_RIGHT,
2811 MC_DOUBLE_LEFT,
2812 MC_HOVER,
2813};
2814
2815static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2816static constexpr int MAX_OFFSET_HOVER = 5;
2817
2819
2820const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK{500};
2821
2822static void ScrollMainViewport(int x, int y)
2823{
2824 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2825 Window *w = GetMainWindow();
2826 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2827 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2828 }
2829}
2830
2840static const int8_t scrollamt[16][2] = {
2841 { 0, 0},
2842 {-2, 0},
2843 { 0, -2},
2844 {-2, -1},
2845 { 2, 0},
2846 { 0, 0},
2847 { 2, -1},
2848 { 0, -2},
2849 { 0, 2},
2850 {-2, 1},
2851 { 0, 0},
2852 {-2, 0},
2853 { 2, 1},
2854 { 0, 2},
2855 { 2, 0},
2856 { 0, 0},
2857};
2858
2859static void HandleKeyScrolling()
2860{
2861 /*
2862 * Check that any of the dirkeys is pressed and that the focused window
2863 * doesn't have an edit-box as focused widget.
2864 */
2865 if (_dirkeys && !EditBoxInGlobalFocus()) {
2866 int factor = _shift_pressed ? 50 : 10;
2867
2868 if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2869 /* Key scrolling stops following a vehicle. */
2870 Window *main_window = GetMainWindow();
2871 main_window->viewport->CancelFollow(*main_window);
2872 }
2873
2874 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2875 }
2876}
2877
2878static void MouseLoop(MouseClick click, int mousewheel)
2879{
2880 /* World generation is multithreaded and messes with companies.
2881 * But there is no company related window open anyway, so _current_company is not used. */
2882 assert(HasModalProgress() || IsLocalCompany());
2883
2884 HandlePlacePresize();
2886
2887 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2888 if (HandleMouseDragDrop() == ES_HANDLED) return;
2889 if (HandleWindowDragging() == ES_HANDLED) return;
2890 if (HandleActiveWidget() == ES_HANDLED) return;
2891 if (HandleViewportScroll() == ES_HANDLED) return;
2892
2894
2895 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2896 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2897
2898 int x = _cursor.pos.x;
2899 int y = _cursor.pos.y;
2900 Window *w = FindWindowFromPt(x, y);
2901 if (w == nullptr) return;
2902
2903 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2904 Viewport *vp = IsPtInWindowViewport(w, x, y);
2905
2906 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2907 if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2908
2909 if (mousewheel != 0) {
2910 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2911 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) {
2912 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2913 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2914 }
2915 }
2916
2917 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2918 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2919 }
2920
2921 if (vp != nullptr) {
2922 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2923 _scrolling_viewport = true;
2924 _cursor.fix_at = true;
2925 return;
2926 }
2927
2928 switch (click) {
2929 case MC_DOUBLE_LEFT:
2930 case MC_LEFT:
2931 if (HandleViewportClicked(*vp, x, y)) return;
2933 _settings_client.gui.scroll_mode == ViewportScrollMode::MapLMB) {
2934 _scrolling_viewport = true;
2935 _cursor.fix_at = false;
2936 return;
2937 }
2938 break;
2939
2940 case MC_RIGHT:
2942 _settings_client.gui.scroll_mode != ViewportScrollMode::MapLMB) {
2943 _scrolling_viewport = true;
2944 _cursor.fix_at = (_settings_client.gui.scroll_mode == ViewportScrollMode::ViewportRMBFixed ||
2946 DispatchRightClickEvent(w, x - w->left, y - w->top);
2947 return;
2948 }
2949 break;
2950
2951 default:
2952 break;
2953 }
2954 }
2955
2956 switch (click) {
2957 case MC_LEFT:
2958 case MC_DOUBLE_LEFT:
2959 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2960 return;
2961
2962 default:
2963 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2964 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2965 * Simulate a right button click so we can get started. */
2966 [[fallthrough]];
2967
2968 case MC_RIGHT:
2969 DispatchRightClickEvent(w, x - w->left, y - w->top);
2970 return;
2971
2972 case MC_HOVER:
2973 DispatchHoverEvent(w, x - w->left, y - w->top);
2974 break;
2975 }
2976
2977 /* We're not doing anything with 2D scrolling, so reset the value. */
2978 _cursor.h_wheel = 0.0f;
2979 _cursor.v_wheel = 0.0f;
2980 _cursor.wheel_moved = false;
2981}
2982
2987{
2988 /* World generation is multithreaded and messes with companies.
2989 * But there is no company related window open anyway, so _current_company is not used. */
2990 assert(HasModalProgress() || IsLocalCompany());
2991
2992 static std::chrono::steady_clock::time_point double_click_time = {};
2993 static Point double_click_pos = {0, 0};
2994
2995 /* Mouse event? */
2996 MouseClick click = MC_NONE;
2998 click = MC_LEFT;
2999 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
3000 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
3001 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
3002 click = MC_DOUBLE_LEFT;
3003 }
3004 double_click_time = std::chrono::steady_clock::now();
3005 double_click_pos = _cursor.pos;
3006 _left_button_clicked = true;
3007 } else if (_right_button_clicked) {
3008 _right_button_clicked = false;
3009 click = MC_RIGHT;
3010 }
3011
3012 int mousewheel = 0;
3013 if (_cursor.wheel) {
3014 mousewheel = _cursor.wheel;
3015 _cursor.wheel = 0;
3016 }
3017
3018 static std::chrono::steady_clock::time_point hover_time = {};
3019 static Point hover_pos = {0, 0};
3020
3021 if (_settings_client.gui.hover_delay_ms > 0) {
3022 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
3023 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
3024 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
3025 hover_pos = _cursor.pos;
3026 hover_time = std::chrono::steady_clock::now();
3027 _mouse_hovering = false;
3028 } else if (!_mouse_hovering) {
3029 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
3030 click = MC_HOVER;
3031 _mouse_hovering = true;
3032 hover_time = std::chrono::steady_clock::now();
3033 }
3034 }
3035 }
3036
3037 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
3038 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
3040 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
3041 _newgrf_debug_sprite_picker.sprites.clear();
3042 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3044 } else {
3045 MouseLoop(click, mousewheel);
3046 }
3047
3048 /* We have moved the mouse the required distance,
3049 * no need to move it at any later time. */
3050 _cursor.delta.x = 0;
3051 _cursor.delta.y = 0;
3052}
3053
3057static void CheckSoftLimit()
3058{
3059 if (_settings_client.gui.window_soft_limit == 0) return;
3060
3061 for (;;) {
3062 uint deletable_count = 0;
3063 Window *last_deletable = nullptr;
3064 for (Window *w : Window::IterateFromFront()) {
3065 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3066
3067 last_deletable = w;
3068 deletable_count++;
3069 }
3070
3071 /* We've not reached the soft limit yet. */
3072 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3073
3074 assert(last_deletable != nullptr);
3075 last_deletable->Close();
3076 }
3077}
3078
3083{
3084 /* World generation is multithreaded and messes with companies.
3085 * But there is no company related window open anyway, so _current_company is not used. */
3086 assert(HasModalProgress() || IsLocalCompany());
3087
3089
3090 /* Process scheduled window deletion. */
3092
3093 /* HandleMouseEvents was already called for this tick */
3095}
3096
3097static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3098
3099bool CanContinueRealtimeTick()
3100{
3101 auto now = std::chrono::steady_clock::now();
3102 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3103}
3104
3110{
3111 _realtime_tick_start = std::chrono::steady_clock::now();
3112 for (Window *w : Window::Iterate()) {
3113 w->OnRealtimeTick(delta_ms);
3114 }
3115}
3116
3118static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3119 extern int _caret_timer;
3120 _caret_timer += 3;
3121 CursorTick();
3122
3123 HandleKeyScrolling();
3125 DecreaseWindowCounters();
3126});
3127
3131});
3132
3134static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3135 if (_network_dedicated) return;
3136
3137 for (Window *w : Window::Iterate()) {
3140 w->SetDirty();
3141 }
3142 }
3143});
3144
3149{
3150 static auto last_time = std::chrono::steady_clock::now();
3151 auto now = std::chrono::steady_clock::now();
3152 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3153
3154 if (delta_ms.count() == 0) return;
3155
3156 last_time = now;
3157
3160
3162
3164 CallWindowRealtimeTickEvent(delta_ms.count());
3165
3166 /* Process invalidations before anything else. */
3167 for (Window *w : Window::Iterate()) {
3171 }
3172
3173 /* Skip the actual drawing on dedicated servers without screen.
3174 * But still empty the invalidation queues above. */
3175 if (_network_dedicated) return;
3176
3178
3179 for (Window *w : Window::Iterate()) {
3180 /* Update viewport only if window is not shaded. */
3181 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3182 }
3184 /* Redraw mouse cursor in case it was hidden */
3185 DrawMouseCursor();
3186
3187 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3188 /* We are done with the last draw-frame, so we know what sprites we
3189 * clicked on. Reset the picker mode and invalidate the window. */
3190 _newgrf_debug_sprite_picker.mode = SPM_NONE;
3192 }
3193}
3194
3200void SetWindowDirty(WindowClass cls, WindowNumber number)
3201{
3202 for (const Window *w : Window::Iterate()) {
3203 if (w->window_class == cls && w->window_number == number) {
3204 w->SetDirty();
3205 return;
3206 }
3207 }
3208}
3209
3216void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
3217{
3218 for (const Window *w : Window::Iterate()) {
3219 if (w->window_class == cls && w->window_number == number) {
3220 w->SetWidgetDirty(widget_index);
3221 return;
3222 }
3223 }
3224}
3225
3230void SetWindowClassesDirty(WindowClass cls)
3231{
3232 for (const Window *w : Window::Iterate()) {
3233 if (w->window_class == cls) w->SetDirty();
3234 }
3235}
3236
3241{
3242 this->scheduled_resize = true;
3243}
3244
3249{
3250 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3251 while (this->scheduled_resize) {
3252 this->scheduled_resize = false;
3253 this->OnResize();
3254 }
3255}
3256
3262void Window::InvalidateData(int data, bool gui_scope)
3263{
3264 this->SetDirty();
3265 if (!gui_scope) {
3266 /* Schedule GUI-scope invalidation for next redraw. */
3267 this->scheduled_invalidation_data.push_back(data);
3268 }
3269 this->OnInvalidateData(data, gui_scope);
3270}
3271
3276{
3277 for (int data : this->scheduled_invalidation_data) {
3278 if (this->window_class == WC_INVALID) break;
3279 this->OnInvalidateData(data, true);
3280 }
3281 this->scheduled_invalidation_data.clear();
3282}
3283
3288{
3289 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3290
3291 for (const auto &pair : this->widget_lookup) {
3292 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3293 }
3294}
3295
3322void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3323{
3324 for (Window *w : Window::Iterate()) {
3325 if (w->window_class == cls && w->window_number == number) {
3326 w->InvalidateData(data, gui_scope);
3327 return;
3328 }
3329 }
3330}
3331
3340void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3341{
3342 for (Window *w : Window::Iterate()) {
3343 if (w->window_class == cls) {
3344 w->InvalidateData(data, gui_scope);
3345 }
3346 }
3347}
3348
3353{
3354 for (Window *w : Window::Iterate()) {
3355 w->OnGameTick();
3356 }
3357}
3358
3366{
3367 /* Note: the container remains stable, even when deleting windows. */
3368 for (Window *w : Window::Iterate()) {
3370 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3371
3372 w->Close();
3373 }
3374 }
3375}
3376
3385{
3386 /* Note: the container remains stable, even when closing windows. */
3387 for (Window *w : Window::Iterate()) {
3389 w->Close();
3390 }
3391 }
3392}
3393
3398{
3400 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3401 InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3402 CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3403}
3404
3410{
3411 /* Note: the container remains stable, even when deleting windows. */
3412 for (Window *w : Window::Iterate()) {
3414 w->Close();
3415 }
3416 }
3417
3418 for (const Window *w : Window::Iterate()) w->SetDirty();
3419}
3420
3427
3428void ReInitWindow(Window *w, bool zoom_changed)
3429{
3430 if (w == nullptr) return;
3431 if (zoom_changed) {
3432 w->nested_root->AdjustPaddingForZoom();
3434 }
3435 w->ReInit();
3436}
3437
3439void ReInitAllWindows(bool zoom_changed)
3440{
3442 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3443 NWidgetScrollbar::InvalidateDimensionCache();
3444
3446
3447 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3448 * so EnsureVisibleCaption uses the updated size information. */
3449 ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3450 ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3451 for (Window *w : Window::Iterate()) {
3452 if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3453 ReInitWindow(w, zoom_changed);
3454 }
3455
3458
3459 /* Make sure essential parts of all windows are visible */
3460 RelocateAllWindows(_screen.width, _screen.height);
3462}
3463
3471static int PositionWindow(Window *w, WindowClass clss, int setting)
3472{
3473 if (w == nullptr || w->window_class != clss) {
3474 w = FindWindowById(clss, 0);
3475 }
3476 if (w == nullptr) return 0;
3477
3478 int old_left = w->left;
3479 switch (setting) {
3480 case 1: w->left = (_screen.width - w->width) / 2; break;
3481 case 2: w->left = _screen.width - w->width; break;
3482 default: w->left = 0; break;
3483 }
3484 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3485 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3486 return w->left;
3487}
3488
3495{
3496 Debug(misc, 5, "Repositioning Main Toolbar...");
3497 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
3498}
3499
3506{
3507 Debug(misc, 5, "Repositioning statusbar...");
3508 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
3509}
3510
3517{
3518 Debug(misc, 5, "Repositioning news message...");
3519 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
3520}
3521
3528{
3529 Debug(misc, 5, "Repositioning network chat window...");
3530 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
3531}
3532
3533
3540{
3541 for (const Window *w : Window::Iterate()) {
3542 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3543 w->viewport->follow_vehicle = to_index;
3544 w->SetDirty();
3545 }
3546 }
3547}
3548
3549
3555void RelocateAllWindows(int neww, int newh)
3556{
3558
3559 /* Reposition toolbar then status bar before other all windows. */
3560 if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3561 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3562 wt->left = PositionMainToolbar(wt);
3563 }
3564
3565 if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3566 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3567 ws->top = newh - ws->height;
3568 ws->left = PositionStatusbar(ws);
3569 }
3570
3571 for (Window *w : Window::Iterate()) {
3572 int left, top;
3573 /* XXX - this probably needs something more sane. For example specifying
3574 * in a 'backup'-desc that the window should always be centered. */
3575 switch (w->window_class) {
3576 case WC_MAIN_WINDOW:
3577 case WC_BOOTSTRAP:
3578 case WC_HIGHSCORE:
3579 case WC_ENDSCREEN:
3580 ResizeWindow(w, neww, newh);
3581 continue;
3582
3583 case WC_MAIN_TOOLBAR:
3584 case WC_STATUS_BAR:
3585 continue;
3586
3587 case WC_NEWS_WINDOW:
3588 top = newh - w->height;
3589 left = PositionNewsMessage(w);
3590 break;
3591
3593 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3594
3595 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3596 left = PositionNetworkChatWindow(w);
3597 break;
3598
3599 case WC_CONSOLE:
3600 IConsoleResize(w);
3601 continue;
3602
3603 default: {
3604 if (w->flags.Test(WindowFlag::Centred)) {
3605 top = (newh - w->height) >> 1;
3606 left = (neww - w->width) >> 1;
3607 break;
3608 }
3609
3610 left = w->left;
3611 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3612 if (left < 0) left = 0;
3613
3614 top = w->top;
3615 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3616 break;
3617 }
3618 }
3619
3620 EnsureVisibleCaption(w, left, top);
3621 }
3622}
3623
3629void PickerWindowBase::Close([[maybe_unused]] int data)
3630{
3632 this->Window::Close();
3633}
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:139
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:951
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:1291
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1264
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:2675
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:3629
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
bool UpdatePosition(int difference, Scrollbar::Stepping unit=Stepping::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:3434
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:980
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:407
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:67
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:70
Basic functions/variables used all over the place.
void NetworkDrawChatMessage()
Draw the chat message-box.
void NetworkReInitChatBoxSize()
Initialize all font-dependent chat box sizes.
void NetworkUndrawChatMessage()
Hide the chatbox.
Network functions used by other parts of OpenTTD.
Functions/types related to NewGRF debugging.
NewGrfDebugSpritePicker _newgrf_debug_sprite_picker
The sprite picker.
Functions related to news.
void InitNewsItemStructs()
Initialize the news-items data structures.
Definition news_gui.cpp: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:874
void IniSaveWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Save a WindowDesc to config.
Definition settings.cpp:885
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:46
int CheckMatch(uint16_t keycode, bool global_only=false) const
Check if a keycode is bound to something.
Definition hotkeys.cpp:302
Ini file that supports both loading and saving.
Definition ini_type.h:87
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:150
~WindowDesc()
Remove ourselves from the global list of window descs.
Definition window.cpp:140
static void SaveToConfig()
Save all WindowDesc settings to _windows_file.
Definition window.cpp:187
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:160
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:168
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:120
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:378
void SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
Sets the highlighted status of a widget.
Definition window.cpp:252
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:992
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1117
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:799
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1822
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:204
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:1471
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:3262
virtual EventState OnKeyPress(char32_t key, uint16_t keycode)
A key has been pressed.
Definition window_gui.h:654
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:941
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition window.cpp:1154
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:570
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:518
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:750
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition window_gui.h:875
virtual bool OnRightClick(Point pt, WidgetID widget)
A click with the right mouse button has been made on the window.
Definition window_gui.h:681
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:724
virtual void OnDropdownSelect(WidgetID widget, int index, int click_result)
A dropdown option associated to this window has been selected.
Definition window_gui.h:776
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition window.cpp:3275
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1089
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:483
virtual void OnMouseLoop()
Called for every mouse loop run, which is at least once per (game) tick.
Definition window_gui.h:745
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition window.cpp:1035
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:3240
virtual void OnPaint()
The window must be repainted.
Definition window_gui.h:599
virtual void OnDragDrop(Point pt, WidgetID widget)
A dragged 'object' has been released.
Definition window_gui.h:711
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1812
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:756
virtual void OnMouseWheel(int wheel, WidgetID widget)
The mouse wheel has been turned.
Definition window_gui.h:739
AllWindows< true > IterateFromFront
Iterate all windows in Z order from front to back.
Definition window_gui.h:942
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1104
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:499
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:535
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:544
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1801
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:391
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1490
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:298
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:2740
WindowIterator< false > IteratorToFront
Iterate in Z order towards front.
Definition window_gui.h:918
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:563
Window * FindChildWindow(WindowClass wc=WC_INVALID) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1061
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:784
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition window.cpp:367
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:672
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:347
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:422
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:1433
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:732
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1846
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:223
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:990
virtual bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition window_gui.h:697
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:663
void ProcessScheduledResize()
Process scheduled OnResize() event.
Definition window.cpp:3248
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2578
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:704
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:609
virtual void OnResize()
Called after the window got resized.
Definition window_gui.h:768
Window * FindChildWindowById(WindowClass wc, WindowNumber number) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1076
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:527
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1836
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h:761
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:327
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3287
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:584
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:407
bool IsWidgetHighlighted(WidgetID widget_index) const
Gets the highlighted status of a widget.
Definition window.cpp:282
void DisableAllWidgetHighlight()
Disable the highlighted status of all widgets.
Definition window.cpp:234
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:859
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:940
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:688
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:582
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:98
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:294
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:270
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
@ ScrollbarDown
Down-button is lowered bit.
@ DropdownClosed
Dropdown menu of the dropdown widget has closed.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ ScrollbarUp
Up-button is lowered bit.
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ Resize
Resize the nested widget tree.
@ 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:3505
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:2035
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2170
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:3409
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:1209
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:1742
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1195
const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK
Time between 2 left clicks before it becoming a double click.
Definition window.cpp:2820
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:869
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1238
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2723
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:897
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:3082
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3148
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3494
Window * _focused_window
Window that currently has focus.
Definition window.cpp:85
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3365
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:959
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2113
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:1592
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3527
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2365
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:1631
PreventHideDirection
Direction for moving the window.
Definition window.cpp:2020
@ PHD_DOWN
Below v is a safe position.
Definition window.cpp:2022
@ PHD_UP
Above v is a safe position.
Definition window.cpp:2021
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2770
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:2650
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3471
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:435
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:1556
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2815
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1181
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3539
static EventState HandleViewportScroll()
Handle viewport scrolling with the mouse.
Definition window.cpp:2454
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:832
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:630
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1858
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3397
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:2153
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:1414
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3439
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:2075
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1222
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2986
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:1699
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2176
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3516
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2164
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:804
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:1258
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2667
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3230
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:2816
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:475
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1908
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1330
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:461
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:3322
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3422
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:179
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1293
Point AlignInitialConstructionToolbar(int window_width)
Compute the position of the construction toolbars.
Definition window.cpp:1715
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3555
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:2381
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:2348
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3352
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2421
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:775
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:2840
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2518
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:3109
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:1973
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3384
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1997
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:3057
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1166
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1894
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1872
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:3216
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:2759
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3200
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:3340
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
Window * _focused_window
Window that currently has focus.
Definition window.cpp:85
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:435
@ 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:294
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