OpenTTD Source 20260731-master-g77ba2b244a
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 "script/api/script_event_types.hpp"
41#include "timer/timer.h"
42#include "timer/timer_window.h"
43
44#include "widgets/osk_widget.h"
45
46#include "table/strings.h"
47
48#include "safeguards.h"
49
51static Window *_mouseover_last_w = nullptr;
52static Window *_last_scroll_window = nullptr;
53
55WindowList _z_windows;
56
58/* static */ std::vector<Window *> Window::closed_windows;
59
63/* static */ void Window::DeleteClosedWindows()
64{
65 for (Window *w : Window::closed_windows) delete w;
67
68 /* Remove dead entries from the window list */
69 _z_windows.remove(nullptr);
70}
71
74
81
82Point _cursorpos_drag_start;
83
84int _scrollbar_start_pos;
85int _scrollbar_size;
86uint8_t _scroller_click_timeout = 0;
87
90
92
97std::vector<WindowDesc*> *_window_descs = nullptr;
98
100std::string _windows_file;
101
115WindowDesc::WindowDesc(WindowPosition def_pos, std::string_view ini_key, int16_t def_width_trad, int16_t def_height_trad,
116 WindowClass window_class, WindowClass parent_class, WindowDefaultFlags flags,
117 const std::span<const NWidgetPart> nwid_parts, HotkeyList *hotkeys,
118 const std::source_location location) :
119 source_location(location),
120 default_pos(def_pos),
121 cls(window_class),
122 parent_cls(parent_class),
124 flags(flags),
127 default_width_trad(def_width_trad),
128 default_height_trad(def_height_trad)
129{
130 if (_window_descs == nullptr) _window_descs = new std::vector<WindowDesc*>();
131 _window_descs->push_back(this);
132}
133
136{
137 _window_descs->erase(std::ranges::find(*_window_descs, this));
138}
139
146{
147 return this->pref_width != 0 ? this->pref_width : ScaleGUITrad(this->default_width_trad);
148}
149
156{
157 return this->pref_height != 0 ? this->pref_height : ScaleGUITrad(this->default_height_trad);
158}
159
164{
165 IniFile ini;
167 for (WindowDesc *wd : *_window_descs) {
168 if (wd->ini_key.empty()) continue;
169 IniLoadWindowSettings(ini, wd->ini_key, wd);
170 }
171}
172
174static bool DescSorter(WindowDesc * const &a, WindowDesc * const &b)
175{
176 return a->ini_key < b->ini_key;
177}
178
183{
184 /* Sort the stuff to get a nice ini file on first write */
185 std::sort(_window_descs->begin(), _window_descs->end(), DescSorter);
186
187 IniFile ini;
189 for (WindowDesc *wd : *_window_descs) {
190 if (wd->ini_key.empty()) continue;
191 IniSaveWindowSettings(ini, wd->ini_key, wd);
192 }
194}
195
200{
201 if (this->nested_root != nullptr && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != nullptr) {
202 if (this->window_desc.pref_sticky) this->flags.Set(WindowFlag::Sticky);
203 } else {
204 /* There is no stickybox; clear the preference in case someone tried to be funny */
205 this->window_desc.pref_sticky = false;
206 }
207}
208
218int Window::GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height) const
219{
220 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
221 if (line_height < 0) line_height = wid->resize_y;
222 if (clickpos < wid->pos_y + padding) return INT_MAX;
223 return (clickpos - wid->pos_y - padding) / line_height;
224}
225
230{
231 for (auto &pair : this->widget_lookup) {
232 NWidgetBase *nwid = pair.second;
233 if (nwid->IsHighlighted()) {
235 nwid->SetDirty(this);
236 }
237 }
238
239 this->flags.Reset(WindowFlag::Highlighted);
240}
241
247void Window::SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
248{
249 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
250 if (nwid == nullptr) return;
251
252 nwid->SetHighlighted(highlighted_colour);
253 nwid->SetDirty(this);
254
255 if (highlighted_colour != TextColour::Invalid) {
256 /* If we set a highlight, the window has a highlight */
258 } else {
259 /* If we disable a highlight, check all widgets if anyone still has a highlight */
260 bool valid = false;
261 for (const auto &pair : this->widget_lookup) {
262 nwid = pair.second;
263 if (!nwid->IsHighlighted()) continue;
264
265 valid = true;
266 }
267 /* If nobody has a highlight, disable the flag on the window */
268 if (!valid) this->flags.Reset(WindowFlag::Highlighted);
269 }
270}
271
277bool Window::IsWidgetHighlighted(WidgetID widget_index) const
278{
279 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
280 if (nwid == nullptr) return false;
281
282 return nwid->IsHighlighted();
283}
284
293void Window::OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
294{
295 if (widget < 0) return;
296
297 /* Many dropdown selections depend on the position of the main toolbar,
298 * so if it doesn't exist (e.g. the end screen has appeared), just skip the instant close behaviour. */
299 if (instant_close && FindWindowById(WindowClass::MainToolbar, 0) != nullptr) {
300 /* Send event for selected option if we're still
301 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
302 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
303 this->OnDropdownSelect(widget, index, click_result);
304 }
305 }
306
307 /* Raise the dropdown button */
308 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
309 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
311 } else {
312 this->RaiseWidget(widget);
313 }
314 this->SetWidgetDirty(widget);
315}
316
323{
324 return this->GetWidget<NWidgetScrollbar>(widnum);
325}
326
333{
334 return this->GetWidget<NWidgetScrollbar>(widnum);
335}
336
343{
344 auto query = this->querystrings.find(widnum);
345 return query != this->querystrings.end() ? query->second : nullptr;
346}
347
354{
355 auto query = this->querystrings.find(widnum);
356 return query != this->querystrings.end() ? query->second : nullptr;
357}
358
363{
364 for (auto &qs : this->querystrings) {
365 qs.second->text.UpdateSize();
366 }
367}
368
373/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
374{
375 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
376 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
377 }
378
379 return nullptr;
380}
381
386/* virtual */ Point Window::GetCaretPosition() const
387{
388 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
389 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
390 }
391
392 Point pt = {0, 0};
393 return pt;
394}
395
402/* virtual */ Rect Window::GetTextBoundingRect(size_t from, size_t to) const
403{
404 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
405 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
406 }
407
408 Rect r = {0, 0, 0, 0};
409 return r;
410}
411
417/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
418{
419 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
420 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
421 }
422
423 return -1;
424}
425
431{
432 if (_focused_window == w) return;
433
434 /* Don't focus a tooltip */
435 if (w != nullptr && w->window_class == WindowClass::ToolTips) return;
436
437 /* Invalidate focused widget */
438 if (_focused_window != nullptr) {
439 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
440 }
441
442 /* Remember which window was previously focused */
443 Window *old_focused = _focused_window;
444 _focused_window = w;
445
446 /* So we can inform it that it lost focus */
447 if (old_focused != nullptr) old_focused->OnFocusLost(false);
448 if (_focused_window != nullptr) _focused_window->OnFocus();
449}
450
457{
458 if (_focused_window == nullptr) return false;
459
460 /* The console does not have an edit box so a special case is needed. */
461 if (_focused_window->window_class == WindowClass::Console) return true;
462
463 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
464}
465
471{
472 return _focused_window && _focused_window->window_class == WindowClass::Console;
473}
474
479{
480 if (this->nested_focus != nullptr) {
482
483 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
484 this->nested_focus->SetDirty(this);
485 this->nested_focus = nullptr;
486 }
487}
488
495{
496 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
497 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
498
499 if (this->nested_focus != nullptr) {
500 /* Do nothing if widget_index is already focused. */
501 if (widget == this->nested_focus) return false;
502
503 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
504 this->nested_focus->SetDirty(this);
506 }
507
508 this->nested_focus = widget;
510 return true;
511}
512
513std::string Window::GetWidgetString([[maybe_unused]] WidgetID widget, StringID stringid) const
514{
515 if (stringid == STR_NULL) return {};
516 return GetString(stringid);
517}
518
523{
524 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxGainedFocus();
525}
526
531{
532 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
533}
534
539void Window::RaiseButtons(bool autoraise)
540{
541 for (auto &pair : this->widget_lookup) {
542 WidgetType type = pair.second->type;
543 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
544 if (wid != nullptr && ((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
545 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
546 wid->SetLowered(false);
547 wid->SetDirty(this);
548 }
549 }
550
551 /* Special widgets without widget index */
552 {
553 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
554 if (wid != nullptr) {
555 wid->SetLowered(false);
556 wid->SetDirty(this);
557 }
558 }
559}
560
565void Window::SetWidgetDirty(WidgetID widget_index) const
566{
567 /* Sometimes this function is called before the window is even fully initialized */
568 auto it = this->widget_lookup.find(widget_index);
569 if (it == std::end(this->widget_lookup)) return;
570
571 it->second->SetDirty(this);
572}
573
580{
581 if (hotkey < 0) return EventState::NotHandled;
582
583 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
584 if (nw == nullptr || nw->IsDisabled()) return EventState::NotHandled;
585
586 if (nw->type == WWT_EDITBOX) {
587 if (this->IsShaded()) return EventState::NotHandled;
588
589 /* Focus editbox */
590 this->SetFocusedWidget(hotkey);
591 SetFocusedWindow(this);
592 } else {
593 /* Click button */
594 this->OnClick(Point(), hotkey, 1);
595 }
596 return EventState::Handled;
597}
598
605{
606 /* Button click for this widget may already have been handled. */
607 if (this->IsWidgetLowered(widget) && this->timeout_timer == TIMEOUT_DURATION) return;
608
609 this->LowerWidget(widget);
610 this->SetTimeout();
611 this->SetWidgetDirty(widget);
612 SndClickBeep();
613}
614
615static void StartWindowDrag(Window *w);
616static void StartWindowSizing(Window *w, bool to_left);
617
625static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
626{
627 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
628 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
629
630 /* Allow dropdown close flag detection to work. */
632
633 bool focused_widget_changed = false;
634
635 /* If clicked on a window that previously did not have focus */
636 if (_focused_window != w) {
637 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
638 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
639 focused_widget_changed = true;
641 } else if (_focused_window != nullptr && _focused_window->window_class == WindowClass::DropdownMenu) {
642 /* The previously focused window was a dropdown menu, but the user clicked on another window that
643 * isn't focusable. Close the dropdown menu anyway. */
644 SetFocusedWindow(nullptr);
645 }
646 }
647
648 if (nw == nullptr) return; // exit if clicked outside of widgets
649
650 /* don't allow any interaction if the button has been disabled */
651 if (nw->IsDisabled()) return;
652
653 WidgetID widget_index = nw->GetIndex();
654
655 /* Clicked on a widget that is not disabled.
656 * So unless the clicked widget is the caption bar, change focus to this widget.
657 * Exception: In the OSK we always want the editbox to stay focused. */
658 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WindowClass::OnScreenKeyboard) {
659 /* focused_widget_changed is 'now' only true if the window this widget
660 * is in gained focus. In that case it must remain true, also if the
661 * local widget focus did not change. As such it's the logical-or of
662 * both changed states.
663 *
664 * If this is not preserved, then the OSK window would be opened when
665 * a user has the edit box focused and then click on another window and
666 * then back again on the edit box (to type some text).
667 */
668 focused_widget_changed |= w->SetFocusedWidget(widget_index);
669 }
670
671 /* Dropdown window of this widget was closed so don't process click this time. */
673
674 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
675
676 Point pt = { x, y };
677
678 switch (widget_type) {
679 case NWID_VSCROLLBAR:
680 case NWID_HSCROLLBAR:
681 ScrollbarClickHandler(w, nw, x, y);
682 break;
683
684 case WWT_EDITBOX: {
685 QueryString *query = w->GetQueryString(widget_index);
686 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
687 break;
688 }
689
690 case WWT_CLOSEBOX: // 'X'
691 w->Close();
692 return;
693
694 case WWT_CAPTION: // 'Title bar'
696 return;
697
698 case WWT_RESIZEBOX:
699 /* When the resize widget is on the left size of the window
700 * we assume that that button is used to resize to the left. */
701 StartWindowSizing(w, nw->pos_x < (w->width / 2));
702 nw->SetDirty(w);
703 return;
704
705 case WWT_DEFSIZEBOX: {
706 if (_ctrl_pressed) {
707 if (click_count > 1) {
708 w->window_desc.pref_width = 0;
710 } else {
713 }
714 } else {
715 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
716 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
717
718 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
719 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
720 /* dx and dy has to go by step.. calculate it.
721 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
722 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
723 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
724 ResizeWindow(w, dx, dy, false);
725 }
726
727 nw->SetLowered(true);
728 nw->SetDirty(w);
729 w->SetTimeout();
730 break;
731 }
732
733 case WWT_DEBUGBOX:
735 break;
736
737 case WWT_SHADEBOX:
738 nw->SetDirty(w);
739 w->SetShaded(!w->IsShaded());
740 return;
741
742 case WWT_STICKYBOX:
744 nw->SetDirty(w);
746 return;
747
748 default:
749 break;
750 }
751
752 /* Widget has no index, so the window is not interested in it. */
753 if (widget_index < 0) return;
754
755 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
756 if (w->IsWidgetHighlighted(widget_index)) {
757 w->SetWidgetHighlight(widget_index, TextColour::Invalid);
758 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
759 }
760
761 w->OnClick(pt, widget_index, click_count);
762}
763
770static void DispatchRightClickEvent(Window *w, int x, int y)
771{
772 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
773 if (wid == nullptr) return;
774
775 Point pt = { x, y };
776
777 /* No widget to handle, or the window is not interested in it. */
778 if (wid->GetIndex() >= 0) {
779 if (w->OnRightClick(pt, wid->GetIndex())) return;
780 }
781
782 /* Right-click close is enabled and there is a closebox. */
783 if (_settings_client.gui.right_click_wnd_close == RightClickClose::Yes && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
784 w->Close();
785 } else if (_settings_client.gui.right_click_wnd_close == RightClickClose::YesExceptSticky && !w->flags.Test(WindowFlag::Sticky) && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
786 /* Right-click close is enabled, but excluding sticky windows. */
787 w->Close();
788 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TooltipCloseCondition::RightClick) && wid->GetToolTip() != STR_NULL) {
790 }
791}
792
799static void DispatchHoverEvent(Window *w, int x, int y)
800{
801 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
802
803 /* No widget to handle */
804 if (wid == nullptr) return;
805
806 Point pt = { x, y };
807
808 /* Show the tooltip if there is any */
809 if (!w->OnTooltip(pt, wid->GetIndex(), TooltipCloseCondition::Hover) && wid->GetToolTip() != STR_NULL) {
811 return;
812 }
813
814 /* Widget has no index, so the window is not interested in it. */
815 if (wid->GetIndex() < 0) return;
816
817 w->OnHover(pt, wid->GetIndex());
818}
819
827static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
828{
829 if (nwid == nullptr) return;
830
831 /* Using wheel on caption/shade-box shades or unshades the window. */
832 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
833 w->SetShaded(wheel < 0);
834 return;
835 }
836
837 /* Wheeling a vertical scrollbar. */
838 if (nwid->type == NWID_VSCROLLBAR) {
839 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
840 if (sb->GetCount() > sb->GetCapacity()) {
841 if (sb->UpdatePosition(wheel)) {
842 w->OnScrollbarScroll(nwid->GetIndex());
843 w->SetDirty();
844 }
845 }
846 return;
847 }
848
849 /* Scroll the widget attached to the scrollbar. */
850 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
851 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
852 if (sb->UpdatePosition(wheel)) {
854 w->SetDirty();
855 }
856 }
857}
858
864static bool MayBeShown(const Window *w)
865{
866 /* If we're not modal, everything is okay. */
867 if (!HasModalProgress()) return true;
868
869 switch (w->window_class) {
870 case WindowClass::MainWindow:
871 case WindowClass::ModalProgress:
872 case WindowClass::ConfirmPopupQuery:
873 return true;
874
875 default:
876 return false;
877 }
878}
879
892static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
893{
895 ++it;
896 for (; !it.IsEnd(); ++it) {
897 const Window *v = *it;
898 if (MayBeShown(v) &&
899 right > v->left &&
900 bottom > v->top &&
901 left < v->left + v->width &&
902 top < v->top + v->height) {
903 /* v and rectangle intersect with each other */
904 int x;
905
906 if (left < (x = v->left)) {
907 DrawOverlappedWindow(w, left, top, x, bottom);
908 DrawOverlappedWindow(w, x, top, right, bottom);
909 return;
910 }
911
912 if (right > (x = v->left + v->width)) {
913 DrawOverlappedWindow(w, left, top, x, bottom);
914 DrawOverlappedWindow(w, x, top, right, bottom);
915 return;
916 }
917
918 if (top < (x = v->top)) {
919 DrawOverlappedWindow(w, left, top, right, x);
920 DrawOverlappedWindow(w, left, x, right, bottom);
921 return;
922 }
923
924 if (bottom > (x = v->top + v->height)) {
925 DrawOverlappedWindow(w, left, top, right, x);
926 DrawOverlappedWindow(w, left, x, right, bottom);
927 return;
928 }
929
930 return;
931 }
932 }
933
934 /* Setup blitter, and dispatch a repaint event to window *wz */
935 DrawPixelInfo *dp = _cur_dpi;
936 dp->width = right - left;
937 dp->height = bottom - top;
938 dp->left = left - w->left;
939 dp->top = top - w->top;
940 dp->pitch = _screen.pitch;
941 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
942 dp->zoom = ZoomLevel::Min;
943 w->OnPaint();
944}
945
954void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
955{
956 DrawPixelInfo bk;
957 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
958
959 for (Window *w : Window::IterateFromBack()) {
960 if (MayBeShown(w) &&
961 right > w->left &&
962 bottom > w->top &&
963 left < w->left + w->width &&
964 top < w->top + w->height) {
965 /* Window w intersects with the rectangle => needs repaint */
966 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));
967 }
968 }
969}
970
976{
977 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
978}
979
987void Window::ReInit(int rx, int ry, bool reposition)
988{
989 this->SetDirty(); // Mark whole current window as dirty.
990
991 /* Save current size. */
992 int window_width = this->width * _gui_scale / this->scale;
993 int window_height = this->height * _gui_scale / this->scale;
994 this->scale = _gui_scale;
995
996 this->OnInit();
997 /* Re-initialize window smallest size. */
998 this->nested_root->SetupSmallestSize(this);
999 this->nested_root->AssignSizePosition(SizingType::Smallest, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1000 this->width = this->nested_root->smallest_x;
1001 this->height = this->nested_root->smallest_y;
1002 this->resize.step_width = this->nested_root->resize_x;
1003 this->resize.step_height = this->nested_root->resize_y;
1004
1005 /* Resize as close to the original size + requested resize as possible. */
1006 window_width = std::max(window_width + rx, this->width);
1007 window_height = std::max(window_height + ry, this->height);
1008 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
1009 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
1010 /* dx and dy has to go by step.. calculate it.
1011 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1012 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1013 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1014
1015 if (reposition) {
1016 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1017 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1018 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1019 }
1020
1021 ResizeWindow(this, dx, dy, true, false);
1022 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1023}
1024
1030void Window::SetShaded(bool make_shaded)
1031{
1032 if (this->shade_select == nullptr) return;
1033
1034 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1035 if (this->shade_select->shown_plane != desired) {
1036 if (make_shaded) {
1037 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1038 this->unshaded_size.width = this->width;
1039 this->unshaded_size.height = this->height;
1040 this->shade_select->SetDisplayedPlane(desired);
1041 this->ReInit(0, -this->height);
1042 } else {
1043 this->shade_select->SetDisplayedPlane(desired);
1044 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1045 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1046 this->ReInit(dx, dy);
1047 }
1048 }
1049}
1050
1056Window *Window::FindChildWindow(WindowClass wc) const
1057{
1058 for (Window *v : Window::Iterate()) {
1059 if ((wc == WindowClass::Invalid || wc == v->window_class) && v->parent == this) return v;
1060 }
1061
1062 return nullptr;
1063}
1064
1072{
1073 for (Window *v : Window::Iterate()) {
1074 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1075 }
1076
1077 return nullptr;
1078}
1079
1084void Window::CloseChildWindows(WindowClass wc) const
1085{
1086 Window *child = this->FindChildWindow(wc);
1087 while (child != nullptr) {
1088 child->Close();
1089 child = this->FindChildWindow(wc);
1090 }
1091}
1092
1093
1099void Window::CloseChildWindowById(WindowClass wc, WindowNumber number) const
1100{
1101 Window *child = this->FindChildWindowById(wc, number);
1102 while (child != nullptr) {
1103 child->Close();
1104 child = this->FindChildWindowById(wc, number);
1105 }
1106}
1107
1112void Window::Close([[maybe_unused]] int data)
1113{
1114 /* Don't close twice. */
1115 if (*this->z_position == nullptr) return;
1116
1117 *this->z_position = nullptr;
1118
1119 if (_thd.window_class == this->window_class &&
1120 _thd.window_number == this->window_number) {
1122 }
1123
1124 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1125 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1126
1127 /* We can't scroll the window when it's closed. */
1128 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1129
1130 /* Make sure we don't try to access non-existing query strings. */
1131 this->querystrings.clear();
1132
1133 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1134 if (_focused_window == this) {
1135 this->OnFocusLost(true);
1136 _focused_window = nullptr;
1137 }
1138
1139 this->CloseChildWindows();
1140
1141 this->SetDirty();
1142
1143 Window::closed_windows.push_back(this);
1144}
1145
1150{
1151 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1152 assert(*this->z_position == nullptr);
1153}
1154
1161Window *FindWindowById(WindowClass cls, WindowNumber number)
1162{
1163 for (Window *w : Window::Iterate()) {
1164 if (w->window_class == cls && w->window_number == number) return w;
1165 }
1166
1167 return nullptr;
1168}
1169
1176Window *FindWindowByClass(WindowClass cls)
1177{
1178 for (Window *w : Window::Iterate()) {
1179 if (w->window_class == cls) return w;
1180 }
1181
1182 return nullptr;
1183}
1184
1191{
1192 Window *w = FindWindowById(WindowClass::MainWindow, 0);
1193 assert(w != nullptr);
1194 return w;
1195}
1196
1204void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1205{
1206 Window *w = FindWindowById(cls, number);
1207 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1208 w->Close(data);
1209 }
1210}
1211
1217void CloseWindowByClass(WindowClass cls, int data)
1218{
1219 /* Note: the container remains stable, even when deleting windows. */
1220 for (Window *w : Window::Iterate()) {
1221 if (w->window_class == cls) {
1222 w->Close(data);
1223 }
1224 }
1225}
1226
1233void CloseCompanyWindows(CompanyID id)
1234{
1235 /* Note: the container remains stable, even when deleting windows. */
1236 for (Window *w : Window::Iterate()) {
1237 if (w->owner == id) {
1238 w->Close();
1239 }
1240 }
1241
1242 /* Also delete the company specific windows that don't have a company-colour. */
1243 CloseWindowById(WindowClass::BuyCompany, id);
1244}
1245
1253void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1254{
1255 for (Window *w : Window::Iterate()) {
1256 if (w->owner != old_owner) continue;
1257
1258 switch (w->window_class) {
1259 case WindowClass::CompanyLivery:
1260 case WindowClass::Finances:
1261 case WindowClass::StationList:
1262 case WindowClass::TrainList:
1263 case WindowClass::RoadVehicleList:
1264 case WindowClass::ShipList:
1265 case WindowClass::AircraftList:
1266 case WindowClass::BuyCompany:
1267 case WindowClass::Company:
1268 case WindowClass::CompanyInfrastructure:
1269 case WindowClass::VehicleOrders: // Changing owner would also require changing WindowDesc, which is not possible; however keeping the old one crashes because of missing widgets etc.. See ShowOrdersWindow().
1270 continue;
1271
1272 default:
1273 w->owner = new_owner;
1274 break;
1275 }
1276 }
1277}
1278
1279static void BringWindowToFront(Window *w, bool dirty = true);
1280
1289{
1290 Window *w = FindWindowById(cls, number);
1291
1292 if (w != nullptr) {
1293 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1294
1295 w->SetWhiteBorder();
1297 w->SetDirty();
1298 }
1299
1300 return w;
1301}
1302
1303static inline bool IsVitalWindow(const Window *w)
1304{
1305 switch (w->window_class) {
1306 case WindowClass::MainToolbar:
1307 case WindowClass::Statusbar:
1308 case WindowClass::News:
1309 case WindowClass::NetworkChat:
1310 return true;
1311
1312 default:
1313 return false;
1314 }
1315}
1316
1325static uint GetWindowZPriority(WindowClass wc)
1326{
1327 assert(wc != WindowClass::Invalid);
1328
1329 uint z_priority = 0;
1330
1331 switch (wc) {
1332 case WindowClass::ToolTips:
1333 ++z_priority;
1334 [[fallthrough]];
1335
1336 case WindowClass::ErrorMessage:
1337 case WindowClass::ConfirmPopupQuery:
1338 ++z_priority;
1339 [[fallthrough]];
1340
1341 case WindowClass::Endscreen:
1342 ++z_priority;
1343 [[fallthrough]];
1344
1345 case WindowClass::Highscore:
1346 ++z_priority;
1347 [[fallthrough]];
1348
1349 case WindowClass::DropdownMenu:
1350 ++z_priority;
1351 [[fallthrough]];
1352
1353 case WindowClass::MainToolbar:
1354 case WindowClass::Statusbar:
1355 ++z_priority;
1356 [[fallthrough]];
1357
1358 case WindowClass::OnScreenKeyboard:
1359 ++z_priority;
1360 [[fallthrough]];
1361
1362 case WindowClass::QueryString:
1363 case WindowClass::NetworkChat:
1364 ++z_priority;
1365 [[fallthrough]];
1366
1367 case WindowClass::NetworkAskRelay:
1368 case WindowClass::ModalProgress:
1369 case WindowClass::NetworkStatus:
1370 case WindowClass::SavePreset:
1371 ++z_priority;
1372 [[fallthrough]];
1373
1374 case WindowClass::GenerateLandscape:
1375 case WindowClass::SaveLoad:
1376 case WindowClass::GameOptions:
1377 case WindowClass::CustomCurrenty:
1378 case WindowClass::Network:
1379 case WindowClass::NewGRFParameters:
1380 case WindowClass::ScriptList:
1381 case WindowClass::ScriptSettings:
1382 case WindowClass::Textfile:
1383 ++z_priority;
1384 [[fallthrough]];
1385
1386 case WindowClass::Console:
1387 ++z_priority;
1388 [[fallthrough]];
1389
1390 case WindowClass::News:
1391 ++z_priority;
1392 [[fallthrough]];
1393
1394 default:
1395 ++z_priority;
1396 [[fallthrough]];
1397
1398 case WindowClass::MainWindow:
1399 return z_priority;
1400 }
1401}
1402
1409static void BringWindowToFront(Window *w, bool dirty)
1410{
1411 auto priority = GetWindowZPriority(w->window_class);
1412 WindowList::iterator dest = _z_windows.begin();
1413 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1414
1415 if (dest != w->z_position) {
1416 _z_windows.splice(dest, _z_windows, w->z_position);
1417 }
1418
1419 if (dirty) w->SetDirty();
1420}
1421
1429{
1430 /* Set up window properties; some of them are needed to set up smallest size below */
1431 this->window_class = this->window_desc.cls;
1432 this->SetWhiteBorder();
1433 if (this->window_desc.default_pos == WindowPosition::Center) this->flags.Set(WindowFlag::Centred);
1434 this->owner = INVALID_OWNER;
1435 this->nested_focus = nullptr;
1436 this->window_number = window_number;
1437
1438 this->OnInit();
1439 /* Initialize smallest size. */
1440 this->nested_root->SetupSmallestSize(this);
1441 /* Initialize to smallest size. */
1442 this->nested_root->AssignSizePosition(SizingType::Smallest, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1443
1444 /* Further set up window properties,
1445 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1446 this->resize.step_width = this->nested_root->resize_x;
1447 this->resize.step_height = this->nested_root->resize_y;
1448
1449 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1450 * (so we don't interrupt typing) unless the new window has a text box. */
1451 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WindowClass::DropdownMenu;
1452 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1453 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1454
1455 /* Insert the window into the correct location in the z-ordering. */
1456 BringWindowToFront(this, false);
1457}
1458
1466void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1467{
1468 this->left = x;
1469 this->top = y;
1470 this->width = sm_width;
1471 this->height = sm_height;
1472}
1473
1485void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1486{
1487 if (allow_resize) {
1488 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1489 def_height = std::max(def_height, this->height);
1490 /* Try to make windows smaller when our window is too small.
1491 * w->(width|height) is normally the same as min_(width|height),
1492 * but this way the GUIs can be made a little more dynamic;
1493 * one can use the same spec for multiple windows and those
1494 * can then determine the real minimum size of the window. */
1495 if (this->width != def_width || this->height != def_height) {
1496 /* Think about the overlapping toolbars when determining the minimum window size */
1497 int free_height = _screen.height;
1498 const Window *wt = FindWindowById(WindowClass::Statusbar, 0);
1499 if (wt != nullptr) free_height -= wt->height;
1500 wt = FindWindowById(WindowClass::MainToolbar, 0);
1501 if (wt != nullptr) free_height -= wt->height;
1502
1503 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1504 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1505
1506 /* X and Y has to go by step.. calculate it.
1507 * The cast to int is necessary else x/y are implicitly cast to
1508 * unsigned int, which won't work. */
1509 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1510 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1511
1512 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1513 /* ResizeWindow() calls this->OnResize(). */
1514 } else {
1515 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1516 this->OnResize();
1517 }
1518 }
1519
1520 int nx = this->left;
1521 int ny = this->top;
1522
1523 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1524
1525 const Window *wt = FindWindowById(WindowClass::MainToolbar, 0);
1526 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1527 nx = std::max(nx, 0);
1528
1529 if (this->viewport != nullptr) {
1530 this->viewport->left += nx - this->left;
1531 this->viewport->top += ny - this->top;
1532 }
1533 this->left = nx;
1534 this->top = ny;
1535
1536 this->SetDirty();
1537}
1538
1551static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1552{
1553 int right = width + left;
1554 int bottom = height + top;
1555
1556 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1557
1558 /* Make sure it is not obscured by any window. */
1559 for (const Window *w : Window::Iterate()) {
1560 if (w->window_class == WindowClass::MainWindow) continue;
1561
1562 if (right > w->left &&
1563 w->left + w->width > left &&
1564 bottom > w->top &&
1565 w->top + w->height > top) {
1566 return false;
1567 }
1568 }
1569
1570 pos.x = left;
1571 pos.y = top;
1572 return true;
1573}
1574
1587static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1588{
1589 bool rtl = _current_text_dir == TD_RTL;
1590
1591 /* Left part of the rectangle may be at most 1/4 off-screen,
1592 * right part of the rectangle may be at most 1/2 off-screen
1593 */
1594 if (rtl) {
1595 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1596 } else {
1597 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1598 }
1599
1600 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1601 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1602
1603 /* Make sure it is not obscured by any window. */
1604 for (const Window *w : Window::Iterate()) {
1605 if (w->window_class == WindowClass::MainWindow) continue;
1606
1607 if (left + width > w->left &&
1608 w->left + w->width > left &&
1609 top + height > w->top &&
1610 w->top + w->height > top) {
1611 return false;
1612 }
1613 }
1614
1615 pos.x = left;
1616 pos.y = top;
1617 return true;
1618}
1619
1626static Point GetAutoPlacePosition(int width, int height)
1627{
1628 Point pt;
1629
1630 bool rtl = _current_text_dir == TD_RTL;
1631
1632 /* First attempt, try top-left of the screen */
1633 const Window *main_toolbar = FindWindowByClass(WindowClass::MainToolbar);
1634 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1635 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1636
1637 /* Second attempt, try around all existing windows.
1638 * The new window must be entirely on-screen, and not overlap with an existing window.
1639 * Eight starting points are tried, two at each corner.
1640 */
1641 for (const Window *w : Window::Iterate()) {
1642 if (w->window_class == WindowClass::MainWindow) continue;
1643
1644 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1645 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1646 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1647 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1648 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1649 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1650 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1651 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1652 }
1653
1654 /* Third attempt, try around all existing windows.
1655 * The new window may be partly off-screen, and must not overlap with an existing window.
1656 * Only four starting points are tried.
1657 */
1658 for (const Window *w : Window::Iterate()) {
1659 if (w->window_class == WindowClass::MainWindow) continue;
1660
1661 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1662 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1663 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1664 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1665 }
1666
1667 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1668 * of the closebox
1669 */
1670 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1671 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1672 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.captiontext.Vertical());
1673
1674restart:
1675 for (const Window *w : Window::Iterate()) {
1676 if (w->left == left && w->top == top) {
1677 left += offset_x;
1678 top += offset_y;
1679 goto restart;
1680 }
1681 }
1682
1683 pt.x = left;
1684 pt.y = top;
1685 return pt;
1686}
1687
1695{
1696 const Window *w = FindWindowById(WindowClass::MainToolbar, 0);
1697 assert(w != nullptr);
1698 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1699 return pt;
1700}
1701
1711{
1712 Point pt = GetToolbarAlignedWindowPosition(window_width);
1713 const Window *w = FindWindowByClass(WindowClass::ScenarioGenerateLandscape);
1714 if (w != nullptr && w->top == pt.y && !_settings_client.gui.link_terraform_toolbar) {
1715 pt.x = w->left + (_current_text_dir == TD_RTL ? w->width : - window_width);
1716 }
1717 return pt;
1718}
1719
1737static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1738{
1739 Point pt;
1740 const Window *w;
1741
1742 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1743 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1744
1745 if (desc.parent_cls != WindowClass::None && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1746 bool rtl = _current_text_dir == TD_RTL;
1747 if (desc.parent_cls == WindowClass::BuildToolbar || desc.parent_cls == WindowClass::ScenarioGenerateLandscape) {
1748 pt.x = w->left + (rtl ? w->width - default_width : 0);
1749 pt.y = w->top + w->height;
1750 return pt;
1751 } else {
1752 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1753 * - Y position: closebox of parent + closebox of child + statusbar
1754 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1755 */
1756 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.captiontext.Vertical());
1757 if (w->top + 3 * indent_y < _screen.height) {
1758 pt.y = w->top + indent_y;
1759 int indent_close = NWidgetLeaf::closebox_dimension.width;
1760 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1761 if (_current_text_dir == TD_RTL) {
1762 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1763 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1764 } else {
1765 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1766 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1767 }
1768 }
1769 }
1770 }
1771
1772 switch (desc.default_pos) {
1773 case WindowPosition::AlignToolbar: // Align to the toolbar
1774 return GetToolbarAlignedWindowPosition(default_width);
1775
1776 case WindowPosition::Automatic: // Find a good automatic position for the window
1777 return GetAutoPlacePosition(default_width, default_height);
1778
1779 case WindowPosition::Center: // Centre the window horizontally
1780 pt.x = (_screen.width - default_width) / 2;
1781 pt.y = (_screen.height - default_height) / 2;
1782 break;
1783
1785 pt.x = 0;
1786 pt.y = 0;
1787 break;
1788
1789 default:
1790 NOT_REACHED();
1791 }
1792
1793 return pt;
1794}
1795
1796/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1797{
1798 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1799}
1800
1808{
1809 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1810 this->nested_root->FillWidgetLookup(this->widget_lookup);
1811}
1812
1818{
1819 this->nested_root->AdjustPaddingForZoom();
1820 this->InitializeData(window_number);
1821 this->ApplyDefaults();
1822 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1823 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1824 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1825}
1826
1832{
1833 this->CreateNestedTree();
1834 this->FinishInitNested(window_number);
1835}
1836
1842{
1843 this->z_position = _z_windows.insert(_z_windows.end(), this);
1844}
1845
1854{
1855 for (Window *w : Window::IterateFromFront()) {
1856 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1857 return w;
1858 }
1859 }
1860
1861 return nullptr;
1862}
1863
1868{
1869 IConsoleClose();
1870
1871 _focused_window = nullptr;
1872 _mouseover_last_w = nullptr;
1873 _last_scroll_window = nullptr;
1874 _scrolling_viewport = false;
1875 _mouse_hovering = false;
1876
1878 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1879 NWidgetScrollbar::InvalidateDimensionCache();
1880
1882
1884}
1885
1890{
1892
1893 for (Window *w : Window::Iterate()) w->Close();
1894
1896
1897 assert(_z_windows.empty());
1898}
1899
1904{
1907 _thd.Reset();
1908}
1909
1910static void DecreaseWindowCounters()
1911{
1912 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1913
1914 for (Window *w : Window::Iterate()) {
1915 if (_scroller_click_timeout == 0) {
1916 /* Unclick scrollbar buttons if they are pressed. */
1917 for (auto &pair : w->widget_lookup) {
1918 NWidgetBase *nwid = pair.second;
1919 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1920 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1921 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1924 sb->SetDirty(w);
1925 }
1926 }
1927 }
1928 }
1929
1930 /* Handle editboxes */
1931 for (auto &pair : w->querystrings) {
1932 pair.second->HandleEditBox(w, pair.first);
1933 }
1934
1935 w->OnMouseLoop();
1936 }
1937
1938 for (Window *w : Window::Iterate()) {
1939 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1941
1942 w->OnTimeout();
1943 w->RaiseButtons(true);
1944 }
1945 }
1946}
1947
1948static void HandlePlacePresize()
1949{
1951
1952 Window *w = _thd.GetCallbackWnd();
1953 if (w == nullptr) return;
1954
1955 Point pt = GetTileBelowCursor();
1956 if (pt.x == -1) {
1957 _thd.selend.x = -1;
1958 return;
1959 }
1960
1961 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1962}
1963
1969{
1971
1972 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return EventState::Handled; // Dragging, but the mouse did not move.
1973
1974 Window *w = _thd.GetCallbackWnd();
1975 if (w != nullptr) {
1976 /* Send an event in client coordinates. */
1977 Point pt;
1978 pt.x = _cursor.pos.x - w->left;
1979 pt.y = _cursor.pos.y - w->top;
1980 if (_left_button_down) {
1981 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1982 } else {
1983 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1984 }
1985 }
1986
1987 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1988 return EventState::Handled;
1989}
1990
1992static void HandleMouseOver()
1993{
1994 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1995
1996 /* We changed window, put an OnMouseOver event to the last window */
1997 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1998 /* Reset mouse-over coordinates of previous window */
1999 Point pt = { -1, -1 };
2000 _mouseover_last_w->OnMouseOver(pt, 0);
2001 }
2002
2003 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
2005
2006 if (w != nullptr) {
2007 /* send an event in client coordinates. */
2008 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2009 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
2010 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
2011 }
2012}
2013
2015enum class PreventHideDirection : uint8_t {
2018};
2019
2030static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
2031{
2032 if (v == nullptr) return;
2033
2034 const int min_visible = rect.Height();
2035
2036 int v_bottom = v->top + v->height - 1;
2037 int v_right = v->left + v->width - 1;
2038 int safe_y = (dir == PreventHideDirection::Up) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2039
2040 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2041 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2042
2043 /* Vertically, the rectangle is hidden behind v. */
2044 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2045 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2046 return;
2047 }
2048 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2049 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2050 return;
2051 }
2052
2053 /* Horizontally also hidden, force movement to a safe area. */
2054 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2055 *nx = v->left - min_visible - rect.left;
2056 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2057 *nx = v_right + min_visible - rect.right;
2058 } else {
2059 *ny = safe_y;
2060 }
2061}
2062
2070static void EnsureVisibleCaption(Window *w, int nx, int ny)
2071{
2072 /* Search for the title bar rectangle. */
2073 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2074 if (caption != nullptr) {
2075 const Rect caption_rect = caption->GetCurrentRect();
2076
2077 const int min_visible = caption_rect.Height();
2078
2079 /* Make sure the window doesn't leave the screen */
2080 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2081 ny = Clamp(ny, 0, _screen.height - min_visible);
2082
2083 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2084 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WindowClass::MainToolbar, 0), w->left, PreventHideDirection::Down);
2085 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WindowClass::Statusbar, 0), w->left, PreventHideDirection::Up);
2086 }
2087
2088 if (w->viewport != nullptr) {
2089 w->viewport->left += nx - w->left;
2090 w->viewport->top += ny - w->top;
2091 }
2092
2093 w->left = nx;
2094 w->top = ny;
2095}
2096
2108void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2109{
2110 if (delta_x != 0 || delta_y != 0) {
2111 if (clamp_to_screen) {
2112 /* Determine the new right/bottom position. If that is outside of the bounds of
2113 * the resolution clamp it in such a manner that it stays within the bounds. */
2114 int new_right = w->left + w->width + delta_x;
2115 int new_bottom = w->top + w->height + delta_y;
2116 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2117 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2118 }
2119
2120 w->SetDirty();
2121
2122 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);
2123 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);
2124 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2125 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2126
2127 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);
2128 w->width = w->nested_root->current_x;
2129 w->height = w->nested_root->current_y;
2130 }
2131
2132 EnsureVisibleCaption(w, w->left, w->top);
2133
2134 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2135 if (schedule_resize) {
2136 w->ScheduleResize();
2137 } else {
2138 w->OnResize();
2139 }
2140 w->SetDirty();
2141}
2142
2149{
2150 Window *w = FindWindowById(WindowClass::MainToolbar, 0);
2151 return (w == nullptr) ? 0 : w->top + w->height;
2152}
2153
2160{
2161 Window *w = FindWindowById(WindowClass::Statusbar, 0);
2162 return (w == nullptr) ? _screen.height : w->top;
2163}
2164
2165static bool _dragging_window;
2166
2172{
2173 /* Get out immediately if no window is being dragged at all. */
2175
2176 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2177 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return EventState::Handled;
2178
2179 /* Otherwise find the window... */
2180 for (Window *w : Window::Iterate()) {
2182 /* Stop the dragging if the left mouse button was released */
2183 if (!_left_button_down) {
2185 break;
2186 }
2187
2188 w->SetDirty();
2189
2190 int x = _cursor.pos.x + _drag_delta.x;
2191 int y = _cursor.pos.y + _drag_delta.y;
2192 int nx = x;
2193 int ny = y;
2194
2195 if (_settings_client.gui.window_snap_radius != 0) {
2196 int hsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2197 int vsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2198 int delta;
2199
2200 for (const Window *v : Window::Iterate()) {
2201 if (v == w) continue; // Don't snap at yourself
2202
2203 if (y + w->height > v->top && y < v->top + v->height) {
2204 /* Your left border <-> other right border */
2205 delta = abs(v->left + v->width - x);
2206 if (delta <= hsnap) {
2207 nx = v->left + v->width;
2208 hsnap = delta;
2209 }
2210
2211 /* Your right border <-> other left border */
2212 delta = abs(v->left - x - w->width);
2213 if (delta <= hsnap) {
2214 nx = v->left - w->width;
2215 hsnap = delta;
2216 }
2217 }
2218
2219 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2220 /* Your left border <-> other left border */
2221 delta = abs(v->left - x);
2222 if (delta <= hsnap) {
2223 nx = v->left;
2224 hsnap = delta;
2225 }
2226
2227 /* Your right border <-> other right border */
2228 delta = abs(v->left + v->width - x - w->width);
2229 if (delta <= hsnap) {
2230 nx = v->left + v->width - w->width;
2231 hsnap = delta;
2232 }
2233 }
2234
2235 if (x + w->width > v->left && x < v->left + v->width) {
2236 /* Your top border <-> other bottom border */
2237 delta = abs(v->top + v->height - y);
2238 if (delta <= vsnap) {
2239 ny = v->top + v->height;
2240 vsnap = delta;
2241 }
2242
2243 /* Your bottom border <-> other top border */
2244 delta = abs(v->top - y - w->height);
2245 if (delta <= vsnap) {
2246 ny = v->top - w->height;
2247 vsnap = delta;
2248 }
2249 }
2250
2251 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2252 /* Your top border <-> other top border */
2253 delta = abs(v->top - y);
2254 if (delta <= vsnap) {
2255 ny = v->top;
2256 vsnap = delta;
2257 }
2258
2259 /* Your bottom border <-> other bottom border */
2260 delta = abs(v->top + v->height - y - w->height);
2261 if (delta <= vsnap) {
2262 ny = v->top + v->height - w->height;
2263 vsnap = delta;
2264 }
2265 }
2266 }
2267 }
2268
2269 EnsureVisibleCaption(w, nx, ny);
2270
2271 w->SetDirty();
2272 return EventState::Handled;
2274 /* Stop the sizing if the left mouse button was released */
2275 if (!_left_button_down) {
2278 w->SetDirty();
2279 break;
2280 }
2281
2282 /* Compute difference in pixels between cursor position and reference point in the window.
2283 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2284 */
2285 int x, y = _cursor.pos.y - _drag_delta.y;
2287 x = _drag_delta.x - _cursor.pos.x;
2288 } else {
2289 x = _cursor.pos.x - _drag_delta.x;
2290 }
2291
2292 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2293 if (w->resize.step_width == 0) x = 0;
2294 if (w->resize.step_height == 0) y = 0;
2295
2296 /* Check the resize button won't go past the bottom of the screen */
2297 if (w->top + w->height + y > _screen.height) {
2298 y = _screen.height - w->height - w->top;
2299 }
2300
2301 /* X and Y has to go by step.. calculate it.
2302 * The cast to int is necessary else x/y are implicitly cast to
2303 * unsigned int, which won't work. */
2304 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2305 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2306
2307 /* Check that we don't go below the minimum set size */
2308 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2309 x = w->nested_root->smallest_x - w->width;
2310 }
2311 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2312 y = w->nested_root->smallest_y - w->height;
2313 }
2314
2315 /* Window already on size */
2316 if (x == 0 && y == 0) return EventState::Handled;
2317
2318 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2319 _drag_delta.y += y;
2320 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2321 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2322 w->SetDirty();
2323 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2324 /* ResizeWindow() below ensures marking new position as dirty. */
2325 } else {
2326 _drag_delta.x += x;
2327 }
2328
2329 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2330 ResizeWindow(w, x, y);
2331 return EventState::Handled;
2332 }
2333 }
2334
2335 _dragging_window = false;
2336 return EventState::Handled;
2337}
2338
2344{
2347 _dragging_window = true;
2348
2349 _drag_delta.x = w->left - _cursor.pos.x;
2350 _drag_delta.y = w->top - _cursor.pos.y;
2351
2353}
2354
2360static void StartWindowSizing(Window *w, bool to_left)
2361{
2364 _dragging_window = true;
2365
2366 _drag_delta.x = _cursor.pos.x;
2367 _drag_delta.y = _cursor.pos.y;
2368
2370}
2371
2377{
2378 int i;
2380 bool rtl = false;
2381
2382 if (sb->type == NWID_HSCROLLBAR) {
2383 i = _cursor.pos.x - _cursorpos_drag_start.x;
2384 rtl = _current_text_dir == TD_RTL;
2385 } else {
2386 i = _cursor.pos.y - _cursorpos_drag_start.y;
2387 }
2388
2389 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2390 if (_scroller_click_timeout == 1) {
2391 _scroller_click_timeout = 3;
2392 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2394 w->SetDirty();
2395 }
2396 }
2397 return;
2398 }
2399
2400 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2401 int range = sb->GetCount() - sb->GetCapacity();
2402 if (range <= 0) return;
2403
2404 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2405 if (rtl) pos = range - pos;
2406 if (sb->SetPosition(pos)) {
2408 w->SetDirty();
2409 }
2410}
2411
2417{
2418 for (Window *w : Window::Iterate()) {
2419 if (w->mouse_capture_widget >= 0) {
2420 /* Abort if no button is clicked any more. */
2421 if (!_left_button_down) {
2424 return EventState::Handled;
2425 }
2426
2427 /* Handle scrollbar internally, or dispatch click event */
2429 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2431 } else {
2432 /* If cursor hasn't moved, there is nothing to do. */
2433 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return EventState::Handled;
2434
2435 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2436 w->OnClick(pt, w->mouse_capture_widget, 0);
2437 }
2438 return EventState::Handled;
2439 }
2440 }
2441
2443}
2444
2450{
2451 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2452
2454
2455 /* When we don't have a last scroll window we are starting to scroll.
2456 * When the last scroll window and this are not the same we went
2457 * outside of the window and should not left-mouse scroll anymore. */
2458 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2459
2460 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))) {
2461 _cursor.fix_at = false;
2462 _scrolling_viewport = false;
2463 _last_scroll_window = nullptr;
2465 }
2466
2467 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2468 /* If the main window is following a vehicle, then first let go of it! */
2469 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle)->GetMovingFront();
2470 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2472 }
2473
2474 Point delta;
2475 if (scrollwheel_scrolling) {
2476 /* We are using scrollwheels for scrolling */
2477 /* Use the integer part for movement */
2478 delta.x = static_cast<int>(_cursor.h_wheel);
2479 delta.y = static_cast<int>(_cursor.v_wheel);
2480 /* Keep the fractional part so that subtle movement is accumulated */
2481 float temp;
2482 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2483 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2484 } else {
2486 delta.x = -_cursor.delta.x;
2487 delta.y = -_cursor.delta.y;
2488 } else {
2489 delta.x = _cursor.delta.x;
2490 delta.y = _cursor.delta.y;
2491 }
2492 }
2493
2494 /* Create a scroll-event and send it to the window */
2495 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2496
2497 _cursor.delta.x = 0;
2498 _cursor.delta.y = 0;
2499 _cursor.wheel_moved = false;
2500 return EventState::Handled;
2501}
2502
2514{
2515 bool bring_to_front = false;
2516
2517 if (w->window_class == WindowClass::MainWindow ||
2518 IsVitalWindow(w) ||
2519 w->window_class == WindowClass::ToolTips ||
2520 w->window_class == WindowClass::DropdownMenu) {
2521 return true;
2522 }
2523
2524 /* Use unshaded window size rather than current size for shaded windows. */
2525 int w_width = w->width;
2526 int w_height = w->height;
2527 if (w->IsShaded()) {
2528 w_width = w->unshaded_size.width;
2529 w_height = w->unshaded_size.height;
2530 }
2531
2533 ++it;
2534 for (; !it.IsEnd(); ++it) {
2535 Window *u = *it;
2536 /* A modal child will prevent the activation of the parent window */
2538 u->SetWhiteBorder();
2539 u->SetDirty();
2540 return false;
2541 }
2542
2543 if (u->window_class == WindowClass::MainWindow ||
2544 IsVitalWindow(u) ||
2545 u->window_class == WindowClass::ToolTips ||
2546 u->window_class == WindowClass::DropdownMenu) {
2547 continue;
2548 }
2549
2550 /* Window sizes don't interfere, leave z-order alone */
2551 if (w->left + w_width <= u->left ||
2552 u->left + u->width <= w->left ||
2553 w->top + w_height <= u->top ||
2554 u->top + u->height <= w->top) {
2555 continue;
2556 }
2557
2558 bring_to_front = true;
2559 }
2560
2561 if (bring_to_front) BringWindowToFront(w);
2562 return true;
2563}
2564
2573EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2574{
2575 QueryString *query = this->GetQueryString(wid);
2576 if (query == nullptr) return EventState::NotHandled;
2577
2578 int action = QueryString::ACTION_NOTHING;
2579
2580 switch (query->text.HandleKeyPress(key, keycode)) {
2581 case HKPR_EDITING:
2582 this->SetWidgetDirty(wid);
2583 this->OnEditboxChanged(wid);
2584 break;
2585
2586 case HKPR_CURSOR:
2587 this->SetWidgetDirty(wid);
2588 /* For the OSK also invalidate the parent window */
2589 if (this->window_class == WindowClass::OnScreenKeyboard) this->InvalidateData();
2590 break;
2591
2592 case HKPR_CONFIRM:
2593 if (this->window_class == WindowClass::OnScreenKeyboard) {
2594 this->OnClick(Point(), WID_OSK_OK, 1);
2595 } else if (query->ok_button >= 0) {
2596 this->OnClick(Point(), query->ok_button, 1);
2597 } else {
2598 action = query->ok_button;
2599 }
2600 break;
2601
2602 case HKPR_CANCEL:
2603 if (this->window_class == WindowClass::OnScreenKeyboard) {
2604 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2605 } else if (query->cancel_button >= 0) {
2606 this->OnClick(Point(), query->cancel_button, 1);
2607 } else {
2608 action = query->cancel_button;
2609 }
2610 break;
2611
2612 case HKPR_NOT_HANDLED:
2614
2615 default: break;
2616 }
2617
2618 switch (action) {
2620 this->UnfocusFocusedWidget();
2621 break;
2622
2624 if (query->text.GetText().empty()) {
2625 /* If already empty, unfocus instead */
2626 this->UnfocusFocusedWidget();
2627 } else {
2628 query->text.DeleteAll();
2629 this->SetWidgetDirty(wid);
2630 this->OnEditboxChanged(wid);
2631 }
2632 break;
2633
2634 default:
2635 break;
2636 }
2637
2638 return EventState::Handled;
2639}
2640
2645void HandleToolbarHotkey(int hotkey)
2646{
2647 assert(HasModalProgress() || IsLocalCompany());
2648
2649 Window *w = FindWindowById(WindowClass::MainToolbar, 0);
2650 if (w != nullptr) {
2651 if (w->window_desc.hotkeys != nullptr) {
2652 if (hotkey >= 0 && w->OnHotkey(hotkey) == EventState::Handled) return;
2653 }
2654 }
2655}
2656
2662void HandleKeypress(uint keycode, char32_t key)
2663{
2664 /* World generation is multithreaded and messes with companies.
2665 * But there is no company related window open anyway, so _current_company is not used. */
2666 assert(HasModalProgress() || IsLocalCompany());
2667
2668 /*
2669 * The Unicode standard defines an area called the private use area. Code points in this
2670 * area are reserved for private use and thus not portable between systems. For instance,
2671 * Apple defines code points for the arrow keys in this area, but these are only printable
2672 * on a system running OS X. We don't want these keys to show up in text fields and such,
2673 * and thus we have to clear the unicode character when we encounter such a key.
2674 */
2675 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2676
2677 /*
2678 * If both key and keycode is zero, we don't bother to process the event.
2679 */
2680 if (key == 0 && keycode == 0) return;
2681
2682 /* Check if the focused window has a focused editbox */
2683 if (EditBoxInGlobalFocus()) {
2684 /* All input will in this case go to the focused editbox */
2685 if (_focused_window->window_class == WindowClass::Console) {
2686 if (_focused_window->OnKeyPress(key, keycode) == EventState::Handled) return;
2687 } else {
2688 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == EventState::Handled) return;
2689 }
2690 }
2691
2692 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2693 for (Window *w : Window::IterateFromFront()) {
2694 if (w->window_class == WindowClass::MainToolbar) continue;
2695 if (w->window_desc.hotkeys != nullptr) {
2696 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2697 if (hotkey >= 0 && w->OnHotkey(hotkey) == EventState::Handled) return;
2698 }
2699 if (w->OnKeyPress(key, keycode) == EventState::Handled) return;
2700 }
2701
2702 Window *w = FindWindowById(WindowClass::MainToolbar, 0);
2703 /* When there is no toolbar w is null, check for that */
2704 if (w != nullptr) {
2705 if (w->window_desc.hotkeys != nullptr) {
2706 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2707 if (hotkey >= 0 && w->OnHotkey(hotkey) == EventState::Handled) return;
2708 }
2709 if (w->OnKeyPress(key, keycode) == EventState::Handled) return;
2710 }
2711
2712 HandleGlobalHotkeys(key, keycode);
2713}
2714
2719{
2720 /* Call the event, start with the uppermost window. */
2721 for (Window *w : Window::IterateFromFront()) {
2722 if (w->OnCTRLStateChange() == EventState::Handled) return;
2723 }
2724}
2725
2735/* 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)
2736{
2737 QueryString *query = this->GetQueryString(wid);
2738 if (query == nullptr) return;
2739
2740 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2741 this->SetWidgetDirty(wid);
2742 this->OnEditboxChanged(wid);
2743 }
2744}
2745
2754void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2755{
2756 if (!EditBoxInGlobalFocus()) return;
2757
2758 _focused_window->InsertTextString(_focused_window->window_class == WindowClass::Console ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2759}
2760
2765static void HandleAutoscroll()
2766{
2767 if (_game_mode == GameMode::Menu || HasModalProgress()) return;
2768 if (_settings_client.gui.auto_scrolling == ViewportAutoscrolling::Disabled) return;
2769 if (_settings_client.gui.auto_scrolling == ViewportAutoscrolling::MainViewportFullscreen && !_fullscreen) return;
2770
2771 int x = _cursor.pos.x;
2772 int y = _cursor.pos.y;
2773 Window *w = FindWindowFromPt(x, y);
2774 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2775 if (_settings_client.gui.auto_scrolling != ViewportAutoscrolling::EveryViewport && w->window_class != WindowClass::MainWindow) return;
2776
2777 Viewport *vp = IsPtInWindowViewport(w, x, y);
2778 if (vp == nullptr) return;
2779
2780 x -= vp->left;
2781 y -= vp->top;
2782
2783 /* here allows scrolling in both x and y axis */
2784 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2785 static const int SCROLLSPEED = 3;
2786 if (x - 15 < 0) {
2787 w->viewport->CancelFollow(*w);
2788 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2789 } else if (15 - (vp->width - x) > 0) {
2790 w->viewport->CancelFollow(*w);
2791 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2792 }
2793 if (y - 15 < 0) {
2794 w->viewport->CancelFollow(*w);
2795 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2796 } else if (15 - (vp->height - y) > 0) {
2797 w->viewport->CancelFollow(*w);
2798 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2799 }
2800}
2801
2810
2811static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2812static constexpr int MAX_OFFSET_HOVER = 5;
2813
2815
2816const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK{500};
2817
2818static void ScrollMainViewport(int x, int y)
2819{
2820 if (_game_mode != GameMode::Menu && _game_mode != GameMode::Bootstrap) {
2821 Window *w = GetMainWindow();
2822 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2823 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2824 }
2825}
2826
2836static const int8_t scrollamt[16][2] = {
2837 { 0, 0},
2838 {-2, 0},
2839 { 0, -2},
2840 {-2, -1},
2841 { 2, 0},
2842 { 0, 0},
2843 { 2, -1},
2844 { 0, -2},
2845 { 0, 2},
2846 {-2, 1},
2847 { 0, 0},
2848 {-2, 0},
2849 { 2, 1},
2850 { 0, 2},
2851 { 2, 0},
2852 { 0, 0},
2853};
2854
2855static void HandleKeyScrolling()
2856{
2857 /*
2858 * Check that any of the dirkeys is pressed and that the focused window
2859 * doesn't have an edit-box as focused widget.
2860 */
2861 if (_dirkeys.Any() && !EditBoxInGlobalFocus()) {
2862 int factor = _shift_pressed ? 50 : 10;
2863
2864 if (_game_mode != GameMode::Menu && _game_mode != GameMode::Bootstrap) {
2865 /* Key scrolling stops following a vehicle. */
2866 Window *main_window = GetMainWindow();
2867 main_window->viewport->CancelFollow(*main_window);
2868 }
2869
2870 ScrollMainViewport(scrollamt[_dirkeys.base()][0] * factor, scrollamt[_dirkeys.base()][1] * factor);
2871 }
2872}
2873
2874static void MouseLoop(MouseClick click, int mousewheel)
2875{
2876 /* World generation is multithreaded and messes with companies.
2877 * But there is no company related window open anyway, so _current_company is not used. */
2878 assert(HasModalProgress() || IsLocalCompany());
2879
2880 HandlePlacePresize();
2882
2886 if (HandleActiveWidget() == EventState::Handled) return;
2888
2890
2891 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2892 if (click == MouseClick::None && mousewheel == 0 && !scrollwheel_scrolling) return;
2893
2894 int x = _cursor.pos.x;
2895 int y = _cursor.pos.y;
2896 Window *w = FindWindowFromPt(x, y);
2897 if (w == nullptr) return;
2898
2899 if (click != MouseClick::Hover && !MaybeBringWindowToFront(w)) return;
2900 Viewport *vp = IsPtInWindowViewport(w, x, y);
2901
2902 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2903 if (vp != nullptr && (_game_mode == GameMode::Menu || HasModalProgress())) return;
2904
2905 if (mousewheel != 0) {
2906 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2907 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WindowClass::SmallMap)) {
2908 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2909 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2910 }
2911 }
2912
2913 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2914 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2915 }
2916
2917 if (vp != nullptr) {
2918 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2919 _scrolling_viewport = true;
2920 _cursor.fix_at = true;
2921 return;
2922 }
2923
2924 switch (click) {
2926 case MouseClick::Left:
2927 if (HandleViewportClicked(*vp, x, y)) return;
2929 _settings_client.gui.scroll_mode == ViewportScrollMode::MapLMB) {
2930 _scrolling_viewport = true;
2931 _cursor.fix_at = false;
2932 return;
2933 }
2934 break;
2935
2936 case MouseClick::Right:
2938 _settings_client.gui.scroll_mode != ViewportScrollMode::MapLMB) {
2939 _scrolling_viewport = true;
2940 _cursor.fix_at = (_settings_client.gui.scroll_mode == ViewportScrollMode::ViewportRMBFixed ||
2942 DispatchRightClickEvent(w, x - w->left, y - w->top);
2943 return;
2944 }
2945 break;
2946
2947 default:
2948 break;
2949 }
2950 }
2951
2952 switch (click) {
2953 case MouseClick::Left:
2955 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MouseClick::DoubleLeft ? 2 : 1);
2956 return;
2957
2958 default:
2959 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WindowClass::SmallMap) break;
2960 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2961 * Simulate a right button click so we can get started. */
2962 [[fallthrough]];
2963
2964 case MouseClick::Right:
2965 DispatchRightClickEvent(w, x - w->left, y - w->top);
2966 return;
2967
2968 case MouseClick::Hover:
2969 DispatchHoverEvent(w, x - w->left, y - w->top);
2970 break;
2971 }
2972
2973 /* We're not doing anything with 2D scrolling, so reset the value. */
2974 _cursor.h_wheel = 0.0f;
2975 _cursor.v_wheel = 0.0f;
2976 _cursor.wheel_moved = false;
2977}
2978
2983{
2984 /* World generation is multithreaded and messes with companies.
2985 * But there is no company related window open anyway, so _current_company is not used. */
2986 assert(HasModalProgress() || IsLocalCompany());
2987
2988 static std::chrono::steady_clock::time_point double_click_time = {};
2989 static Point double_click_pos = {0, 0};
2990
2991 /* Mouse event? */
2994 click = MouseClick::Left;
2995 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2996 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2997 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2998 click = MouseClick::DoubleLeft;
2999 }
3000 double_click_time = std::chrono::steady_clock::now();
3001 double_click_pos = _cursor.pos;
3002 _left_button_clicked = true;
3003 } else if (_right_button_clicked) {
3004 _right_button_clicked = false;
3005 click = MouseClick::Right;
3006 }
3007
3008 int mousewheel = 0;
3009 if (_cursor.wheel) {
3010 mousewheel = _cursor.wheel;
3011 _cursor.wheel = 0;
3012 }
3013
3014 static std::chrono::steady_clock::time_point hover_time = {};
3015 static Point hover_pos = {0, 0};
3016
3017 if (_settings_client.gui.hover_delay_ms > 0) {
3018 if (!_cursor.in_window || click != MouseClick::None || mousewheel != 0 || _left_button_down || _right_button_down ||
3019 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
3020 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
3021 hover_pos = _cursor.pos;
3022 hover_time = std::chrono::steady_clock::now();
3023 _mouse_hovering = false;
3024 } else if (!_mouse_hovering) {
3025 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
3026 click = MouseClick::Hover;
3027 _mouse_hovering = true;
3028 hover_time = std::chrono::steady_clock::now();
3029 }
3030 }
3031 }
3032
3033 if (click == MouseClick::Left && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
3034 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
3036 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
3037 _newgrf_debug_sprite_picker.sprites.clear();
3038 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3040 } else {
3041 MouseLoop(click, mousewheel);
3042 }
3043
3044 /* We have moved the mouse the required distance,
3045 * no need to move it at any later time. */
3046 _cursor.delta.x = 0;
3047 _cursor.delta.y = 0;
3048}
3049
3053static void CheckSoftLimit()
3054{
3055 if (_settings_client.gui.window_soft_limit == 0) return;
3056
3057 for (;;) {
3058 uint deletable_count = 0;
3059 Window *last_deletable = nullptr;
3060 for (Window *w : Window::IterateFromFront()) {
3061 if (w->window_class == WindowClass::MainWindow || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3062
3063 last_deletable = w;
3064 deletable_count++;
3065 }
3066
3067 /* We've not reached the soft limit yet. */
3068 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3069
3070 assert(last_deletable != nullptr);
3071 last_deletable->Close();
3072 }
3073}
3074
3079{
3080 /* World generation is multithreaded and messes with companies.
3081 * But there is no company related window open anyway, so _current_company is not used. */
3082 assert(HasModalProgress() || IsLocalCompany());
3083
3085
3086 /* Process scheduled window deletion. */
3088
3089 /* HandleMouseEvents was already called for this tick */
3091}
3092
3093static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3094
3095bool CanContinueRealtimeTick()
3096{
3097 auto now = std::chrono::steady_clock::now();
3098 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3099}
3100
3106{
3107 _realtime_tick_start = std::chrono::steady_clock::now();
3108 for (Window *w : Window::Iterate()) {
3109 w->OnRealtimeTick(delta_ms);
3110 }
3111}
3112
3114static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3115 extern int _caret_timer;
3116 _caret_timer += 3;
3117 CursorTick();
3118
3119 HandleKeyScrolling();
3121 DecreaseWindowCounters();
3122});
3123
3127});
3128
3130static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3131 if (_network_dedicated) return;
3132
3133 for (Window *w : Window::Iterate()) {
3136 w->SetDirty();
3137 }
3138 }
3139});
3140
3145{
3146 static auto last_time = std::chrono::steady_clock::now();
3147 auto now = std::chrono::steady_clock::now();
3148 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3149
3150 if (delta_ms.count() == 0) return;
3151
3152 last_time = now;
3153
3156
3158
3160 CallWindowRealtimeTickEvent(delta_ms.count());
3161
3162 /* Process invalidations before anything else. */
3163 for (Window *w : Window::Iterate()) {
3167 }
3168
3169 /* Skip the actual drawing on dedicated servers without screen.
3170 * But still empty the invalidation queues above. */
3171 if (_network_dedicated) return;
3172
3174
3175 for (Window *w : Window::Iterate()) {
3176 /* Update viewport only if window is not shaded. */
3177 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3178 }
3180 /* Redraw mouse cursor in case it was hidden */
3181 DrawMouseCursor();
3182
3183 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3184 /* We are done with the last draw-frame, so we know what sprites we
3185 * clicked on. Reset the picker mode and invalidate the window. */
3186 _newgrf_debug_sprite_picker.mode = SPM_NONE;
3187 InvalidateWindowData(WindowClass::SpriteAligner, 0, 1);
3188 }
3189}
3190
3196void SetWindowDirty(WindowClass cls, WindowNumber number)
3197{
3198 for (const Window *w : Window::Iterate()) {
3199 if (w->window_class == cls && w->window_number == number) {
3200 w->SetDirty();
3201 return;
3202 }
3203 }
3204}
3205
3212void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
3213{
3214 for (const Window *w : Window::Iterate()) {
3215 if (w->window_class == cls && w->window_number == number) {
3216 w->SetWidgetDirty(widget_index);
3217 return;
3218 }
3219 }
3220}
3221
3226void SetWindowClassesDirty(WindowClass cls)
3227{
3228 for (const Window *w : Window::Iterate()) {
3229 if (w->window_class == cls) w->SetDirty();
3230 }
3231}
3232
3237{
3238 this->scheduled_resize = true;
3239}
3240
3245{
3246 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3247 while (this->scheduled_resize) {
3248 this->scheduled_resize = false;
3249 this->OnResize();
3250 }
3251}
3252
3258void Window::InvalidateData(int data, bool gui_scope)
3259{
3260 this->SetDirty();
3261 if (!gui_scope) {
3262 /* Schedule GUI-scope invalidation for next redraw. */
3263 this->scheduled_invalidation_data.push_back(data);
3264 }
3265 this->OnInvalidateData(data, gui_scope);
3266}
3267
3272{
3273 for (int data : this->scheduled_invalidation_data) {
3274 if (this->window_class == WindowClass::Invalid) break;
3275 this->OnInvalidateData(data, true);
3276 }
3277 this->scheduled_invalidation_data.clear();
3278}
3279
3284{
3285 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3286
3287 for (const auto &pair : this->widget_lookup) {
3288 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3289 }
3290}
3291
3318void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3319{
3320 for (Window *w : Window::Iterate()) {
3321 if (w->window_class == cls && w->window_number == number) {
3322 w->InvalidateData(data, gui_scope);
3323 return;
3324 }
3325 }
3326}
3327
3336void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3337{
3338 for (Window *w : Window::Iterate()) {
3339 if (w->window_class == cls) {
3340 w->InvalidateData(data, gui_scope);
3341 }
3342 }
3343}
3344
3349{
3350 for (Window *w : Window::Iterate()) {
3351 w->OnGameTick();
3352 }
3353}
3354
3362{
3363 /* Note: the container remains stable, even when deleting windows. */
3364 for (Window *w : Window::Iterate()) {
3366 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3367
3368 w->Close();
3369 }
3370 }
3371}
3372
3381{
3382 /* Note: the container remains stable, even when closing windows. */
3383 for (Window *w : Window::Iterate()) {
3385 w->Close();
3386 }
3387 }
3388}
3389
3394{
3396 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3397 InvalidateWindowData(WindowClass::MessageHistory, 0); // invalidate the message history
3398 CloseWindowById(WindowClass::News, 0); // close newspaper or general message window if shown
3399}
3400
3406{
3407 /* Note: the container remains stable, even when deleting windows. */
3408 for (Window *w : Window::Iterate()) {
3410 w->Close();
3411 }
3412 }
3413
3414 for (const Window *w : Window::Iterate()) w->SetDirty();
3415}
3416
3419{
3420 CloseWindowById(WindowClass::MainToolbar, 0);
3421 CloseWindowById(WindowClass::Statusbar, 0);
3422}
3423
3424void ReInitWindow(Window *w, bool zoom_changed)
3425{
3426 if (w == nullptr) return;
3427 if (zoom_changed) {
3428 w->nested_root->AdjustPaddingForZoom();
3430 }
3431 w->ReInit();
3432}
3433
3435void ReInitAllWindows(bool zoom_changed)
3436{
3438 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3439 NWidgetScrollbar::InvalidateDimensionCache();
3440
3442
3443 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3444 * so EnsureVisibleCaption uses the updated size information. */
3445 ReInitWindow(FindWindowById(WindowClass::MainToolbar, 0), zoom_changed);
3446 ReInitWindow(FindWindowById(WindowClass::Statusbar, 0), zoom_changed);
3447 for (Window *w : Window::Iterate()) {
3448 if (w->window_class == WindowClass::MainToolbar || w->window_class == WindowClass::Statusbar) continue;
3449 ReInitWindow(w, zoom_changed);
3450 }
3451
3454
3455 /* Make sure essential parts of all windows are visible */
3456 RelocateAllWindows(_screen.width, _screen.height);
3458}
3459
3467static int PositionWindow(Window *w, WindowClass clss, int setting)
3468{
3469 if (w == nullptr || w->window_class != clss) {
3470 w = FindWindowById(clss, 0);
3471 }
3472 if (w == nullptr) return 0;
3473
3474 int old_left = w->left;
3475 switch (setting) {
3476 case 1: w->left = (_screen.width - w->width) / 2; break;
3477 case 2: w->left = _screen.width - w->width; break;
3478 default: w->left = 0; break;
3479 }
3480 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3481 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3482 return w->left;
3483}
3484
3491{
3492 Debug(misc, 5, "Repositioning Main Toolbar...");
3493 return PositionWindow(w, WindowClass::MainToolbar, _settings_client.gui.toolbar_pos);
3494}
3495
3502{
3503 Debug(misc, 5, "Repositioning statusbar...");
3504 return PositionWindow(w, WindowClass::Statusbar, _settings_client.gui.statusbar_pos);
3505}
3506
3513{
3514 Debug(misc, 5, "Repositioning news message...");
3515 return PositionWindow(w, WindowClass::News, _settings_client.gui.statusbar_pos);
3516}
3517
3524{
3525 Debug(misc, 5, "Repositioning network chat window...");
3526 return PositionWindow(w, WindowClass::NetworkChat, _settings_client.gui.statusbar_pos);
3527}
3528
3529
3536{
3537 for (const Window *w : Window::Iterate()) {
3538 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3539 w->viewport->follow_vehicle = to_index;
3540 w->SetDirty();
3541 }
3542 }
3543}
3544
3545
3551void RelocateAllWindows(int neww, int newh)
3552{
3553 CloseWindowByClass(WindowClass::DropdownMenu);
3554
3555 /* Reposition toolbar then status bar before other all windows. */
3556 if (Window *wt = FindWindowById(WindowClass::MainToolbar, 0); wt != nullptr) {
3557 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3558 wt->left = PositionMainToolbar(wt);
3559 }
3560
3561 if (Window *ws = FindWindowById(WindowClass::Statusbar, 0); ws != nullptr) {
3562 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3563 ws->top = newh - ws->height;
3564 ws->left = PositionStatusbar(ws);
3565 }
3566
3567 for (Window *w : Window::Iterate()) {
3568 int left, top;
3569 /* XXX - this probably needs something more sane. For example specifying
3570 * in a 'backup'-desc that the window should always be centered. */
3571 switch (w->window_class) {
3572 case WindowClass::MainWindow:
3573 case WindowClass::Bootstrap:
3574 case WindowClass::Highscore:
3575 case WindowClass::Endscreen:
3576 ResizeWindow(w, neww, newh);
3577 continue;
3578
3579 case WindowClass::MainToolbar:
3580 case WindowClass::Statusbar:
3581 continue;
3582
3583 case WindowClass::News:
3584 top = newh - w->height;
3585 left = PositionNewsMessage(w);
3586 break;
3587
3588 case WindowClass::NetworkChat:
3589 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3590
3591 top = newh - w->height - FindWindowById(WindowClass::Statusbar, 0)->height;
3592 left = PositionNetworkChatWindow(w);
3593 break;
3594
3595 case WindowClass::Console:
3596 IConsoleResize(w);
3597 continue;
3598
3599 default: {
3600 if (w->flags.Test(WindowFlag::Centred)) {
3601 top = (newh - w->height) >> 1;
3602 left = (neww - w->width) >> 1;
3603 break;
3604 }
3605
3606 left = w->left;
3607 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3608 if (left < 0) left = 0;
3609
3610 top = w->top;
3611 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3612 break;
3613 }
3614 }
3615
3616 EnsureVisibleCaption(w, left, top);
3617 }
3618}
3619
3625void PickerWindowBase::Close([[maybe_unused]] int data)
3626{
3628 this->Window::Close();
3629}
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Flip()
Flip all bits.
constexpr Timpl & Reset()
Reset all bits.
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:955
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:1296
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1269
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:2691
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:3625
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.
@ None
A path without any base directory.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:88
void ProcessPendingPerformanceMeasurements()
This drains the PerformanceElement::Sound measurement data queue into _pf_data.
Types for recording game performance data.
@ Drawing
Speed of drawing world and GUI.
@ ViewportDrawing
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
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
DirectionKeys _dirkeys
Pressed direction keys.
Definition gfx.cpp:35
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.
@ 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:315
@ Invalid
Invalid colour.
Definition gfx_type.h:336
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
Definition gfx_type.h:417
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:3450
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:975
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:1520
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1456
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1553
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:690
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:723
@ Bootstrap
In the content bootstrap process.
Definition openttd.h:22
@ Menu
In the main menu.
Definition openttd.h:19
Types related to the osk widgets.
@ 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:899
void IniSaveWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Save a WindowDesc to config.
Definition settings.cpp:910
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.
@ MainViewportFullscreen
Scroll main viewport at edge when using fullscreen.
@ EveryViewport
Scroll all viewports at their edges.
@ Disabled
Do not autoscroll when mouse is at edge of viewport.
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:254
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.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > 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:848
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:790
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:818
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:217
uint step_width
Step-size of width resize changes.
Definition window_gui.h:216
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:172
int16_t GetDefaultWidth() const
Determine default width of window.
Definition window.cpp:145
~WindowDesc()
Remove ourselves from the global list of window descs.
Definition window.cpp:135
static void SaveToConfig()
Save all WindowDesc settings to _windows_file.
Definition window.cpp:182
int16_t pref_width
User-preferred width of the window. Zero if unset.
Definition window_gui.h:191
const WindowPosition default_pos
Preferred position of the window.
Definition window_gui.h:182
bool pref_sticky
Preferred stickyness.
Definition window_gui.h:190
int16_t pref_height
User-preferred height of the window. Zero if unset.
Definition window_gui.h:192
int16_t GetDefaultHeight() const
Determine default height of window.
Definition window.cpp:155
const int16_t default_height_trad
Preferred initial height of the window (pixels at 1x zoom).
Definition window_gui.h:202
const int16_t default_width_trad
Preferred initial width of the window (pixels at 1x zoom).
Definition window_gui.h:201
const WindowClass cls
Class of the window,.
Definition window_gui.h:183
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:185
const std::source_location source_location
Source location of this definition.
Definition window_gui.h:181
const WindowDefaultFlags flags
Flags.
Definition window_gui.h:186
static void LoadFromConfig()
Load all WindowDesc settings from _windows_file.
Definition window.cpp:163
const WindowClass parent_cls
Class of the parent window.
Definition window_gui.h:184
const HotkeyList * hotkeys
Hotkeys for the window.
Definition window_gui.h:188
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:115
const std::span< const NWidgetPart > nwid_parts
Span of nested widget parts describing the window.
Definition window_gui.h:187
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:273
virtual const struct Textbuf * GetFocusedTextbuf() const
Get the current input text buffer.
Definition window.cpp:373
void SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
Sets the highlighted status of a widget.
Definition window.cpp:247
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:987
void CloseChildWindows(WindowClass wc=WindowClass::Invalid) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1084
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1112
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Definition window_gui.h:798
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1817
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:320
virtual void ApplyDefaults()
Read default values from WindowDesc configuration an apply them to the window.
Definition window.cpp:199
uint8_t white_border_timer
Timer value of the WindowFlag::WhiteBorder for flags.
Definition window_gui.h:307
NWidgetStacked * shade_select
Selection widget (NWID_SELECTION) to use for shading the window. If nullptr, window cannot shade.
Definition window_gui.h:323
void InitializePositionSize(int x, int y, int min_width, int min_height)
Set the position and smallest size of the window.
Definition window.cpp:1466
Dimension unshaded_size
Last known unshaded size (only valid while shaded).
Definition window_gui.h:324
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3258
virtual EventState OnKeyPress(char32_t key, uint16_t keycode)
A key has been pressed.
Definition window_gui.h:653
Window * parent
Parent window.
Definition window_gui.h:328
AllWindows< false > IterateFromBack
Iterate all windows in Z order from back to front.
Definition window_gui.h:940
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition window.cpp:1149
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:469
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:565
uint8_t timeout_timer
Timer value of the WindowFlag::Timeout for flags.
Definition window_gui.h:306
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:513
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:326
virtual void OnGameTick()
Called once per (game) tick.
Definition window_gui.h:749
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition window_gui.h:874
virtual bool OnRightClick(Point pt, WidgetID widget)
A click with the right mouse button has been made on the window.
Definition window_gui.h:680
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:723
virtual void OnDropdownSelect(WidgetID widget, int index, int click_result)
A dropdown option associated to this window has been selected.
Definition window_gui.h:775
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition window.cpp:3271
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:478
virtual void OnMouseLoop()
Called for every mouse loop run, which is at least once per (game) tick.
Definition window_gui.h:744
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition window.cpp:1030
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:304
void ScheduleResize()
Mark this window as resized and in need of OnResize() event.
Definition window.cpp:3236
virtual void OnPaint()
The window must be repainted.
Definition window_gui.h:598
virtual void OnDragDrop(Point pt, WidgetID widget)
A dragged 'object' has been released.
Definition window_gui.h:710
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1807
WindowDesc & window_desc
Window description.
Definition window_gui.h:299
WindowClass window_class
Window class.
Definition window_gui.h:301
virtual void OnRealtimeTick(uint delta_ms)
Called periodically.
Definition window_gui.h:755
virtual void OnMouseWheel(int wheel, WidgetID widget)
The mouse wheel has been turned.
Definition window_gui.h:738
AllWindows< true > IterateFromFront
Iterate all windows in Z order from front to back.
Definition window_gui.h:941
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1099
void SetWhiteBorder()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:364
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:494
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:530
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:491
static std::vector< Window * > closed_windows
List of closed windows to delete.
Definition window_gui.h:275
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition window.cpp:539
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1796
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:316
virtual Point GetCaretPosition() const
Get the current caret position if an edit box has the focus.
Definition window.cpp:386
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1485
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:293
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:2735
WindowIterator< false > IteratorToFront
Iterate in Z order towards front.
Definition window_gui.h:917
int left
x position of left edge of the window
Definition window_gui.h:309
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:562
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:355
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:319
virtual void OnEditboxChanged(WidgetID widget)
The text in an editbox has been edited.
Definition window_gui.h:783
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition window.cpp:362
int top
y position of top edge of the window
Definition window_gui.h:310
virtual void OnClick(Point pt, WidgetID widget, int click_count)
A click with the left mouse button has been made on the window.
Definition window_gui.h:671
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:342
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:322
virtual ptrdiff_t GetTextCharacterAtPosition(const Point &pt) const
Get the character that is rendered at a position by the focused edit box.
Definition window.cpp:417
Window * FindChildWindow(WindowClass wc=WindowClass::Invalid) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1056
std::vector< int > scheduled_invalidation_data
Data of scheduled OnInvalidateData() calls.
Definition window_gui.h:282
void InitializeData(WindowNumber window_number)
Initializes the data (except the position and initial size) of a new Window.
Definition window.cpp:1428
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:731
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1841
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:218
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:989
virtual bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition window_gui.h:696
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:460
virtual EventState OnCTRLStateChange()
The state of the control key has changed.
Definition window_gui.h:662
void ProcessScheduledResize()
Process scheduled OnResize() event.
Definition window.cpp:3244
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2573
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:703
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:604
virtual void OnResize()
Called after the window got resized.
Definition window_gui.h:767
Window * FindChildWindowById(WindowClass wc, WindowNumber number) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1071
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:522
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1831
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition window_gui.h:760
WindowFlags flags
Window flags.
Definition window_gui.h:300
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:322
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3283
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:579
static void DeleteClosedWindows()
Delete all closed windows.
Definition window.cpp:63
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:321
bool scheduled_resize
Set if window has been resized.
Definition window_gui.h:283
virtual Rect GetTextBoundingRect(size_t from, size_t to) const
Get the bounding rectangle for a text range if an edit box has the focus.
Definition window.cpp:402
bool IsWidgetHighlighted(WidgetID widget_index) const
Gets the highlighted status of a widget.
Definition window.cpp:277
void DisableAllWidgetHighlight()
Disable the highlighted status of all widgets.
Definition window.cpp:229
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:858
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:939
int height
Height of the window (number of pixels down in y direction).
Definition window_gui.h:312
virtual void OnHover(Point pt, WidgetID widget)
The mouse is hovering over a widget in the window, perform an action for it.
Definition window_gui.h:687
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
virtual void OnInit()
Notification that the nested widget tree gets initialized.
Definition window_gui.h:581
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
@ 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:293
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:269
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:3501
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:2030
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2165
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:3405
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:1204
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:1737
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1190
const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK
Time between 2 left clicks before it becoming a double click.
Definition window.cpp:2816
static Point _drag_delta
delta between mouse cursor and upper left corner of dragged window
Definition window.cpp:50
static bool MayBeShown(const Window *w)
Returns whether a window may be shown or not.
Definition window.cpp:864
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1233
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2718
MouseClick
Mouse states during the MouseLoop.
Definition window.cpp:2803
@ None
No action to process.
Definition window.cpp:2804
@ Right
A click with the right mouse button.
Definition window.cpp:2806
@ Left
A click with the left mouse button.
Definition window.cpp:2805
@ DoubleLeft
A double click with the left mouse button.
Definition window.cpp:2807
@ Hover
The mouse started hovering.
Definition window.cpp:2808
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:892
bool _scrolling_viewport
A viewport is being scrolled with the mouse.
Definition window.cpp:88
void InputLoop()
Regular call from the global game loop.
Definition window.cpp:3078
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3144
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3490
Window * _focused_window
Window that currently has focus.
Definition window.cpp:80
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3361
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:954
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2108
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:1587
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3523
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2360
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:1626
PreventHideDirection
Direction for moving the window.
Definition window.cpp:2015
@ Down
Below v is a safe position.
Definition window.cpp:2017
@ Up
Above v is a safe position.
Definition window.cpp:2016
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2765
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:73
void HandleToolbarHotkey(int hotkey)
Handle Toolbar hotkey events - can come from a source like the MacBook Touch Bar.
Definition window.cpp:2645
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3467
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:430
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:1551
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2811
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1176
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3535
static EventState HandleViewportScroll()
Handle viewport scrolling with the mouse.
Definition window.cpp:2449
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:827
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:625
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1853
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3393
static Window * _last_scroll_window
Window of the last scroll event.
Definition window.cpp:52
int GetMainViewTop()
Return the top of the main view available for general use.
Definition window.cpp:2148
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:1409
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3435
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:2070
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1217
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2982
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:1694
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2171
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3512
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2159
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:799
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:1253
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2662
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3226
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:2812
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:470
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1903
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1325
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:456
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:3318
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3418
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:174
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1288
Point AlignInitialConstructionToolbar(int window_width)
Compute the position of the construction toolbars.
Definition window.cpp:1710
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3551
std::string _windows_file
Config file to store WindowDesc.
Definition window.cpp:100
static void HandleScrollbarScrolling(Window *w)
Handle scrollbar scrolling with the mouse.
Definition window.cpp:2376
bool _mouse_hovering
The mouse is hovering over the same point.
Definition window.cpp:89
static void StartWindowDrag(Window *w)
Start window dragging.
Definition window.cpp:2343
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3348
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2416
std::vector< WindowDesc * > * _window_descs
List of all WindowDescs.
Definition window.cpp:97
static void DispatchRightClickEvent(Window *w, int x, int y)
Dispatch right mouse-button click in window.
Definition window.cpp:770
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:2836
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2513
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:55
void CallWindowRealtimeTickEvent(uint delta_ms)
Dispatch OnRealtimeTick event over all windows.
Definition window.cpp:3105
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:91
static EventState HandleMouseDragDrop()
Handle dragging and dropping in mouse dragging mode (SpecialMouseMode::DragDrop).
Definition window.cpp:1968
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3380
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1992
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:51
static void CheckSoftLimit()
Check the soft limit of deletable (non vital, non sticky) windows.
Definition window.cpp:3053
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1161
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1889
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1867
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:3212
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:2754
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3196
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:3336
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:155
@ NoClose
This window can't be interactively closed.
Definition window_gui.h:158
@ NoFocus
This window won't get focus/make any other window lose focus when click.
Definition window_gui.h:157
@ Modal
The window is a modal child of some other window, meaning the parent is 'inactive'.
Definition window_gui.h:156
@ RightClick
Close the tooltip when releasing the right mouse button.
Definition window_gui.h:264
@ Hover
Close the tooltip when stopping to hovering, i.e. moving the mouse.
Definition window_gui.h:265
Window * _focused_window
Window that currently has focus.
Definition window.cpp:80
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:430
@ SizingLeft
Window is being resized towards the left.
Definition window_gui.h:228
@ DisableVpScroll
Window does not do autoscroll,.
Definition window_gui.h:231
@ Highlighted
Window has a widget that has a highlight.
Definition window_gui.h:233
@ Centred
Window is centered and shall stay centered after ReInit.
Definition window_gui.h:234
@ Dragging
Window is being dragged.
Definition window_gui.h:226
@ SizingRight
Window is being resized towards the right.
Definition window_gui.h:227
@ WhiteBorder
Window white border counter bit mask.
Definition window_gui.h:232
@ Timeout
Window timeout counter.
Definition window_gui.h:224
@ Sticky
Window is made sticky by user.
Definition window_gui.h:230
static const int TIMEOUT_DURATION
The initial timeout value for WindowFlag::Timeout.
Definition window_gui.h:240
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:293
SpecialMouseMode
Mouse modes.
@ DragDrop
Drag&drop an object.
@ Presize
Presizing mode (docks, tunnels).
EnumBitSet< WindowDefaultFlag, uint8_t > WindowDefaultFlags
Bitset of WindowDefaultFlag elements.
Definition window_gui.h:162
WindowPosition
How do we the window to be placed?
Definition window_gui.h:144
@ AlignToolbar
Align toward the toolbar.
Definition window_gui.h:148
@ Automatic
Find a place automatically.
Definition window_gui.h:146
@ Center
Center the window.
Definition window_gui.h:147
@ Manual
Manually align the window (so no automatic location finding).
Definition window_gui.h:145
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:55
int WidgetID
Widget ID.
Definition window_type.h:21
EventState
State of handling an event.
@ Handled
The passed event is handled.
@ NotHandled
The passed event is not handled.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Definition window_type.h:24
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