OpenTTD Source 20260621-master-g720d10536d
window.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "company_func.h"
12#include "gfx_func.h"
13#include "console_func.h"
14#include "console_gui.h"
15#include "viewport_func.h"
16#include "progress.h"
17#include "blitter/factory.hpp"
18#include "zoom_func.h"
19#include "vehicle_base.h"
20#include "depot_func.h"
21#include "window_func.h"
22#include "tilehighlight_func.h"
23#include "network/network.h"
24#include "querystring_gui.h"
25#include "strings_func.h"
26#include "settings_type.h"
27#include "settings_func.h"
28#include "ini_type.h"
29#include "newgrf_debug.h"
30#include "hotkeys.h"
31#include "toolbar_gui.h"
32#include "statusbar_gui.h"
33#include "error.h"
34#include "game/game.hpp"
36#include "framerate_type.h"
38#include "news_func.h"
39#include "sound_func.h"
40#include "timer/timer.h"
41#include "timer/timer_window.h"
42
43#include "table/strings.h"
44
45#include "safeguards.h"
46
48static Window *_mouseover_last_w = nullptr;
49static Window *_last_scroll_window = nullptr;
50
52WindowList _z_windows;
53
55/* static */ std::vector<Window *> Window::closed_windows;
56
60/* static */ void Window::DeleteClosedWindows()
61{
62 for (Window *w : Window::closed_windows) delete w;
64
65 /* Remove dead entries from the window list */
66 _z_windows.remove(nullptr);
67}
68
71
78
79Point _cursorpos_drag_start;
80
81int _scrollbar_start_pos;
82int _scrollbar_size;
83uint8_t _scroller_click_timeout = 0;
84
87
89
94std::vector<WindowDesc*> *_window_descs = nullptr;
95
97std::string _windows_file;
98
112WindowDesc::WindowDesc(WindowPosition def_pos, std::string_view ini_key, int16_t def_width_trad, int16_t def_height_trad,
113 WindowClass window_class, WindowClass parent_class, WindowDefaultFlags flags,
114 const std::span<const NWidgetPart> nwid_parts, HotkeyList *hotkeys,
115 const std::source_location location) :
116 source_location(location),
117 default_pos(def_pos),
118 cls(window_class),
119 parent_cls(parent_class),
121 flags(flags),
124 default_width_trad(def_width_trad),
125 default_height_trad(def_height_trad)
126{
127 if (_window_descs == nullptr) _window_descs = new std::vector<WindowDesc*>();
128 _window_descs->push_back(this);
129}
130
133{
134 _window_descs->erase(std::ranges::find(*_window_descs, this));
135}
136
143{
144 return this->pref_width != 0 ? this->pref_width : ScaleGUITrad(this->default_width_trad);
145}
146
153{
154 return this->pref_height != 0 ? this->pref_height : ScaleGUITrad(this->default_height_trad);
155}
156
161{
162 IniFile ini;
164 for (WindowDesc *wd : *_window_descs) {
165 if (wd->ini_key.empty()) continue;
166 IniLoadWindowSettings(ini, wd->ini_key, wd);
167 }
168}
169
171static bool DescSorter(WindowDesc * const &a, WindowDesc * const &b)
172{
173 return a->ini_key < b->ini_key;
174}
175
180{
181 /* Sort the stuff to get a nice ini file on first write */
182 std::sort(_window_descs->begin(), _window_descs->end(), DescSorter);
183
184 IniFile ini;
186 for (WindowDesc *wd : *_window_descs) {
187 if (wd->ini_key.empty()) continue;
188 IniSaveWindowSettings(ini, wd->ini_key, wd);
189 }
191}
192
197{
198 if (this->nested_root != nullptr && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != nullptr) {
199 if (this->window_desc.pref_sticky) this->flags.Set(WindowFlag::Sticky);
200 } else {
201 /* There is no stickybox; clear the preference in case someone tried to be funny */
202 this->window_desc.pref_sticky = false;
203 }
204}
205
215int Window::GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height) const
216{
217 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
218 if (line_height < 0) line_height = wid->resize_y;
219 if (clickpos < wid->pos_y + padding) return INT_MAX;
220 return (clickpos - wid->pos_y - padding) / line_height;
221}
222
227{
228 for (auto &pair : this->widget_lookup) {
229 NWidgetBase *nwid = pair.second;
230 if (nwid->IsHighlighted()) {
232 nwid->SetDirty(this);
233 }
234 }
235
236 this->flags.Reset(WindowFlag::Highlighted);
237}
238
244void Window::SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
245{
246 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
247 if (nwid == nullptr) return;
248
249 nwid->SetHighlighted(highlighted_colour);
250 nwid->SetDirty(this);
251
252 if (highlighted_colour != TextColour::Invalid) {
253 /* If we set a highlight, the window has a highlight */
255 } else {
256 /* If we disable a highlight, check all widgets if anyone still has a highlight */
257 bool valid = false;
258 for (const auto &pair : this->widget_lookup) {
259 nwid = pair.second;
260 if (!nwid->IsHighlighted()) continue;
261
262 valid = true;
263 }
264 /* If nobody has a highlight, disable the flag on the window */
265 if (!valid) this->flags.Reset(WindowFlag::Highlighted);
266 }
267}
268
274bool Window::IsWidgetHighlighted(WidgetID widget_index) const
275{
276 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
277 if (nwid == nullptr) return false;
278
279 return nwid->IsHighlighted();
280}
281
290void Window::OnDropdownClose(Point pt, WidgetID widget, int index, int click_result, bool instant_close)
291{
292 if (widget < 0) return;
293
294 /* Many dropdown selections depend on the position of the main toolbar,
295 * so if it doesn't exist (e.g. the end screen has appeared), just skip the instant close behaviour. */
296 if (instant_close && FindWindowById(WindowClass::MainToolbar, 0) != nullptr) {
297 /* Send event for selected option if we're still
298 * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
299 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
300 this->OnDropdownSelect(widget, index, click_result);
301 }
302 }
303
304 /* Raise the dropdown button */
305 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
306 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
308 } else {
309 this->RaiseWidget(widget);
310 }
311 this->SetWidgetDirty(widget);
312}
313
320{
321 return this->GetWidget<NWidgetScrollbar>(widnum);
322}
323
330{
331 return this->GetWidget<NWidgetScrollbar>(widnum);
332}
333
340{
341 auto query = this->querystrings.find(widnum);
342 return query != this->querystrings.end() ? query->second : nullptr;
343}
344
351{
352 auto query = this->querystrings.find(widnum);
353 return query != this->querystrings.end() ? query->second : nullptr;
354}
355
360{
361 for (auto &qs : this->querystrings) {
362 qs.second->text.UpdateSize();
363 }
364}
365
370/* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
371{
372 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
373 return &this->GetQueryString(this->nested_focus->GetIndex())->text;
374 }
375
376 return nullptr;
377}
378
383/* virtual */ Point Window::GetCaretPosition() const
384{
385 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
386 return this->GetQueryString(this->nested_focus->GetIndex())->GetCaretPosition(this, this->nested_focus->GetIndex());
387 }
388
389 Point pt = {0, 0};
390 return pt;
391}
392
399/* virtual */ Rect Window::GetTextBoundingRect(size_t from, size_t to) const
400{
401 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
402 return this->GetQueryString(this->nested_focus->GetIndex())->GetBoundingRect(this, this->nested_focus->GetIndex(), from, to);
403 }
404
405 Rect r = {0, 0, 0, 0};
406 return r;
407}
408
414/* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
415{
416 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
417 return this->GetQueryString(this->nested_focus->GetIndex())->GetCharAtPosition(this, this->nested_focus->GetIndex(), pt);
418 }
419
420 return -1;
421}
422
428{
429 if (_focused_window == w) return;
430
431 /* Don't focus a tooltip */
432 if (w != nullptr && w->window_class == WindowClass::ToolTips) return;
433
434 /* Invalidate focused widget */
435 if (_focused_window != nullptr) {
436 if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
437 }
438
439 /* Remember which window was previously focused */
440 Window *old_focused = _focused_window;
441 _focused_window = w;
442
443 /* So we can inform it that it lost focus */
444 if (old_focused != nullptr) old_focused->OnFocusLost(false);
445 if (_focused_window != nullptr) _focused_window->OnFocus();
446}
447
454{
455 if (_focused_window == nullptr) return false;
456
457 /* The console does not have an edit box so a special case is needed. */
458 if (_focused_window->window_class == WindowClass::Console) return true;
459
460 return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
461}
462
468{
469 return _focused_window && _focused_window->window_class == WindowClass::Console;
470}
471
476{
477 if (this->nested_focus != nullptr) {
479
480 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
481 this->nested_focus->SetDirty(this);
482 this->nested_focus = nullptr;
483 }
484}
485
492{
493 NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
494 assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
495
496 if (this->nested_focus != nullptr) {
497 /* Do nothing if widget_index is already focused. */
498 if (widget == this->nested_focus) return false;
499
500 /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
501 this->nested_focus->SetDirty(this);
503 }
504
505 this->nested_focus = widget;
507 return true;
508}
509
510std::string Window::GetWidgetString([[maybe_unused]] WidgetID widget, StringID stringid) const
511{
512 if (stringid == STR_NULL) return {};
513 return GetString(stringid);
514}
515
520{
521 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxGainedFocus();
522}
523
528{
529 if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
530}
531
536void Window::RaiseButtons(bool autoraise)
537{
538 for (auto &pair : this->widget_lookup) {
539 WidgetType type = pair.second->type;
540 NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
541 if (wid != nullptr && ((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
542 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
543 wid->SetLowered(false);
544 wid->SetDirty(this);
545 }
546 }
547
548 /* Special widgets without widget index */
549 {
550 NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
551 if (wid != nullptr) {
552 wid->SetLowered(false);
553 wid->SetDirty(this);
554 }
555 }
556}
557
562void Window::SetWidgetDirty(WidgetID widget_index) const
563{
564 /* Sometimes this function is called before the window is even fully initialized */
565 auto it = this->widget_lookup.find(widget_index);
566 if (it == std::end(this->widget_lookup)) return;
567
568 it->second->SetDirty(this);
569}
570
577{
578 if (hotkey < 0) return EventState::NotHandled;
579
580 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
581 if (nw == nullptr || nw->IsDisabled()) return EventState::NotHandled;
582
583 if (nw->type == WWT_EDITBOX) {
584 if (this->IsShaded()) return EventState::NotHandled;
585
586 /* Focus editbox */
587 this->SetFocusedWidget(hotkey);
588 SetFocusedWindow(this);
589 } else {
590 /* Click button */
591 this->OnClick(Point(), hotkey, 1);
592 }
593 return EventState::Handled;
594}
595
602{
603 /* Button click for this widget may already have been handled. */
604 if (this->IsWidgetLowered(widget) && this->timeout_timer == TIMEOUT_DURATION) return;
605
606 this->LowerWidget(widget);
607 this->SetTimeout();
608 this->SetWidgetDirty(widget);
609 SndClickBeep();
610}
611
612static void StartWindowDrag(Window *w);
613static void StartWindowSizing(Window *w, bool to_left);
614
622static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
623{
624 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
625 WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
626
627 /* Allow dropdown close flag detection to work. */
629
630 bool focused_widget_changed = false;
631
632 /* If clicked on a window that previously did not have focus */
633 if (_focused_window != w) {
634 /* Don't switch focus to an unfocusable window, or if the 'X' (close button) was clicked. */
635 if (!w->window_desc.flags.Test(WindowDefaultFlag::NoFocus) && widget_type != WWT_CLOSEBOX) {
636 focused_widget_changed = true;
638 } else if (_focused_window != nullptr && _focused_window->window_class == WindowClass::DropdownMenu) {
639 /* The previously focused window was a dropdown menu, but the user clicked on another window that
640 * isn't focusable. Close the dropdown menu anyway. */
641 SetFocusedWindow(nullptr);
642 }
643 }
644
645 if (nw == nullptr) return; // exit if clicked outside of widgets
646
647 /* don't allow any interaction if the button has been disabled */
648 if (nw->IsDisabled()) return;
649
650 WidgetID widget_index = nw->GetIndex();
651
652 /* Clicked on a widget that is not disabled.
653 * So unless the clicked widget is the caption bar, change focus to this widget.
654 * Exception: In the OSK we always want the editbox to stay focused. */
655 if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WindowClass::OnScreenKeyboard) {
656 /* focused_widget_changed is 'now' only true if the window this widget
657 * is in gained focus. In that case it must remain true, also if the
658 * local widget focus did not change. As such it's the logical-or of
659 * both changed states.
660 *
661 * If this is not preserved, then the OSK window would be opened when
662 * a user has the edit box focused and then click on another window and
663 * then back again on the edit box (to type some text).
664 */
665 focused_widget_changed |= w->SetFocusedWidget(widget_index);
666 }
667
668 /* Dropdown window of this widget was closed so don't process click this time. */
670
671 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
672
673 Point pt = { x, y };
674
675 switch (widget_type) {
676 case NWID_VSCROLLBAR:
677 case NWID_HSCROLLBAR:
678 ScrollbarClickHandler(w, nw, x, y);
679 break;
680
681 case WWT_EDITBOX: {
682 QueryString *query = w->GetQueryString(widget_index);
683 if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
684 break;
685 }
686
687 case WWT_CLOSEBOX: // 'X'
688 w->Close();
689 return;
690
691 case WWT_CAPTION: // 'Title bar'
693 return;
694
695 case WWT_RESIZEBOX:
696 /* When the resize widget is on the left size of the window
697 * we assume that that button is used to resize to the left. */
698 StartWindowSizing(w, nw->pos_x < (w->width / 2));
699 nw->SetDirty(w);
700 return;
701
702 case WWT_DEFSIZEBOX: {
703 if (_ctrl_pressed) {
704 if (click_count > 1) {
705 w->window_desc.pref_width = 0;
707 } else {
710 }
711 } else {
712 int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
713 int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
714
715 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
716 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
717 /* dx and dy has to go by step.. calculate it.
718 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
719 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
720 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
721 ResizeWindow(w, dx, dy, false);
722 }
723
724 nw->SetLowered(true);
725 nw->SetDirty(w);
726 w->SetTimeout();
727 break;
728 }
729
730 case WWT_DEBUGBOX:
732 break;
733
734 case WWT_SHADEBOX:
735 nw->SetDirty(w);
736 w->SetShaded(!w->IsShaded());
737 return;
738
739 case WWT_STICKYBOX:
741 nw->SetDirty(w);
743 return;
744
745 default:
746 break;
747 }
748
749 /* Widget has no index, so the window is not interested in it. */
750 if (widget_index < 0) return;
751
752 /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
753 if (w->IsWidgetHighlighted(widget_index)) {
754 w->SetWidgetHighlight(widget_index, TextColour::Invalid);
755 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
756 }
757
758 w->OnClick(pt, widget_index, click_count);
759}
760
767static void DispatchRightClickEvent(Window *w, int x, int y)
768{
769 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
770 if (wid == nullptr) return;
771
772 Point pt = { x, y };
773
774 /* No widget to handle, or the window is not interested in it. */
775 if (wid->GetIndex() >= 0) {
776 if (w->OnRightClick(pt, wid->GetIndex())) return;
777 }
778
779 /* Right-click close is enabled and there is a closebox. */
780 if (_settings_client.gui.right_click_wnd_close == RightClickClose::Yes && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
781 w->Close();
782 } else if (_settings_client.gui.right_click_wnd_close == RightClickClose::YesExceptSticky && !w->flags.Test(WindowFlag::Sticky) && !w->window_desc.flags.Test(WindowDefaultFlag::NoClose)) {
783 /* Right-click close is enabled, but excluding sticky windows. */
784 w->Close();
785 } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->GetIndex(), TooltipCloseCondition::RightClick) && wid->GetToolTip() != STR_NULL) {
787 }
788}
789
796static void DispatchHoverEvent(Window *w, int x, int y)
797{
798 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
799
800 /* No widget to handle */
801 if (wid == nullptr) return;
802
803 Point pt = { x, y };
804
805 /* Show the tooltip if there is any */
806 if (!w->OnTooltip(pt, wid->GetIndex(), TooltipCloseCondition::Hover) && wid->GetToolTip() != STR_NULL) {
808 return;
809 }
810
811 /* Widget has no index, so the window is not interested in it. */
812 if (wid->GetIndex() < 0) return;
813
814 w->OnHover(pt, wid->GetIndex());
815}
816
824static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
825{
826 if (nwid == nullptr) return;
827
828 /* Using wheel on caption/shade-box shades or unshades the window. */
829 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
830 w->SetShaded(wheel < 0);
831 return;
832 }
833
834 /* Wheeling a vertical scrollbar. */
835 if (nwid->type == NWID_VSCROLLBAR) {
836 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
837 if (sb->GetCount() > sb->GetCapacity()) {
838 if (sb->UpdatePosition(wheel)) {
839 w->OnScrollbarScroll(nwid->GetIndex());
840 w->SetDirty();
841 }
842 }
843 return;
844 }
845
846 /* Scroll the widget attached to the scrollbar. */
847 Scrollbar *sb = (nwid->GetScrollbarIndex() >= 0 ? w->GetScrollbar(nwid->GetScrollbarIndex()) : nullptr);
848 if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
849 if (sb->UpdatePosition(wheel)) {
851 w->SetDirty();
852 }
853 }
854}
855
861static bool MayBeShown(const Window *w)
862{
863 /* If we're not modal, everything is okay. */
864 if (!HasModalProgress()) return true;
865
866 switch (w->window_class) {
867 case WindowClass::MainWindow:
868 case WindowClass::ModalProgress:
869 case WindowClass::ConfirmPopupQuery:
870 return true;
871
872 default:
873 return false;
874 }
875}
876
889static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
890{
892 ++it;
893 for (; !it.IsEnd(); ++it) {
894 const Window *v = *it;
895 if (MayBeShown(v) &&
896 right > v->left &&
897 bottom > v->top &&
898 left < v->left + v->width &&
899 top < v->top + v->height) {
900 /* v and rectangle intersect with each other */
901 int x;
902
903 if (left < (x = v->left)) {
904 DrawOverlappedWindow(w, left, top, x, bottom);
905 DrawOverlappedWindow(w, x, top, right, bottom);
906 return;
907 }
908
909 if (right > (x = v->left + v->width)) {
910 DrawOverlappedWindow(w, left, top, x, bottom);
911 DrawOverlappedWindow(w, x, top, right, bottom);
912 return;
913 }
914
915 if (top < (x = v->top)) {
916 DrawOverlappedWindow(w, left, top, right, x);
917 DrawOverlappedWindow(w, left, x, right, bottom);
918 return;
919 }
920
921 if (bottom > (x = v->top + v->height)) {
922 DrawOverlappedWindow(w, left, top, right, x);
923 DrawOverlappedWindow(w, left, x, right, bottom);
924 return;
925 }
926
927 return;
928 }
929 }
930
931 /* Setup blitter, and dispatch a repaint event to window *wz */
932 DrawPixelInfo *dp = _cur_dpi;
933 dp->width = right - left;
934 dp->height = bottom - top;
935 dp->left = left - w->left;
936 dp->top = top - w->top;
937 dp->pitch = _screen.pitch;
938 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
939 dp->zoom = ZoomLevel::Min;
940 w->OnPaint();
941}
942
951void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
952{
953 DrawPixelInfo bk;
954 AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
955
956 for (Window *w : Window::IterateFromBack()) {
957 if (MayBeShown(w) &&
958 right > w->left &&
959 bottom > w->top &&
960 left < w->left + w->width &&
961 top < w->top + w->height) {
962 /* Window w intersects with the rectangle => needs repaint */
963 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));
964 }
965 }
966}
967
973{
974 AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
975}
976
984void Window::ReInit(int rx, int ry, bool reposition)
985{
986 this->SetDirty(); // Mark whole current window as dirty.
987
988 /* Save current size. */
989 int window_width = this->width * _gui_scale / this->scale;
990 int window_height = this->height * _gui_scale / this->scale;
991 this->scale = _gui_scale;
992
993 this->OnInit();
994 /* Re-initialize window smallest size. */
995 this->nested_root->SetupSmallestSize(this);
996 this->nested_root->AssignSizePosition(SizingType::Smallest, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
997 this->width = this->nested_root->smallest_x;
998 this->height = this->nested_root->smallest_y;
999 this->resize.step_width = this->nested_root->resize_x;
1000 this->resize.step_height = this->nested_root->resize_y;
1001
1002 /* Resize as close to the original size + requested resize as possible. */
1003 window_width = std::max(window_width + rx, this->width);
1004 window_height = std::max(window_height + ry, this->height);
1005 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
1006 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
1007 /* dx and dy has to go by step.. calculate it.
1008 * The cast to int is necessary else dx/dy are implicitly cast to unsigned int, which won't work. */
1009 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
1010 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
1011
1012 if (reposition) {
1013 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1014 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1015 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), false);
1016 }
1017
1018 ResizeWindow(this, dx, dy, true, false);
1019 /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
1020}
1021
1027void Window::SetShaded(bool make_shaded)
1028{
1029 if (this->shade_select == nullptr) return;
1030
1031 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1032 if (this->shade_select->shown_plane != desired) {
1033 if (make_shaded) {
1034 if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1035 this->unshaded_size.width = this->width;
1036 this->unshaded_size.height = this->height;
1037 this->shade_select->SetDisplayedPlane(desired);
1038 this->ReInit(0, -this->height);
1039 } else {
1040 this->shade_select->SetDisplayedPlane(desired);
1041 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1042 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1043 this->ReInit(dx, dy);
1044 }
1045 }
1046}
1047
1053Window *Window::FindChildWindow(WindowClass wc) const
1054{
1055 for (Window *v : Window::Iterate()) {
1056 if ((wc == WindowClass::Invalid || wc == v->window_class) && v->parent == this) return v;
1057 }
1058
1059 return nullptr;
1060}
1061
1069{
1070 for (Window *v : Window::Iterate()) {
1071 if (wc == v->window_class && number == v->window_number && v->parent == this) return v;
1072 }
1073
1074 return nullptr;
1075}
1076
1081void Window::CloseChildWindows(WindowClass wc) const
1082{
1083 Window *child = this->FindChildWindow(wc);
1084 while (child != nullptr) {
1085 child->Close();
1086 child = this->FindChildWindow(wc);
1087 }
1088}
1089
1090
1096void Window::CloseChildWindowById(WindowClass wc, WindowNumber number) const
1097{
1098 Window *child = this->FindChildWindowById(wc, number);
1099 while (child != nullptr) {
1100 child->Close();
1101 child = this->FindChildWindowById(wc, number);
1102 }
1103}
1104
1109void Window::Close([[maybe_unused]] int data)
1110{
1111 /* Don't close twice. */
1112 if (*this->z_position == nullptr) return;
1113
1114 *this->z_position = nullptr;
1115
1116 if (_thd.window_class == this->window_class &&
1117 _thd.window_number == this->window_number) {
1119 }
1120
1121 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1122 if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1123
1124 /* We can't scroll the window when it's closed. */
1125 if (_last_scroll_window == this) _last_scroll_window = nullptr;
1126
1127 /* Make sure we don't try to access non-existing query strings. */
1128 this->querystrings.clear();
1129
1130 /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1131 if (_focused_window == this) {
1132 this->OnFocusLost(true);
1133 _focused_window = nullptr;
1134 }
1135
1136 this->CloseChildWindows();
1137
1138 this->SetDirty();
1139
1140 Window::closed_windows.push_back(this);
1141}
1142
1147{
1148 /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1149 assert(*this->z_position == nullptr);
1150}
1151
1158Window *FindWindowById(WindowClass cls, WindowNumber number)
1159{
1160 for (Window *w : Window::Iterate()) {
1161 if (w->window_class == cls && w->window_number == number) return w;
1162 }
1163
1164 return nullptr;
1165}
1166
1173Window *FindWindowByClass(WindowClass cls)
1174{
1175 for (Window *w : Window::Iterate()) {
1176 if (w->window_class == cls) return w;
1177 }
1178
1179 return nullptr;
1180}
1181
1188{
1189 Window *w = FindWindowById(WindowClass::MainWindow, 0);
1190 assert(w != nullptr);
1191 return w;
1192}
1193
1201void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1202{
1203 Window *w = FindWindowById(cls, number);
1204 if (w != nullptr && (force || !w->flags.Test(WindowFlag::Sticky))) {
1205 w->Close(data);
1206 }
1207}
1208
1214void CloseWindowByClass(WindowClass cls, int data)
1215{
1216 /* Note: the container remains stable, even when deleting windows. */
1217 for (Window *w : Window::Iterate()) {
1218 if (w->window_class == cls) {
1219 w->Close(data);
1220 }
1221 }
1222}
1223
1230void CloseCompanyWindows(CompanyID id)
1231{
1232 /* Note: the container remains stable, even when deleting windows. */
1233 for (Window *w : Window::Iterate()) {
1234 if (w->owner == id) {
1235 w->Close();
1236 }
1237 }
1238
1239 /* Also delete the company specific windows that don't have a company-colour. */
1240 CloseWindowById(WindowClass::BuyCompany, id);
1241}
1242
1250void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1251{
1252 for (Window *w : Window::Iterate()) {
1253 if (w->owner != old_owner) continue;
1254
1255 switch (w->window_class) {
1256 case WindowClass::CompanyLivery:
1257 case WindowClass::Finances:
1258 case WindowClass::StationList:
1259 case WindowClass::TrainList:
1260 case WindowClass::RoadVehicleList:
1261 case WindowClass::ShipList:
1262 case WindowClass::AircraftList:
1263 case WindowClass::BuyCompany:
1264 case WindowClass::Company:
1265 case WindowClass::CompanyInfrastructure:
1266 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().
1267 continue;
1268
1269 default:
1270 w->owner = new_owner;
1271 break;
1272 }
1273 }
1274}
1275
1276static void BringWindowToFront(Window *w, bool dirty = true);
1277
1286{
1287 Window *w = FindWindowById(cls, number);
1288
1289 if (w != nullptr) {
1290 if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1291
1292 w->SetWhiteBorder();
1294 w->SetDirty();
1295 }
1296
1297 return w;
1298}
1299
1300static inline bool IsVitalWindow(const Window *w)
1301{
1302 switch (w->window_class) {
1303 case WindowClass::MainToolbar:
1304 case WindowClass::Statusbar:
1305 case WindowClass::News:
1306 case WindowClass::NetworkChat:
1307 return true;
1308
1309 default:
1310 return false;
1311 }
1312}
1313
1322static uint GetWindowZPriority(WindowClass wc)
1323{
1324 assert(wc != WindowClass::Invalid);
1325
1326 uint z_priority = 0;
1327
1328 switch (wc) {
1329 case WindowClass::ToolTips:
1330 ++z_priority;
1331 [[fallthrough]];
1332
1333 case WindowClass::ErrorMessage:
1334 case WindowClass::ConfirmPopupQuery:
1335 ++z_priority;
1336 [[fallthrough]];
1337
1338 case WindowClass::Endscreen:
1339 ++z_priority;
1340 [[fallthrough]];
1341
1342 case WindowClass::Highscore:
1343 ++z_priority;
1344 [[fallthrough]];
1345
1346 case WindowClass::DropdownMenu:
1347 ++z_priority;
1348 [[fallthrough]];
1349
1350 case WindowClass::MainToolbar:
1351 case WindowClass::Statusbar:
1352 ++z_priority;
1353 [[fallthrough]];
1354
1355 case WindowClass::OnScreenKeyboard:
1356 ++z_priority;
1357 [[fallthrough]];
1358
1359 case WindowClass::QueryString:
1360 case WindowClass::NetworkChat:
1361 ++z_priority;
1362 [[fallthrough]];
1363
1364 case WindowClass::NetworkAskRelay:
1365 case WindowClass::ModalProgress:
1366 case WindowClass::NetworkStatus:
1367 case WindowClass::SavePreset:
1368 ++z_priority;
1369 [[fallthrough]];
1370
1371 case WindowClass::GenerateLandscape:
1372 case WindowClass::SaveLoad:
1373 case WindowClass::GameOptions:
1374 case WindowClass::CustomCurrenty:
1375 case WindowClass::Network:
1376 case WindowClass::NewGRFParameters:
1377 case WindowClass::ScriptList:
1378 case WindowClass::ScriptSettings:
1379 case WindowClass::Textfile:
1380 ++z_priority;
1381 [[fallthrough]];
1382
1383 case WindowClass::Console:
1384 ++z_priority;
1385 [[fallthrough]];
1386
1387 case WindowClass::News:
1388 ++z_priority;
1389 [[fallthrough]];
1390
1391 default:
1392 ++z_priority;
1393 [[fallthrough]];
1394
1395 case WindowClass::MainWindow:
1396 return z_priority;
1397 }
1398}
1399
1406static void BringWindowToFront(Window *w, bool dirty)
1407{
1408 auto priority = GetWindowZPriority(w->window_class);
1409 WindowList::iterator dest = _z_windows.begin();
1410 while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1411
1412 if (dest != w->z_position) {
1413 _z_windows.splice(dest, _z_windows, w->z_position);
1414 }
1415
1416 if (dirty) w->SetDirty();
1417}
1418
1426{
1427 /* Set up window properties; some of them are needed to set up smallest size below */
1428 this->window_class = this->window_desc.cls;
1429 this->SetWhiteBorder();
1430 if (this->window_desc.default_pos == WindowPosition::Center) this->flags.Set(WindowFlag::Centred);
1431 this->owner = INVALID_OWNER;
1432 this->nested_focus = nullptr;
1433 this->window_number = window_number;
1434
1435 this->OnInit();
1436 /* Initialize smallest size. */
1437 this->nested_root->SetupSmallestSize(this);
1438 /* Initialize to smallest size. */
1439 this->nested_root->AssignSizePosition(SizingType::Smallest, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1440
1441 /* Further set up window properties,
1442 * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1443 this->resize.step_width = this->nested_root->resize_x;
1444 this->resize.step_height = this->nested_root->resize_y;
1445
1446 /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1447 * (so we don't interrupt typing) unless the new window has a text box. */
1448 bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WindowClass::DropdownMenu;
1449 bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1450 if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1451
1452 /* Insert the window into the correct location in the z-ordering. */
1453 BringWindowToFront(this, false);
1454}
1455
1463void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1464{
1465 this->left = x;
1466 this->top = y;
1467 this->width = sm_width;
1468 this->height = sm_height;
1469}
1470
1482void Window::FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
1483{
1484 if (allow_resize) {
1485 def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1486 def_height = std::max(def_height, this->height);
1487 /* Try to make windows smaller when our window is too small.
1488 * w->(width|height) is normally the same as min_(width|height),
1489 * but this way the GUIs can be made a little more dynamic;
1490 * one can use the same spec for multiple windows and those
1491 * can then determine the real minimum size of the window. */
1492 if (this->width != def_width || this->height != def_height) {
1493 /* Think about the overlapping toolbars when determining the minimum window size */
1494 int free_height = _screen.height;
1495 const Window *wt = FindWindowById(WindowClass::Statusbar, 0);
1496 if (wt != nullptr) free_height -= wt->height;
1497 wt = FindWindowById(WindowClass::MainToolbar, 0);
1498 if (wt != nullptr) free_height -= wt->height;
1499
1500 int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1501 int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1502
1503 /* X and Y has to go by step.. calculate it.
1504 * The cast to int is necessary else x/y are implicitly cast to
1505 * unsigned int, which won't work. */
1506 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1507 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1508
1509 ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1510 /* ResizeWindow() calls this->OnResize(). */
1511 } else {
1512 /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1513 this->OnResize();
1514 }
1515 }
1516
1517 int nx = this->left;
1518 int ny = this->top;
1519
1520 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1521
1522 const Window *wt = FindWindowById(WindowClass::MainToolbar, 0);
1523 ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1524 nx = std::max(nx, 0);
1525
1526 if (this->viewport != nullptr) {
1527 this->viewport->left += nx - this->left;
1528 this->viewport->top += ny - this->top;
1529 }
1530 this->left = nx;
1531 this->top = ny;
1532
1533 this->SetDirty();
1534}
1535
1548static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1549{
1550 int right = width + left;
1551 int bottom = height + top;
1552
1553 if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1554
1555 /* Make sure it is not obscured by any window. */
1556 for (const Window *w : Window::Iterate()) {
1557 if (w->window_class == WindowClass::MainWindow) continue;
1558
1559 if (right > w->left &&
1560 w->left + w->width > left &&
1561 bottom > w->top &&
1562 w->top + w->height > top) {
1563 return false;
1564 }
1565 }
1566
1567 pos.x = left;
1568 pos.y = top;
1569 return true;
1570}
1571
1584static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1585{
1586 bool rtl = _current_text_dir == TD_RTL;
1587
1588 /* Left part of the rectangle may be at most 1/4 off-screen,
1589 * right part of the rectangle may be at most 1/2 off-screen
1590 */
1591 if (rtl) {
1592 if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1593 } else {
1594 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1595 }
1596
1597 /* Bottom part of the rectangle may be at most 1/4 off-screen */
1598 if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1599
1600 /* Make sure it is not obscured by any window. */
1601 for (const Window *w : Window::Iterate()) {
1602 if (w->window_class == WindowClass::MainWindow) continue;
1603
1604 if (left + width > w->left &&
1605 w->left + w->width > left &&
1606 top + height > w->top &&
1607 w->top + w->height > top) {
1608 return false;
1609 }
1610 }
1611
1612 pos.x = left;
1613 pos.y = top;
1614 return true;
1615}
1616
1623static Point GetAutoPlacePosition(int width, int height)
1624{
1625 Point pt;
1626
1627 bool rtl = _current_text_dir == TD_RTL;
1628
1629 /* First attempt, try top-left of the screen */
1630 const Window *main_toolbar = FindWindowByClass(WindowClass::MainToolbar);
1631 const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1632 if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1633
1634 /* Second attempt, try around all existing windows.
1635 * The new window must be entirely on-screen, and not overlap with an existing window.
1636 * Eight starting points are tried, two at each corner.
1637 */
1638 for (const Window *w : Window::Iterate()) {
1639 if (w->window_class == WindowClass::MainWindow) continue;
1640
1641 if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1642 if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1643 if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1644 if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1645 if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1646 if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1647 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1648 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1649 }
1650
1651 /* Third attempt, try around all existing windows.
1652 * The new window may be partly off-screen, and must not overlap with an existing window.
1653 * Only four starting points are tried.
1654 */
1655 for (const Window *w : Window::Iterate()) {
1656 if (w->window_class == WindowClass::MainWindow) continue;
1657
1658 if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1659 if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1660 if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1661 if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1662 }
1663
1664 /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1665 * of the closebox
1666 */
1667 int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1668 int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1669 int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.captiontext.Vertical());
1670
1671restart:
1672 for (const Window *w : Window::Iterate()) {
1673 if (w->left == left && w->top == top) {
1674 left += offset_x;
1675 top += offset_y;
1676 goto restart;
1677 }
1678 }
1679
1680 pt.x = left;
1681 pt.y = top;
1682 return pt;
1683}
1684
1692{
1693 const Window *w = FindWindowById(WindowClass::MainToolbar, 0);
1694 assert(w != nullptr);
1695 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1696 return pt;
1697}
1698
1708{
1709 Point pt = GetToolbarAlignedWindowPosition(window_width);
1710 const Window *w = FindWindowByClass(WindowClass::ScenarioGenerateLandscape);
1711 if (w != nullptr && w->top == pt.y && !_settings_client.gui.link_terraform_toolbar) {
1712 pt.x = w->left + (_current_text_dir == TD_RTL ? w->width : - window_width);
1713 }
1714 return pt;
1715}
1716
1734static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1735{
1736 Point pt;
1737 const Window *w;
1738
1739 int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1740 int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1741
1742 if (desc.parent_cls != WindowClass::None && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1743 bool rtl = _current_text_dir == TD_RTL;
1744 if (desc.parent_cls == WindowClass::BuildToolbar || desc.parent_cls == WindowClass::ScenarioGenerateLandscape) {
1745 pt.x = w->left + (rtl ? w->width - default_width : 0);
1746 pt.y = w->top + w->height;
1747 return pt;
1748 } else {
1749 /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1750 * - Y position: closebox of parent + closebox of child + statusbar
1751 * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1752 */
1753 int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.captiontext.Vertical());
1754 if (w->top + 3 * indent_y < _screen.height) {
1755 pt.y = w->top + indent_y;
1756 int indent_close = NWidgetLeaf::closebox_dimension.width;
1757 int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1758 if (_current_text_dir == TD_RTL) {
1759 pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1760 if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1761 } else {
1762 pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1763 if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1764 }
1765 }
1766 }
1767 }
1768
1769 switch (desc.default_pos) {
1770 case WindowPosition::AlignToolbar: // Align to the toolbar
1771 return GetToolbarAlignedWindowPosition(default_width);
1772
1773 case WindowPosition::Automatic: // Find a good automatic position for the window
1774 return GetAutoPlacePosition(default_width, default_height);
1775
1776 case WindowPosition::Center: // Centre the window horizontally
1777 pt.x = (_screen.width - default_width) / 2;
1778 pt.y = (_screen.height - default_height) / 2;
1779 break;
1780
1782 pt.x = 0;
1783 pt.y = 0;
1784 break;
1785
1786 default:
1787 NOT_REACHED();
1788 }
1789
1790 return pt;
1791}
1792
1793/* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1794{
1795 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1796}
1797
1805{
1806 this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1807 this->nested_root->FillWidgetLookup(this->widget_lookup);
1808}
1809
1815{
1816 this->nested_root->AdjustPaddingForZoom();
1817 this->InitializeData(window_number);
1818 this->ApplyDefaults();
1819 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1820 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1821 this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight(), true);
1822}
1823
1829{
1830 this->CreateNestedTree();
1831 this->FinishInitNested(window_number);
1832}
1833
1839{
1840 this->z_position = _z_windows.insert(_z_windows.end(), this);
1841}
1842
1851{
1852 for (Window *w : Window::IterateFromFront()) {
1853 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1854 return w;
1855 }
1856 }
1857
1858 return nullptr;
1859}
1860
1865{
1866 IConsoleClose();
1867
1868 _focused_window = nullptr;
1869 _mouseover_last_w = nullptr;
1870 _last_scroll_window = nullptr;
1871 _scrolling_viewport = false;
1872 _mouse_hovering = false;
1873
1875 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1876 NWidgetScrollbar::InvalidateDimensionCache();
1877
1879
1881}
1882
1887{
1889
1890 for (Window *w : Window::Iterate()) w->Close();
1891
1893
1894 assert(_z_windows.empty());
1895}
1896
1901{
1904 _thd.Reset();
1905}
1906
1907static void DecreaseWindowCounters()
1908{
1909 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1910
1911 for (Window *w : Window::Iterate()) {
1912 if (_scroller_click_timeout == 0) {
1913 /* Unclick scrollbar buttons if they are pressed. */
1914 for (auto &pair : w->widget_lookup) {
1915 NWidgetBase *nwid = pair.second;
1916 if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1917 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1918 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
1921 sb->SetDirty(w);
1922 }
1923 }
1924 }
1925 }
1926
1927 /* Handle editboxes */
1928 for (auto &pair : w->querystrings) {
1929 pair.second->HandleEditBox(w, pair.first);
1930 }
1931
1932 w->OnMouseLoop();
1933 }
1934
1935 for (Window *w : Window::Iterate()) {
1936 if (w->flags.Test(WindowFlag::Timeout) && --w->timeout_timer == 0) {
1938
1939 w->OnTimeout();
1940 w->RaiseButtons(true);
1941 }
1942 }
1943}
1944
1945static void HandlePlacePresize()
1946{
1948
1949 Window *w = _thd.GetCallbackWnd();
1950 if (w == nullptr) return;
1951
1952 Point pt = GetTileBelowCursor();
1953 if (pt.x == -1) {
1954 _thd.selend.x = -1;
1955 return;
1956 }
1957
1958 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1959}
1960
1966{
1968
1969 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return EventState::Handled; // Dragging, but the mouse did not move.
1970
1971 Window *w = _thd.GetCallbackWnd();
1972 if (w != nullptr) {
1973 /* Send an event in client coordinates. */
1974 Point pt;
1975 pt.x = _cursor.pos.x - w->left;
1976 pt.y = _cursor.pos.y - w->top;
1977 if (_left_button_down) {
1978 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1979 } else {
1980 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1981 }
1982 }
1983
1984 if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1985 return EventState::Handled;
1986}
1987
1989static void HandleMouseOver()
1990{
1991 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1992
1993 /* We changed window, put an OnMouseOver event to the last window */
1994 if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1995 /* Reset mouse-over coordinates of previous window */
1996 Point pt = { -1, -1 };
1997 _mouseover_last_w->OnMouseOver(pt, 0);
1998 }
1999
2000 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
2002
2003 if (w != nullptr) {
2004 /* send an event in client coordinates. */
2005 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2006 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
2007 if (widget != nullptr) w->OnMouseOver(pt, widget->GetIndex());
2008 }
2009}
2010
2012enum class PreventHideDirection : uint8_t {
2015};
2016
2027static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
2028{
2029 if (v == nullptr) return;
2030
2031 const int min_visible = rect.Height();
2032
2033 int v_bottom = v->top + v->height - 1;
2034 int v_right = v->left + v->width - 1;
2035 int safe_y = (dir == PreventHideDirection::Up) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
2036
2037 if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
2038 if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
2039
2040 /* Vertically, the rectangle is hidden behind v. */
2041 if (*nx + rect.left + min_visible < v->left) { // At left of v.
2042 if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
2043 return;
2044 }
2045 if (*nx + rect.right - min_visible > v_right) { // At right of v.
2046 if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
2047 return;
2048 }
2049
2050 /* Horizontally also hidden, force movement to a safe area. */
2051 if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
2052 *nx = v->left - min_visible - rect.left;
2053 } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
2054 *nx = v_right + min_visible - rect.right;
2055 } else {
2056 *ny = safe_y;
2057 }
2058}
2059
2067static void EnsureVisibleCaption(Window *w, int nx, int ny)
2068{
2069 /* Search for the title bar rectangle. */
2070 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
2071 if (caption != nullptr) {
2072 const Rect caption_rect = caption->GetCurrentRect();
2073
2074 const int min_visible = caption_rect.Height();
2075
2076 /* Make sure the window doesn't leave the screen */
2077 nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
2078 ny = Clamp(ny, 0, _screen.height - min_visible);
2079
2080 /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
2081 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WindowClass::MainToolbar, 0), w->left, PreventHideDirection::Down);
2082 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WindowClass::Statusbar, 0), w->left, PreventHideDirection::Up);
2083 }
2084
2085 if (w->viewport != nullptr) {
2086 w->viewport->left += nx - w->left;
2087 w->viewport->top += ny - w->top;
2088 }
2089
2090 w->left = nx;
2091 w->top = ny;
2092}
2093
2105void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2106{
2107 if (delta_x != 0 || delta_y != 0) {
2108 if (clamp_to_screen) {
2109 /* Determine the new right/bottom position. If that is outside of the bounds of
2110 * the resolution clamp it in such a manner that it stays within the bounds. */
2111 int new_right = w->left + w->width + delta_x;
2112 int new_bottom = w->top + w->height + delta_y;
2113 if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2114 if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2115 }
2116
2117 w->SetDirty();
2118
2119 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);
2120 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);
2121 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2122 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2123
2124 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);
2125 w->width = w->nested_root->current_x;
2126 w->height = w->nested_root->current_y;
2127 }
2128
2129 EnsureVisibleCaption(w, w->left, w->top);
2130
2131 /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2132 if (schedule_resize) {
2133 w->ScheduleResize();
2134 } else {
2135 w->OnResize();
2136 }
2137 w->SetDirty();
2138}
2139
2146{
2147 Window *w = FindWindowById(WindowClass::MainToolbar, 0);
2148 return (w == nullptr) ? 0 : w->top + w->height;
2149}
2150
2157{
2158 Window *w = FindWindowById(WindowClass::Statusbar, 0);
2159 return (w == nullptr) ? _screen.height : w->top;
2160}
2161
2162static bool _dragging_window;
2163
2169{
2170 /* Get out immediately if no window is being dragged at all. */
2172
2173 /* If button still down, but cursor hasn't moved, there is nothing to do. */
2174 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return EventState::Handled;
2175
2176 /* Otherwise find the window... */
2177 for (Window *w : Window::Iterate()) {
2179 /* Stop the dragging if the left mouse button was released */
2180 if (!_left_button_down) {
2182 break;
2183 }
2184
2185 w->SetDirty();
2186
2187 int x = _cursor.pos.x + _drag_delta.x;
2188 int y = _cursor.pos.y + _drag_delta.y;
2189 int nx = x;
2190 int ny = y;
2191
2192 if (_settings_client.gui.window_snap_radius != 0) {
2193 int hsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2194 int vsnap = ScaleGUITrad(_settings_client.gui.window_snap_radius);
2195 int delta;
2196
2197 for (const Window *v : Window::Iterate()) {
2198 if (v == w) continue; // Don't snap at yourself
2199
2200 if (y + w->height > v->top && y < v->top + v->height) {
2201 /* Your left border <-> other right border */
2202 delta = abs(v->left + v->width - x);
2203 if (delta <= hsnap) {
2204 nx = v->left + v->width;
2205 hsnap = delta;
2206 }
2207
2208 /* Your right border <-> other left border */
2209 delta = abs(v->left - x - w->width);
2210 if (delta <= hsnap) {
2211 nx = v->left - w->width;
2212 hsnap = delta;
2213 }
2214 }
2215
2216 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2217 /* Your left border <-> other left border */
2218 delta = abs(v->left - x);
2219 if (delta <= hsnap) {
2220 nx = v->left;
2221 hsnap = delta;
2222 }
2223
2224 /* Your right border <-> other right border */
2225 delta = abs(v->left + v->width - x - w->width);
2226 if (delta <= hsnap) {
2227 nx = v->left + v->width - w->width;
2228 hsnap = delta;
2229 }
2230 }
2231
2232 if (x + w->width > v->left && x < v->left + v->width) {
2233 /* Your top border <-> other bottom border */
2234 delta = abs(v->top + v->height - y);
2235 if (delta <= vsnap) {
2236 ny = v->top + v->height;
2237 vsnap = delta;
2238 }
2239
2240 /* Your bottom border <-> other top border */
2241 delta = abs(v->top - y - w->height);
2242 if (delta <= vsnap) {
2243 ny = v->top - w->height;
2244 vsnap = delta;
2245 }
2246 }
2247
2248 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2249 /* Your top border <-> other top border */
2250 delta = abs(v->top - y);
2251 if (delta <= vsnap) {
2252 ny = v->top;
2253 vsnap = delta;
2254 }
2255
2256 /* Your bottom border <-> other bottom border */
2257 delta = abs(v->top + v->height - y - w->height);
2258 if (delta <= vsnap) {
2259 ny = v->top + v->height - w->height;
2260 vsnap = delta;
2261 }
2262 }
2263 }
2264 }
2265
2266 EnsureVisibleCaption(w, nx, ny);
2267
2268 w->SetDirty();
2269 return EventState::Handled;
2271 /* Stop the sizing if the left mouse button was released */
2272 if (!_left_button_down) {
2275 w->SetDirty();
2276 break;
2277 }
2278
2279 /* Compute difference in pixels between cursor position and reference point in the window.
2280 * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2281 */
2282 int x, y = _cursor.pos.y - _drag_delta.y;
2284 x = _drag_delta.x - _cursor.pos.x;
2285 } else {
2286 x = _cursor.pos.x - _drag_delta.x;
2287 }
2288
2289 /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2290 if (w->resize.step_width == 0) x = 0;
2291 if (w->resize.step_height == 0) y = 0;
2292
2293 /* Check the resize button won't go past the bottom of the screen */
2294 if (w->top + w->height + y > _screen.height) {
2295 y = _screen.height - w->height - w->top;
2296 }
2297
2298 /* X and Y has to go by step.. calculate it.
2299 * The cast to int is necessary else x/y are implicitly cast to
2300 * unsigned int, which won't work. */
2301 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2302 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2303
2304 /* Check that we don't go below the minimum set size */
2305 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2306 x = w->nested_root->smallest_x - w->width;
2307 }
2308 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2309 y = w->nested_root->smallest_y - w->height;
2310 }
2311
2312 /* Window already on size */
2313 if (x == 0 && y == 0) return EventState::Handled;
2314
2315 /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2316 _drag_delta.y += y;
2317 if (w->flags.Test(WindowFlag::SizingLeft) && x != 0) {
2318 _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2319 w->SetDirty();
2320 w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2321 /* ResizeWindow() below ensures marking new position as dirty. */
2322 } else {
2323 _drag_delta.x += x;
2324 }
2325
2326 /* ResizeWindow sets both pre- and after-size to dirty for redrawing */
2327 ResizeWindow(w, x, y);
2328 return EventState::Handled;
2329 }
2330 }
2331
2332 _dragging_window = false;
2333 return EventState::Handled;
2334}
2335
2341{
2344 _dragging_window = true;
2345
2346 _drag_delta.x = w->left - _cursor.pos.x;
2347 _drag_delta.y = w->top - _cursor.pos.y;
2348
2350}
2351
2357static void StartWindowSizing(Window *w, bool to_left)
2358{
2361 _dragging_window = true;
2362
2363 _drag_delta.x = _cursor.pos.x;
2364 _drag_delta.y = _cursor.pos.y;
2365
2367}
2368
2374{
2375 int i;
2377 bool rtl = false;
2378
2379 if (sb->type == NWID_HSCROLLBAR) {
2380 i = _cursor.pos.x - _cursorpos_drag_start.x;
2381 rtl = _current_text_dir == TD_RTL;
2382 } else {
2383 i = _cursor.pos.y - _cursorpos_drag_start.y;
2384 }
2385
2386 if (sb->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown})) {
2387 if (_scroller_click_timeout == 1) {
2388 _scroller_click_timeout = 3;
2389 if (sb->UpdatePosition(rtl == sb->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp) ? 1 : -1)) {
2391 w->SetDirty();
2392 }
2393 }
2394 return;
2395 }
2396
2397 /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2398 int range = sb->GetCount() - sb->GetCapacity();
2399 if (range <= 0) return;
2400
2401 int pos = RoundDivSU((i + _scrollbar_start_pos) * range, std::max(1, _scrollbar_size));
2402 if (rtl) pos = range - pos;
2403 if (sb->SetPosition(pos)) {
2405 w->SetDirty();
2406 }
2407}
2408
2414{
2415 for (Window *w : Window::Iterate()) {
2416 if (w->mouse_capture_widget >= 0) {
2417 /* Abort if no button is clicked any more. */
2418 if (!_left_button_down) {
2421 return EventState::Handled;
2422 }
2423
2424 /* Handle scrollbar internally, or dispatch click event */
2426 if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2428 } else {
2429 /* If cursor hasn't moved, there is nothing to do. */
2430 if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return EventState::Handled;
2431
2432 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2433 w->OnClick(pt, w->mouse_capture_widget, 0);
2434 }
2435 return EventState::Handled;
2436 }
2437 }
2438
2440}
2441
2447{
2448 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2449
2451
2452 /* When we don't have a last scroll window we are starting to scroll.
2453 * When the last scroll window and this are not the same we went
2454 * outside of the window and should not left-mouse scroll anymore. */
2455 if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2456
2457 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))) {
2458 _cursor.fix_at = false;
2459 _scrolling_viewport = false;
2460 _last_scroll_window = nullptr;
2462 }
2463
2464 if (_last_scroll_window == GetMainWindow() && _last_scroll_window->viewport->follow_vehicle != VehicleID::Invalid()) {
2465 /* If the main window is following a vehicle, then first let go of it! */
2466 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle)->GetMovingFront();
2467 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2469 }
2470
2471 Point delta;
2472 if (scrollwheel_scrolling) {
2473 /* We are using scrollwheels for scrolling */
2474 /* Use the integer part for movement */
2475 delta.x = static_cast<int>(_cursor.h_wheel);
2476 delta.y = static_cast<int>(_cursor.v_wheel);
2477 /* Keep the fractional part so that subtle movement is accumulated */
2478 float temp;
2479 _cursor.v_wheel = std::modf(_cursor.v_wheel, &temp);
2480 _cursor.h_wheel = std::modf(_cursor.h_wheel, &temp);
2481 } else {
2483 delta.x = -_cursor.delta.x;
2484 delta.y = -_cursor.delta.y;
2485 } else {
2486 delta.x = _cursor.delta.x;
2487 delta.y = _cursor.delta.y;
2488 }
2489 }
2490
2491 /* Create a scroll-event and send it to the window */
2492 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2493
2494 _cursor.delta.x = 0;
2495 _cursor.delta.y = 0;
2496 _cursor.wheel_moved = false;
2497 return EventState::Handled;
2498}
2499
2511{
2512 bool bring_to_front = false;
2513
2514 if (w->window_class == WindowClass::MainWindow ||
2515 IsVitalWindow(w) ||
2516 w->window_class == WindowClass::ToolTips ||
2517 w->window_class == WindowClass::DropdownMenu) {
2518 return true;
2519 }
2520
2521 /* Use unshaded window size rather than current size for shaded windows. */
2522 int w_width = w->width;
2523 int w_height = w->height;
2524 if (w->IsShaded()) {
2525 w_width = w->unshaded_size.width;
2526 w_height = w->unshaded_size.height;
2527 }
2528
2530 ++it;
2531 for (; !it.IsEnd(); ++it) {
2532 Window *u = *it;
2533 /* A modal child will prevent the activation of the parent window */
2535 u->SetWhiteBorder();
2536 u->SetDirty();
2537 return false;
2538 }
2539
2540 if (u->window_class == WindowClass::MainWindow ||
2541 IsVitalWindow(u) ||
2542 u->window_class == WindowClass::ToolTips ||
2543 u->window_class == WindowClass::DropdownMenu) {
2544 continue;
2545 }
2546
2547 /* Window sizes don't interfere, leave z-order alone */
2548 if (w->left + w_width <= u->left ||
2549 u->left + u->width <= w->left ||
2550 w->top + w_height <= u->top ||
2551 u->top + u->height <= w->top) {
2552 continue;
2553 }
2554
2555 bring_to_front = true;
2556 }
2557
2558 if (bring_to_front) BringWindowToFront(w);
2559 return true;
2560}
2561
2570EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2571{
2572 QueryString *query = this->GetQueryString(wid);
2573 if (query == nullptr) return EventState::NotHandled;
2574
2575 int action = QueryString::ACTION_NOTHING;
2576
2577 switch (query->text.HandleKeyPress(key, keycode)) {
2578 case HKPR_EDITING:
2579 this->SetWidgetDirty(wid);
2580 this->OnEditboxChanged(wid);
2581 break;
2582
2583 case HKPR_CURSOR:
2584 this->SetWidgetDirty(wid);
2585 /* For the OSK also invalidate the parent window */
2586 if (this->window_class == WindowClass::OnScreenKeyboard) this->InvalidateData();
2587 break;
2588
2589 case HKPR_CONFIRM:
2590 if (this->window_class == WindowClass::OnScreenKeyboard) {
2591 this->OnClick(Point(), WID_OSK_OK, 1);
2592 } else if (query->ok_button >= 0) {
2593 this->OnClick(Point(), query->ok_button, 1);
2594 } else {
2595 action = query->ok_button;
2596 }
2597 break;
2598
2599 case HKPR_CANCEL:
2600 if (this->window_class == WindowClass::OnScreenKeyboard) {
2601 this->OnClick(Point(), WID_OSK_CANCEL, 1);
2602 } else if (query->cancel_button >= 0) {
2603 this->OnClick(Point(), query->cancel_button, 1);
2604 } else {
2605 action = query->cancel_button;
2606 }
2607 break;
2608
2609 case HKPR_NOT_HANDLED:
2611
2612 default: break;
2613 }
2614
2615 switch (action) {
2617 this->UnfocusFocusedWidget();
2618 break;
2619
2621 if (query->text.GetText().empty()) {
2622 /* If already empty, unfocus instead */
2623 this->UnfocusFocusedWidget();
2624 } else {
2625 query->text.DeleteAll();
2626 this->SetWidgetDirty(wid);
2627 this->OnEditboxChanged(wid);
2628 }
2629 break;
2630
2631 default:
2632 break;
2633 }
2634
2635 return EventState::Handled;
2636}
2637
2642void HandleToolbarHotkey(int hotkey)
2643{
2644 assert(HasModalProgress() || IsLocalCompany());
2645
2646 Window *w = FindWindowById(WindowClass::MainToolbar, 0);
2647 if (w != nullptr) {
2648 if (w->window_desc.hotkeys != nullptr) {
2649 if (hotkey >= 0 && w->OnHotkey(hotkey) == EventState::Handled) return;
2650 }
2651 }
2652}
2653
2659void HandleKeypress(uint keycode, char32_t key)
2660{
2661 /* World generation is multithreaded and messes with companies.
2662 * But there is no company related window open anyway, so _current_company is not used. */
2663 assert(HasModalProgress() || IsLocalCompany());
2664
2665 /*
2666 * The Unicode standard defines an area called the private use area. Code points in this
2667 * area are reserved for private use and thus not portable between systems. For instance,
2668 * Apple defines code points for the arrow keys in this area, but these are only printable
2669 * on a system running OS X. We don't want these keys to show up in text fields and such,
2670 * and thus we have to clear the unicode character when we encounter such a key.
2671 */
2672 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2673
2674 /*
2675 * If both key and keycode is zero, we don't bother to process the event.
2676 */
2677 if (key == 0 && keycode == 0) return;
2678
2679 /* Check if the focused window has a focused editbox */
2680 if (EditBoxInGlobalFocus()) {
2681 /* All input will in this case go to the focused editbox */
2682 if (_focused_window->window_class == WindowClass::Console) {
2683 if (_focused_window->OnKeyPress(key, keycode) == EventState::Handled) return;
2684 } else {
2685 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->GetIndex(), key, keycode) == EventState::Handled) return;
2686 }
2687 }
2688
2689 /* Call the event, start with the uppermost window, but ignore the toolbar. */
2690 for (Window *w : Window::IterateFromFront()) {
2691 if (w->window_class == WindowClass::MainToolbar) continue;
2692 if (w->window_desc.hotkeys != nullptr) {
2693 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2694 if (hotkey >= 0 && w->OnHotkey(hotkey) == EventState::Handled) return;
2695 }
2696 if (w->OnKeyPress(key, keycode) == EventState::Handled) return;
2697 }
2698
2699 Window *w = FindWindowById(WindowClass::MainToolbar, 0);
2700 /* When there is no toolbar w is null, check for that */
2701 if (w != nullptr) {
2702 if (w->window_desc.hotkeys != nullptr) {
2703 int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2704 if (hotkey >= 0 && w->OnHotkey(hotkey) == EventState::Handled) return;
2705 }
2706 if (w->OnKeyPress(key, keycode) == EventState::Handled) return;
2707 }
2708
2709 HandleGlobalHotkeys(key, keycode);
2710}
2711
2716{
2717 /* Call the event, start with the uppermost window. */
2718 for (Window *w : Window::IterateFromFront()) {
2719 if (w->OnCTRLStateChange() == EventState::Handled) return;
2720 }
2721}
2722
2732/* 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)
2733{
2734 QueryString *query = this->GetQueryString(wid);
2735 if (query == nullptr) return;
2736
2737 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2738 this->SetWidgetDirty(wid);
2739 this->OnEditboxChanged(wid);
2740 }
2741}
2742
2751void HandleTextInput(std::string_view str, bool marked, std::optional<size_t> caret, std::optional<size_t> insert_location, std::optional<size_t> replacement_end)
2752{
2753 if (!EditBoxInGlobalFocus()) return;
2754
2755 _focused_window->InsertTextString(_focused_window->window_class == WindowClass::Console ? 0 : _focused_window->nested_focus->GetIndex(), str, marked, caret, insert_location, replacement_end);
2756}
2757
2762static void HandleAutoscroll()
2763{
2764 if (_game_mode == GameMode::Menu || HasModalProgress()) return;
2765 if (_settings_client.gui.auto_scrolling == ViewportAutoscrolling::Disabled) return;
2766 if (_settings_client.gui.auto_scrolling == ViewportAutoscrolling::MainViewportFullscreen && !_fullscreen) return;
2767
2768 int x = _cursor.pos.x;
2769 int y = _cursor.pos.y;
2770 Window *w = FindWindowFromPt(x, y);
2771 if (w == nullptr || w->flags.Test(WindowFlag::DisableVpScroll)) return;
2772 if (_settings_client.gui.auto_scrolling != ViewportAutoscrolling::EveryViewport && w->window_class != WindowClass::MainWindow) return;
2773
2774 Viewport *vp = IsPtInWindowViewport(w, x, y);
2775 if (vp == nullptr) return;
2776
2777 x -= vp->left;
2778 y -= vp->top;
2779
2780 /* here allows scrolling in both x and y axis */
2781 /* If we succeed at scrolling in any direction, stop following a vehicle. */
2782 static const int SCROLLSPEED = 3;
2783 if (x - 15 < 0) {
2784 w->viewport->CancelFollow(*w);
2785 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2786 } else if (15 - (vp->width - x) > 0) {
2787 w->viewport->CancelFollow(*w);
2788 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2789 }
2790 if (y - 15 < 0) {
2791 w->viewport->CancelFollow(*w);
2792 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2793 } else if (15 - (vp->height - y) > 0) {
2794 w->viewport->CancelFollow(*w);
2795 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2796 }
2797}
2798
2807
2808static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2809static constexpr int MAX_OFFSET_HOVER = 5;
2810
2812
2813const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK{500};
2814
2815static void ScrollMainViewport(int x, int y)
2816{
2817 if (_game_mode != GameMode::Menu && _game_mode != GameMode::Bootstrap) {
2818 Window *w = GetMainWindow();
2819 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
2820 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
2821 }
2822}
2823
2833static const int8_t scrollamt[16][2] = {
2834 { 0, 0},
2835 {-2, 0},
2836 { 0, -2},
2837 {-2, -1},
2838 { 2, 0},
2839 { 0, 0},
2840 { 2, -1},
2841 { 0, -2},
2842 { 0, 2},
2843 {-2, 1},
2844 { 0, 0},
2845 {-2, 0},
2846 { 2, 1},
2847 { 0, 2},
2848 { 2, 0},
2849 { 0, 0},
2850};
2851
2852static void HandleKeyScrolling()
2853{
2854 /*
2855 * Check that any of the dirkeys is pressed and that the focused window
2856 * doesn't have an edit-box as focused widget.
2857 */
2858 if (_dirkeys.Any() && !EditBoxInGlobalFocus()) {
2859 int factor = _shift_pressed ? 50 : 10;
2860
2861 if (_game_mode != GameMode::Menu && _game_mode != GameMode::Bootstrap) {
2862 /* Key scrolling stops following a vehicle. */
2863 Window *main_window = GetMainWindow();
2864 main_window->viewport->CancelFollow(*main_window);
2865 }
2866
2867 ScrollMainViewport(scrollamt[_dirkeys.base()][0] * factor, scrollamt[_dirkeys.base()][1] * factor);
2868 }
2869}
2870
2871static void MouseLoop(MouseClick click, int mousewheel)
2872{
2873 /* World generation is multithreaded and messes with companies.
2874 * But there is no company related window open anyway, so _current_company is not used. */
2875 assert(HasModalProgress() || IsLocalCompany());
2876
2877 HandlePlacePresize();
2879
2883 if (HandleActiveWidget() == EventState::Handled) return;
2885
2887
2888 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == ScrollWheelScrolling::ScrollMap && _cursor.wheel_moved;
2889 if (click == MouseClick::None && mousewheel == 0 && !scrollwheel_scrolling) return;
2890
2891 int x = _cursor.pos.x;
2892 int y = _cursor.pos.y;
2893 Window *w = FindWindowFromPt(x, y);
2894 if (w == nullptr) return;
2895
2896 if (click != MouseClick::Hover && !MaybeBringWindowToFront(w)) return;
2897 Viewport *vp = IsPtInWindowViewport(w, x, y);
2898
2899 /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2900 if (vp != nullptr && (_game_mode == GameMode::Menu || HasModalProgress())) return;
2901
2902 if (mousewheel != 0) {
2903 /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2904 if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WindowClass::SmallMap)) {
2905 if (NWidgetCore *nwid = w->nested_root->GetWidgetFromPos(x - w->left, y - w->top); nwid != nullptr) {
2906 w->OnMouseWheel(mousewheel, nwid->GetIndex());
2907 }
2908 }
2909
2910 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2911 if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2912 }
2913
2914 if (vp != nullptr) {
2915 if (scrollwheel_scrolling && !w->flags.Test(WindowFlag::DisableVpScroll)) {
2916 _scrolling_viewport = true;
2917 _cursor.fix_at = true;
2918 return;
2919 }
2920
2921 switch (click) {
2923 case MouseClick::Left:
2924 if (HandleViewportClicked(*vp, x, y)) return;
2926 _settings_client.gui.scroll_mode == ViewportScrollMode::MapLMB) {
2927 _scrolling_viewport = true;
2928 _cursor.fix_at = false;
2929 return;
2930 }
2931 break;
2932
2933 case MouseClick::Right:
2935 _settings_client.gui.scroll_mode != ViewportScrollMode::MapLMB) {
2936 _scrolling_viewport = true;
2937 _cursor.fix_at = (_settings_client.gui.scroll_mode == ViewportScrollMode::ViewportRMBFixed ||
2939 DispatchRightClickEvent(w, x - w->left, y - w->top);
2940 return;
2941 }
2942 break;
2943
2944 default:
2945 break;
2946 }
2947 }
2948
2949 switch (click) {
2950 case MouseClick::Left:
2952 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MouseClick::DoubleLeft ? 2 : 1);
2953 return;
2954
2955 default:
2956 if (!scrollwheel_scrolling || w == nullptr || w->window_class != WindowClass::SmallMap) break;
2957 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2958 * Simulate a right button click so we can get started. */
2959 [[fallthrough]];
2960
2961 case MouseClick::Right:
2962 DispatchRightClickEvent(w, x - w->left, y - w->top);
2963 return;
2964
2965 case MouseClick::Hover:
2966 DispatchHoverEvent(w, x - w->left, y - w->top);
2967 break;
2968 }
2969
2970 /* We're not doing anything with 2D scrolling, so reset the value. */
2971 _cursor.h_wheel = 0.0f;
2972 _cursor.v_wheel = 0.0f;
2973 _cursor.wheel_moved = false;
2974}
2975
2980{
2981 /* World generation is multithreaded and messes with companies.
2982 * But there is no company related window open anyway, so _current_company is not used. */
2983 assert(HasModalProgress() || IsLocalCompany());
2984
2985 static std::chrono::steady_clock::time_point double_click_time = {};
2986 static Point double_click_pos = {0, 0};
2987
2988 /* Mouse event? */
2991 click = MouseClick::Left;
2992 if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2993 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2994 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2995 click = MouseClick::DoubleLeft;
2996 }
2997 double_click_time = std::chrono::steady_clock::now();
2998 double_click_pos = _cursor.pos;
2999 _left_button_clicked = true;
3000 } else if (_right_button_clicked) {
3001 _right_button_clicked = false;
3002 click = MouseClick::Right;
3003 }
3004
3005 int mousewheel = 0;
3006 if (_cursor.wheel) {
3007 mousewheel = _cursor.wheel;
3008 _cursor.wheel = 0;
3009 }
3010
3011 static std::chrono::steady_clock::time_point hover_time = {};
3012 static Point hover_pos = {0, 0};
3013
3014 if (_settings_client.gui.hover_delay_ms > 0) {
3015 if (!_cursor.in_window || click != MouseClick::None || mousewheel != 0 || _left_button_down || _right_button_down ||
3016 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
3017 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
3018 hover_pos = _cursor.pos;
3019 hover_time = std::chrono::steady_clock::now();
3020 _mouse_hovering = false;
3021 } else if (!_mouse_hovering) {
3022 if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
3023 click = MouseClick::Hover;
3024 _mouse_hovering = true;
3025 hover_time = std::chrono::steady_clock::now();
3026 }
3027 }
3028 }
3029
3030 if (click == MouseClick::Left && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
3031 /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
3033 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
3034 _newgrf_debug_sprite_picker.sprites.clear();
3035 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
3037 } else {
3038 MouseLoop(click, mousewheel);
3039 }
3040
3041 /* We have moved the mouse the required distance,
3042 * no need to move it at any later time. */
3043 _cursor.delta.x = 0;
3044 _cursor.delta.y = 0;
3045}
3046
3050static void CheckSoftLimit()
3051{
3052 if (_settings_client.gui.window_soft_limit == 0) return;
3053
3054 for (;;) {
3055 uint deletable_count = 0;
3056 Window *last_deletable = nullptr;
3057 for (Window *w : Window::IterateFromFront()) {
3058 if (w->window_class == WindowClass::MainWindow || IsVitalWindow(w) || w->flags.Test(WindowFlag::Sticky)) continue;
3059
3060 last_deletable = w;
3061 deletable_count++;
3062 }
3063
3064 /* We've not reached the soft limit yet. */
3065 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
3066
3067 assert(last_deletable != nullptr);
3068 last_deletable->Close();
3069 }
3070}
3071
3076{
3077 /* World generation is multithreaded and messes with companies.
3078 * But there is no company related window open anyway, so _current_company is not used. */
3079 assert(HasModalProgress() || IsLocalCompany());
3080
3082
3083 /* Process scheduled window deletion. */
3085
3086 /* HandleMouseEvents was already called for this tick */
3088}
3089
3090static std::chrono::time_point<std::chrono::steady_clock> _realtime_tick_start;
3091
3092bool CanContinueRealtimeTick()
3093{
3094 auto now = std::chrono::steady_clock::now();
3095 return std::chrono::duration_cast<std::chrono::milliseconds>(now - _realtime_tick_start).count() < (MILLISECONDS_PER_TICK * 3 / 4);
3096}
3097
3103{
3104 _realtime_tick_start = std::chrono::steady_clock::now();
3105 for (Window *w : Window::Iterate()) {
3106 w->OnRealtimeTick(delta_ms);
3107 }
3108}
3109
3111static const IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3112 extern int _caret_timer;
3113 _caret_timer += 3;
3114 CursorTick();
3115
3116 HandleKeyScrolling();
3118 DecreaseWindowCounters();
3119});
3120
3124});
3125
3127static const IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3128 if (_network_dedicated) return;
3129
3130 for (Window *w : Window::Iterate()) {
3133 w->SetDirty();
3134 }
3135 }
3136});
3137
3142{
3143 static auto last_time = std::chrono::steady_clock::now();
3144 auto now = std::chrono::steady_clock::now();
3145 auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3146
3147 if (delta_ms.count() == 0) return;
3148
3149 last_time = now;
3150
3153
3155
3157 CallWindowRealtimeTickEvent(delta_ms.count());
3158
3159 /* Process invalidations before anything else. */
3160 for (Window *w : Window::Iterate()) {
3164 }
3165
3166 /* Skip the actual drawing on dedicated servers without screen.
3167 * But still empty the invalidation queues above. */
3168 if (_network_dedicated) return;
3169
3171
3172 for (Window *w : Window::Iterate()) {
3173 /* Update viewport only if window is not shaded. */
3174 if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3175 }
3177 /* Redraw mouse cursor in case it was hidden */
3178 DrawMouseCursor();
3179
3180 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW) {
3181 /* We are done with the last draw-frame, so we know what sprites we
3182 * clicked on. Reset the picker mode and invalidate the window. */
3183 _newgrf_debug_sprite_picker.mode = SPM_NONE;
3184 InvalidateWindowData(WindowClass::SpriteAligner, 0, 1);
3185 }
3186}
3187
3193void SetWindowDirty(WindowClass cls, WindowNumber number)
3194{
3195 for (const Window *w : Window::Iterate()) {
3196 if (w->window_class == cls && w->window_number == number) {
3197 w->SetDirty();
3198 return;
3199 }
3200 }
3201}
3202
3209void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
3210{
3211 for (const Window *w : Window::Iterate()) {
3212 if (w->window_class == cls && w->window_number == number) {
3213 w->SetWidgetDirty(widget_index);
3214 return;
3215 }
3216 }
3217}
3218
3223void SetWindowClassesDirty(WindowClass cls)
3224{
3225 for (const Window *w : Window::Iterate()) {
3226 if (w->window_class == cls) w->SetDirty();
3227 }
3228}
3229
3234{
3235 this->scheduled_resize = true;
3236}
3237
3242{
3243 /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3244 while (this->scheduled_resize) {
3245 this->scheduled_resize = false;
3246 this->OnResize();
3247 }
3248}
3249
3255void Window::InvalidateData(int data, bool gui_scope)
3256{
3257 this->SetDirty();
3258 if (!gui_scope) {
3259 /* Schedule GUI-scope invalidation for next redraw. */
3260 this->scheduled_invalidation_data.push_back(data);
3261 }
3262 this->OnInvalidateData(data, gui_scope);
3263}
3264
3269{
3270 for (int data : this->scheduled_invalidation_data) {
3271 if (this->window_class == WindowClass::Invalid) break;
3272 this->OnInvalidateData(data, true);
3273 }
3274 this->scheduled_invalidation_data.clear();
3275}
3276
3281{
3282 if (!this->flags.Test(WindowFlag::Highlighted)) return;
3283
3284 for (const auto &pair : this->widget_lookup) {
3285 if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3286 }
3287}
3288
3315void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3316{
3317 for (Window *w : Window::Iterate()) {
3318 if (w->window_class == cls && w->window_number == number) {
3319 w->InvalidateData(data, gui_scope);
3320 return;
3321 }
3322 }
3323}
3324
3333void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3334{
3335 for (Window *w : Window::Iterate()) {
3336 if (w->window_class == cls) {
3337 w->InvalidateData(data, gui_scope);
3338 }
3339 }
3340}
3341
3346{
3347 for (Window *w : Window::Iterate()) {
3348 w->OnGameTick();
3349 }
3350}
3351
3359{
3360 /* Note: the container remains stable, even when deleting windows. */
3361 for (Window *w : Window::Iterate()) {
3363 !w->flags.Test(WindowFlag::Sticky)) { // do not delete windows which are 'pinned'
3364
3365 w->Close();
3366 }
3367 }
3368}
3369
3378{
3379 /* Note: the container remains stable, even when closing windows. */
3380 for (Window *w : Window::Iterate()) {
3382 w->Close();
3383 }
3384 }
3385}
3386
3391{
3393 InvalidateWindowData(WindowClass::Statusbar, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3394 InvalidateWindowData(WindowClass::MessageHistory, 0); // invalidate the message history
3395 CloseWindowById(WindowClass::News, 0); // close newspaper or general message window if shown
3396}
3397
3403{
3404 /* Note: the container remains stable, even when deleting windows. */
3405 for (Window *w : Window::Iterate()) {
3407 w->Close();
3408 }
3409 }
3410
3411 for (const Window *w : Window::Iterate()) w->SetDirty();
3412}
3413
3416{
3417 CloseWindowById(WindowClass::MainToolbar, 0);
3418 CloseWindowById(WindowClass::Statusbar, 0);
3419}
3420
3421void ReInitWindow(Window *w, bool zoom_changed)
3422{
3423 if (w == nullptr) return;
3424 if (zoom_changed) {
3425 w->nested_root->AdjustPaddingForZoom();
3427 }
3428 w->ReInit();
3429}
3430
3432void ReInitAllWindows(bool zoom_changed)
3433{
3435 NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3436 NWidgetScrollbar::InvalidateDimensionCache();
3437
3439
3440 /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3441 * so EnsureVisibleCaption uses the updated size information. */
3442 ReInitWindow(FindWindowById(WindowClass::MainToolbar, 0), zoom_changed);
3443 ReInitWindow(FindWindowById(WindowClass::Statusbar, 0), zoom_changed);
3444 for (Window *w : Window::Iterate()) {
3445 if (w->window_class == WindowClass::MainToolbar || w->window_class == WindowClass::Statusbar) continue;
3446 ReInitWindow(w, zoom_changed);
3447 }
3448
3451
3452 /* Make sure essential parts of all windows are visible */
3453 RelocateAllWindows(_screen.width, _screen.height);
3455}
3456
3464static int PositionWindow(Window *w, WindowClass clss, int setting)
3465{
3466 if (w == nullptr || w->window_class != clss) {
3467 w = FindWindowById(clss, 0);
3468 }
3469 if (w == nullptr) return 0;
3470
3471 int old_left = w->left;
3472 switch (setting) {
3473 case 1: w->left = (_screen.width - w->width) / 2; break;
3474 case 2: w->left = _screen.width - w->width; break;
3475 default: w->left = 0; break;
3476 }
3477 if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3478 AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3479 return w->left;
3480}
3481
3488{
3489 Debug(misc, 5, "Repositioning Main Toolbar...");
3490 return PositionWindow(w, WindowClass::MainToolbar, _settings_client.gui.toolbar_pos);
3491}
3492
3499{
3500 Debug(misc, 5, "Repositioning statusbar...");
3501 return PositionWindow(w, WindowClass::Statusbar, _settings_client.gui.statusbar_pos);
3502}
3503
3510{
3511 Debug(misc, 5, "Repositioning news message...");
3512 return PositionWindow(w, WindowClass::News, _settings_client.gui.statusbar_pos);
3513}
3514
3521{
3522 Debug(misc, 5, "Repositioning network chat window...");
3523 return PositionWindow(w, WindowClass::NetworkChat, _settings_client.gui.statusbar_pos);
3524}
3525
3526
3533{
3534 for (const Window *w : Window::Iterate()) {
3535 if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3536 w->viewport->follow_vehicle = to_index;
3537 w->SetDirty();
3538 }
3539 }
3540}
3541
3542
3548void RelocateAllWindows(int neww, int newh)
3549{
3550 CloseWindowByClass(WindowClass::DropdownMenu);
3551
3552 /* Reposition toolbar then status bar before other all windows. */
3553 if (Window *wt = FindWindowById(WindowClass::MainToolbar, 0); wt != nullptr) {
3554 ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3555 wt->left = PositionMainToolbar(wt);
3556 }
3557
3558 if (Window *ws = FindWindowById(WindowClass::Statusbar, 0); ws != nullptr) {
3559 ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3560 ws->top = newh - ws->height;
3561 ws->left = PositionStatusbar(ws);
3562 }
3563
3564 for (Window *w : Window::Iterate()) {
3565 int left, top;
3566 /* XXX - this probably needs something more sane. For example specifying
3567 * in a 'backup'-desc that the window should always be centered. */
3568 switch (w->window_class) {
3569 case WindowClass::MainWindow:
3570 case WindowClass::Bootstrap:
3571 case WindowClass::Highscore:
3572 case WindowClass::Endscreen:
3573 ResizeWindow(w, neww, newh);
3574 continue;
3575
3576 case WindowClass::MainToolbar:
3577 case WindowClass::Statusbar:
3578 continue;
3579
3580 case WindowClass::News:
3581 top = newh - w->height;
3582 left = PositionNewsMessage(w);
3583 break;
3584
3585 case WindowClass::NetworkChat:
3586 ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3587
3588 top = newh - w->height - FindWindowById(WindowClass::Statusbar, 0)->height;
3589 left = PositionNetworkChatWindow(w);
3590 break;
3591
3592 case WindowClass::Console:
3593 IConsoleResize(w);
3594 continue;
3595
3596 default: {
3597 if (w->flags.Test(WindowFlag::Centred)) {
3598 top = (newh - w->height) >> 1;
3599 left = (neww - w->width) >> 1;
3600 break;
3601 }
3602
3603 left = w->left;
3604 if (left + (w->width >> 1) >= neww) left = neww - w->width;
3605 if (left < 0) left = 0;
3606
3607 top = w->top;
3608 if (top + (w->height >> 1) >= newh) top = newh - w->height;
3609 break;
3610 }
3611 }
3612
3613 EnsureVisibleCaption(w, left, top);
3614 }
3615}
3616
3622void PickerWindowBase::Close([[maybe_unused]] int data)
3623{
3625 this->Window::Close();
3626}
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:949
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:1290
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1263
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:2673
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:3622
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 PFE_SOUND measurement data queue into _pf_data.
Types for recording game performance data.
@ PFE_DRAWING
Speed of drawing world and GUI.
@ PFE_DRAWWORLD
Time spent drawing world viewports in GUI.
Base functions for all Games.
bool _shift_pressed
Is Shift pressed?
Definition gfx.cpp:40
bool _left_button_down
Is left mouse button pressed?
Definition gfx.cpp:42
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
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:316
@ Invalid
Invalid colour.
Definition gfx_type.h:337
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
Definition gfx_type.h:418
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:3432
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:972
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
@ 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:897
void IniSaveWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Save a WindowDesc to config.
Definition settings.cpp:908
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.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
T y
Y coordinate.
T x
X coordinate.
Data about how and where to blit pixels.
Definition gfx_type.h:157
List of hotkeys for a window.
Definition hotkeys.h:46
int CheckMatch(uint16_t keycode, bool global_only=false) const
Check if a keycode is bound to something.
Definition hotkeys.cpp:302
Ini file that supports both loading and saving.
Definition ini_type.h:87
bool SaveToDisk(const std::string &filename)
Save the Ini file's data to the disk.
Definition ini.cpp:42
void LoadFromDisk(std::string_view filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition ini_load.cpp:184
static Vehicle * Get(auto index)
Data stored about a string that can be modified in the GUI.
int ok_button
Widget button of parent window to simulate when pressing OK in OSK.
static const int ACTION_DESELECT
Deselect editbox.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
ptrdiff_t GetCharAtPosition(const Window *w, WidgetID wid, const Point &pt) const
Get the character that is rendered at a position.
Definition misc_gui.cpp: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:142
~WindowDesc()
Remove ourselves from the global list of window descs.
Definition window.cpp:132
static void SaveToConfig()
Save all WindowDesc settings to _windows_file.
Definition window.cpp:179
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:152
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:160
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:112
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:370
void SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
Sets the highlighted status of a widget.
Definition window.cpp:244
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:984
void CloseChildWindows(WindowClass wc=WindowClass::Invalid) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1081
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1109
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:1814
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:196
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:1463
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:3255
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:1146
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:562
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:510
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:3268
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:475
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:1027
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:3233
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:1804
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:1096
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:491
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition window.cpp:527
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:536
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition window.cpp:1793
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:383
virtual void FindWindowPlacementAndResize(int def_width, int def_height, bool allow_resize)
Resize window towards the default size.
Definition window.cpp:1482
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:290
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:2732
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:359
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:339
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:414
Window * FindChildWindow(WindowClass wc=WindowClass::Invalid) const
Find the Window whose parent pointer points to this window.
Definition window.cpp:1053
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:1425
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:1838
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:215
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:3241
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition window.cpp:2570
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:601
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:1068
virtual void OnFocus()
The window has gained focus.
Definition window.cpp:519
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1828
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:319
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition window.cpp:3280
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:576
static void DeleteClosedWindows()
Delete all closed windows.
Definition window.cpp:60
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:399
bool IsWidgetHighlighted(WidgetID widget_index) const
Gets the highlighted status of a widget.
Definition window.cpp:274
void DisableAllWidgetHighlight()
Disable the highlighted status of all widgets.
Definition window.cpp:226
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:294
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:270
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:35
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:74
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:63
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:75
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX).
Definition widget_type.h:54
@ ScrollbarDown
Down-button is lowered bit.
@ DropdownClosed
Dropdown menu of the dropdown widget has closed.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ ScrollbarUp
Up-button is lowered bit.
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ Resize
Resize the nested widget tree.
@ Smallest
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
int PositionStatusbar(Window *w)
(Re)position statusbar window at the screen.
Definition window.cpp:3498
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:2027
static bool _dragging_window
A window is being dragged or resized.
Definition window.cpp:2162
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:3402
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:1201
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:1734
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1187
const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK
Time between 2 left clicks before it becoming a double click.
Definition window.cpp:2813
static Point _drag_delta
delta between mouse cursor and upper left corner of dragged window
Definition window.cpp:47
static bool MayBeShown(const Window *w)
Returns whether a window may be shown or not.
Definition window.cpp:861
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition window.cpp:1230
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition window.cpp:2715
MouseClick
Mouse states during the MouseLoop.
Definition window.cpp:2800
@ None
No action to process.
Definition window.cpp:2801
@ Right
A click with the right mouse button.
Definition window.cpp:2803
@ Left
A click with the left mouse button.
Definition window.cpp:2802
@ DoubleLeft
A double click with the left mouse button.
Definition window.cpp:2804
@ Hover
The mouse started hovering.
Definition window.cpp:2805
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:889
bool _scrolling_viewport
A viewport is being scrolled with the mouse.
Definition window.cpp:85
void InputLoop()
Regular call from the global game loop.
Definition window.cpp:3075
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition window.cpp:3141
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition window.cpp:3487
Window * _focused_window
Window that currently has focus.
Definition window.cpp:77
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition window.cpp:3358
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:951
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition window.cpp:2105
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:1584
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition window.cpp:3520
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition window.cpp:2357
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:1623
PreventHideDirection
Direction for moving the window.
Definition window.cpp:2012
@ Down
Below v is a safe position.
Definition window.cpp:2014
@ Up
Above v is a safe position.
Definition window.cpp:2013
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition window.cpp:2762
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:70
void HandleToolbarHotkey(int hotkey)
Handle Toolbar hotkey events - can come from a source like the MacBook Touch Bar.
Definition window.cpp:2642
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition window.cpp:3464
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:427
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:1548
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition window.cpp:2808
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1173
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3532
static EventState HandleViewportScroll()
Handle viewport scrolling with the mouse.
Definition window.cpp:2446
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition window.cpp:824
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition window.cpp:622
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1850
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition window.cpp:3390
static Window * _last_scroll_window
Window of the last scroll event.
Definition window.cpp:49
int GetMainViewTop()
Return the top of the main view available for general use.
Definition window.cpp:2145
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:1406
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3432
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:2067
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1214
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition window.cpp:2979
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:1691
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition window.cpp:2168
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition window.cpp:3509
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition window.cpp:2156
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition window.cpp:796
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:1250
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition window.cpp:2659
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3223
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:2809
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition window.cpp:467
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition window.cpp:1900
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition window.cpp:1322
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition window.cpp:453
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:3315
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition window.cpp:3415
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:171
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1285
Point AlignInitialConstructionToolbar(int window_width)
Compute the position of the construction toolbars.
Definition window.cpp:1707
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition window.cpp:3548
std::string _windows_file
Config file to store WindowDesc.
Definition window.cpp:97
static void HandleScrollbarScrolling(Window *w)
Handle scrollbar scrolling with the mouse.
Definition window.cpp:2373
bool _mouse_hovering
The mouse is hovering over the same point.
Definition window.cpp:86
static void StartWindowDrag(Window *w)
Start window dragging.
Definition window.cpp:2340
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition window.cpp:3345
static EventState HandleActiveWidget()
Handle active widget (mouse dragging on widget) with the mouse.
Definition window.cpp:2413
std::vector< WindowDesc * > * _window_descs
List of all WindowDescs.
Definition window.cpp:94
static void DispatchRightClickEvent(Window *w, int x, int y)
Dispatch right mouse-button click in window.
Definition window.cpp:767
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:2833
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition window.cpp:2510
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition window.cpp:52
void CallWindowRealtimeTickEvent(uint delta_ms)
Dispatch OnRealtimeTick event over all windows.
Definition window.cpp:3102
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:88
static EventState HandleMouseDragDrop()
Handle dragging and dropping in mouse dragging mode (SpecialMouseMode::DragDrop).
Definition window.cpp:1965
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition window.cpp:3377
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition window.cpp:1989
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:48
static void CheckSoftLimit()
Check the soft limit of deletable (non vital, non sticky) windows.
Definition window.cpp:3050
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1158
void UnInitWindowSystem()
Close down the windowing system.
Definition window.cpp:1886
void InitWindowSystem()
(re)initialize the windowing system
Definition window.cpp:1864
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:3209
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:2751
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3193
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:3333
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:77
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:427
@ 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:294
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:52
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