OpenTTD Source  20241120-master-g6d3adc6169
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 <http://www.gnu.org/licenses/>.
6  */
7 
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"
35 #include "video/video_driver.hpp"
36 #include "framerate_type.h"
37 #include "network/network_func.h"
38 #include "news_func.h"
39 #include "timer/timer.h"
40 #include "timer/timer_window.h"
41 
42 #include "safeguards.h"
43 
50 };
51 
53 static Window *_mouseover_last_w = nullptr;
54 static Window *_last_scroll_window = nullptr;
55 
57 WindowList _z_windows;
58 
60 /* static */ std::vector<Window *> Window::closed_windows;
61 
65 /* static */ void Window::DeleteClosedWindows()
66 {
67  for (Window *w : Window::closed_windows) delete w;
68  Window::closed_windows.clear();
69 
70  /* Remove dead entries from the window list */
71  _z_windows.remove(nullptr);
72 }
73 
76 
77 /*
78  * Window that currently has focus. - The main purpose is to generate
79  * #FocusLost events, not to give next window in z-order focus when a
80  * window is closed.
81  */
82 Window *_focused_window;
83 
84 Point _cursorpos_drag_start;
85 
86 int _scrollbar_start_pos;
87 int _scrollbar_size;
88 uint8_t _scroller_click_timeout = 0;
89 
92 
94 
99 std::vector<WindowDesc*> *_window_descs = nullptr;
100 
102 std::string _windows_file;
103 
105 WindowDesc::WindowDesc(WindowPosition def_pos, const char *ini_key, int16_t def_width_trad, int16_t def_height_trad,
106  WindowClass window_class, WindowClass parent_class, uint32_t flags,
107  const std::span<const NWidgetPart> nwid_parts, HotkeyList *hotkeys,
108  const std::source_location location) :
109  source_location(location),
110  default_pos(def_pos),
111  cls(window_class),
112  parent_cls(parent_class),
113  ini_key(ini_key),
114  flags(flags),
115  nwid_parts(nwid_parts),
116  hotkeys(hotkeys),
117  pref_sticky(false),
118  pref_width(0),
119  pref_height(0),
120  default_width_trad(def_width_trad),
121  default_height_trad(def_height_trad)
122 {
123  if (_window_descs == nullptr) _window_descs = new std::vector<WindowDesc*>();
124  _window_descs->push_back(this);
125 }
126 
127 WindowDesc::~WindowDesc()
128 {
129  _window_descs->erase(std::find(_window_descs->begin(), _window_descs->end(), this));
130 }
131 
138 {
139  return this->pref_width != 0 ? this->pref_width : ScaleGUITrad(this->default_width_trad);
140 }
141 
148 {
149  return this->pref_height != 0 ? this->pref_height : ScaleGUITrad(this->default_height_trad);
150 }
151 
156 {
157  IniFile ini;
159  for (WindowDesc *wd : *_window_descs) {
160  if (wd->ini_key == nullptr) continue;
161  IniLoadWindowSettings(ini, wd->ini_key, wd);
162  }
163 }
164 
168 static bool DescSorter(WindowDesc * const &a, WindowDesc * const &b)
169 {
170  if (a->ini_key != nullptr && b->ini_key != nullptr) return strcmp(a->ini_key, b->ini_key) < 0;
171  return a->ini_key != nullptr;
172 }
173 
178 {
179  /* Sort the stuff to get a nice ini file on first write */
180  std::sort(_window_descs->begin(), _window_descs->end(), DescSorter);
181 
182  IniFile ini;
184  for (WindowDesc *wd : *_window_descs) {
185  if (wd->ini_key == nullptr) continue;
186  IniSaveWindowSettings(ini, wd->ini_key, wd);
187  }
189 }
190 
195 {
196  if (this->nested_root != nullptr && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != nullptr) {
197  if (this->window_desc.pref_sticky) this->flags |= WF_STICKY;
198  } else {
199  /* There is no stickybox; clear the preference in case someone tried to be funny */
200  this->window_desc.pref_sticky = false;
201  }
202 }
203 
213 int Window::GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height) const
214 {
215  const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
216  if (line_height < 0) line_height = wid->resize_y;
217  if (clickpos < wid->pos_y + padding) return INT_MAX;
218  return (clickpos - wid->pos_y - padding) / line_height;
219 }
220 
225 {
226  for (auto &pair : this->widget_lookup) {
227  NWidgetBase *nwid = pair.second;
228  if (nwid->IsHighlighted()) {
229  nwid->SetHighlighted(TC_INVALID);
230  nwid->SetDirty(this);
231  }
232  }
233 
234  CLRBITS(this->flags, WF_HIGHLIGHTED);
235 }
236 
242 void Window::SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
243 {
244  NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
245  if (nwid == nullptr) return;
246 
247  nwid->SetHighlighted(highlighted_colour);
248  nwid->SetDirty(this);
249 
250  if (highlighted_colour != TC_INVALID) {
251  /* If we set a highlight, the window has a highlight */
252  this->flags |= WF_HIGHLIGHTED;
253  } else {
254  /* If we disable a highlight, check all widgets if anyone still has a highlight */
255  bool valid = false;
256  for (const auto &pair : this->widget_lookup) {
257  nwid = pair.second;
258  if (!nwid->IsHighlighted()) continue;
259 
260  valid = true;
261  }
262  /* If nobody has a highlight, disable the flag on the window */
263  if (!valid) CLRBITS(this->flags, WF_HIGHLIGHTED);
264  }
265 }
266 
272 bool Window::IsWidgetHighlighted(WidgetID widget_index) const
273 {
274  const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
275  if (nwid == nullptr) return false;
276 
277  return nwid->IsHighlighted();
278 }
279 
287 void Window::OnDropdownClose(Point pt, WidgetID widget, int index, bool instant_close)
288 {
289  if (widget < 0) return;
290 
291  if (instant_close) {
292  /* Send event for selected option if we're still
293  * on the parent button of the dropdown (behaviour of the dropdowns in the main toolbar). */
294  if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
295  this->OnDropdownSelect(widget, index);
296  }
297  }
298 
299  /* Raise the dropdown button */
300  NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
301  if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
302  nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
303  } else {
304  this->RaiseWidget(widget);
305  }
306  this->SetWidgetDirty(widget);
307 }
308 
315 {
316  return this->GetWidget<NWidgetScrollbar>(widnum);
317 }
318 
325 {
326  return this->GetWidget<NWidgetScrollbar>(widnum);
327 }
328 
335 {
336  auto query = this->querystrings.find(widnum);
337  return query != this->querystrings.end() ? query->second : nullptr;
338 }
339 
346 {
347  auto query = this->querystrings.find(widnum);
348  return query != this->querystrings.end() ? query->second : nullptr;
349 }
350 
355 {
356  for (auto &qs : this->querystrings) {
357  qs.second->text.UpdateSize();
358  }
359 }
360 
365 /* virtual */ const Textbuf *Window::GetFocusedTextbuf() const
366 {
367  if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
368  return &this->GetQueryString(this->nested_focus->index)->text;
369  }
370 
371  return nullptr;
372 }
373 
378 /* virtual */ Point Window::GetCaretPosition() const
379 {
380  if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) {
381  return this->GetQueryString(this->nested_focus->index)->GetCaretPosition(this, this->nested_focus->index);
382  }
383 
384  Point pt = {0, 0};
385  return pt;
386 }
387 
394 /* virtual */ Rect Window::GetTextBoundingRect(const char *from, const char *to) const
395 {
396  if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
397  return this->GetQueryString(this->nested_focus->index)->GetBoundingRect(this, this->nested_focus->index, from, to);
398  }
399 
400  Rect r = {0, 0, 0, 0};
401  return r;
402 }
403 
409 /* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
410 {
411  if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
412  return this->GetQueryString(this->nested_focus->index)->GetCharAtPosition(this, this->nested_focus->index, pt);
413  }
414 
415  return -1;
416 }
417 
423 {
424  if (_focused_window == w) return;
425 
426  /* Don't focus a tooltip */
427  if (w != nullptr && w->window_class == WC_TOOLTIPS) return;
428 
429  /* Invalidate focused widget */
430  if (_focused_window != nullptr) {
431  if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window);
432  }
433 
434  /* Remember which window was previously focused */
435  Window *old_focused = _focused_window;
436  _focused_window = w;
437 
438  /* So we can inform it that it lost focus */
439  if (old_focused != nullptr) old_focused->OnFocusLost(false);
440  if (_focused_window != nullptr) _focused_window->OnFocus();
441 }
442 
449 {
450  if (_focused_window == nullptr) return false;
451 
452  /* The console does not have an edit box so a special case is needed. */
453  if (_focused_window->window_class == WC_CONSOLE) return true;
454 
455  return _focused_window->nested_focus != nullptr && _focused_window->nested_focus->type == WWT_EDITBOX;
456 }
457 
463 {
464  return _focused_window && _focused_window->window_class == WC_CONSOLE;
465 }
466 
471 {
472  if (this->nested_focus != nullptr) {
474 
475  /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
476  this->nested_focus->SetDirty(this);
477  this->nested_focus = nullptr;
478  }
479 }
480 
487 {
488  NWidgetCore *widget = this->GetWidget<NWidgetCore>(widget_index);
489  assert(widget != nullptr); /* Setting focus to a non-existing widget is a bad idea. */
490 
491  if (this->nested_focus != nullptr) {
492  /* Do nothing if widget_index is already focused. */
493  if (widget == this->nested_focus) return false;
494 
495  /* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
496  this->nested_focus->SetDirty(this);
498  }
499 
500  this->nested_focus = widget;
502  return true;
503 }
504 
509 {
511 }
512 
517 {
518  if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
519 }
520 
525 void Window::RaiseButtons(bool autoraise)
526 {
527  for (auto &pair : this->widget_lookup) {
528  WidgetType type = pair.second->type;
529  NWidgetCore *wid = dynamic_cast<NWidgetCore *>(pair.second);
530  if (((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
531  (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && wid->IsLowered()) {
532  wid->SetLowered(false);
533  wid->SetDirty(this);
534  }
535  }
536 
537  /* Special widgets without widget index */
538  {
539  NWidgetCore *wid = this->nested_root != nullptr ? dynamic_cast<NWidgetCore *>(this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX)) : nullptr;
540  if (wid != nullptr) {
541  wid->SetLowered(false);
542  wid->SetDirty(this);
543  }
544  }
545 }
546 
551 void Window::SetWidgetDirty(WidgetID widget_index) const
552 {
553  /* Sometimes this function is called before the window is even fully initialized */
554  auto it = this->widget_lookup.find(widget_index);
555  if (it == std::end(this->widget_lookup)) return;
556 
557  it->second->SetDirty(this);
558 }
559 
566 {
567  if (hotkey < 0) return ES_NOT_HANDLED;
568 
569  NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
570  if (nw == nullptr || nw->IsDisabled()) return ES_NOT_HANDLED;
571 
572  if (nw->type == WWT_EDITBOX) {
573  if (this->IsShaded()) return ES_NOT_HANDLED;
574 
575  /* Focus editbox */
576  this->SetFocusedWidget(hotkey);
577  SetFocusedWindow(this);
578  } else {
579  /* Click button */
580  this->OnClick(Point(), hotkey, 1);
581  }
582  return ES_HANDLED;
583 }
584 
591 {
592  this->LowerWidget(widget);
593  this->SetTimeout();
594  this->SetWidgetDirty(widget);
595 }
596 
597 static void StartWindowDrag(Window *w);
598 static void StartWindowSizing(Window *w, bool to_left);
599 
607 static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
608 {
609  NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
610  WidgetType widget_type = (nw != nullptr) ? nw->type : WWT_EMPTY;
611 
612  /* Allow dropdown close flag detection to work. */
613  if (nw != nullptr) ClrBit(nw->disp_flags, NDB_DROPDOWN_CLOSED);
614 
615  bool focused_widget_changed = false;
616  /* If clicked on a window that previously did not have focus */
617  if (_focused_window != w && // We already have focus, right?
618  (w->window_desc.flags & WDF_NO_FOCUS) == 0 && // Don't lose focus to toolbars
619  widget_type != WWT_CLOSEBOX) { // Don't change focused window if 'X' (close button) was clicked
620  focused_widget_changed = true;
621  SetFocusedWindow(w);
622  }
623 
624  if (nw == nullptr) return; // exit if clicked outside of widgets
625 
626  /* don't allow any interaction if the button has been disabled */
627  if (nw->IsDisabled()) return;
628 
629  WidgetID widget_index = nw->index;
630 
631  /* Clicked on a widget that is not disabled.
632  * So unless the clicked widget is the caption bar, change focus to this widget.
633  * Exception: In the OSK we always want the editbox to stay focused. */
634  if (widget_index >= 0 && widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
635  /* focused_widget_changed is 'now' only true if the window this widget
636  * is in gained focus. In that case it must remain true, also if the
637  * local widget focus did not change. As such it's the logical-or of
638  * both changed states.
639  *
640  * If this is not preserved, then the OSK window would be opened when
641  * a user has the edit box focused and then click on another window and
642  * then back again on the edit box (to type some text).
643  */
644  focused_widget_changed |= w->SetFocusedWidget(widget_index);
645  }
646 
647  /* Dropdown window of this widget was closed so don't process click this time. */
648  if (HasBit(nw->disp_flags, NDB_DROPDOWN_CLOSED)) return;
649 
650  if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
651 
652  Point pt = { x, y };
653 
654  switch (widget_type) {
655  case NWID_VSCROLLBAR:
656  case NWID_HSCROLLBAR:
657  ScrollbarClickHandler(w, nw, x, y);
658  break;
659 
660  case WWT_EDITBOX: {
661  QueryString *query = w->GetQueryString(widget_index);
662  if (query != nullptr) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
663  break;
664  }
665 
666  case WWT_CLOSEBOX: // 'X'
667  w->Close();
668  return;
669 
670  case WWT_CAPTION: // 'Title bar'
671  StartWindowDrag(w);
672  return;
673 
674  case WWT_RESIZEBOX:
675  /* When the resize widget is on the left size of the window
676  * we assume that that button is used to resize to the left. */
677  StartWindowSizing(w, nw->pos_x < (w->width / 2));
678  nw->SetDirty(w);
679  return;
680 
681  case WWT_DEFSIZEBOX: {
682  if (_ctrl_pressed) {
683  w->window_desc.pref_width = w->width;
685  } else {
686  int16_t def_width = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
687  int16_t def_height = std::max<int16_t>(std::min<int16_t>(w->window_desc.GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
688 
689  int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
690  int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
691  /* dx and dy has to go by step.. calculate it.
692  * The cast to int is necessary else dx/dy are implicitly casted to unsigned int, which won't work. */
693  if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
694  if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
695  ResizeWindow(w, dx, dy, false);
696  }
697 
698  nw->SetLowered(true);
699  nw->SetDirty(w);
700  w->SetTimeout();
701  break;
702  }
703 
704  case WWT_DEBUGBOX:
706  break;
707 
708  case WWT_SHADEBOX:
709  nw->SetDirty(w);
710  w->SetShaded(!w->IsShaded());
711  return;
712 
713  case WWT_STICKYBOX:
714  w->flags ^= WF_STICKY;
715  nw->SetDirty(w);
716  if (_ctrl_pressed) w->window_desc.pref_sticky = (w->flags & WF_STICKY) != 0;
717  return;
718 
719  default:
720  break;
721  }
722 
723  /* Widget has no index, so the window is not interested in it. */
724  if (widget_index < 0) return;
725 
726  /* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
727  if (w->IsWidgetHighlighted(widget_index)) {
728  w->SetWidgetHighlight(widget_index, TC_INVALID);
729  Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
730  }
731 
732  w->OnClick(pt, widget_index, click_count);
733 }
734 
741 static void DispatchRightClickEvent(Window *w, int x, int y)
742 {
743  NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
744  if (wid == nullptr) return;
745 
746  Point pt = { x, y };
747 
748  /* No widget to handle, or the window is not interested in it. */
749  if (wid->index >= 0) {
750  if (w->OnRightClick(pt, wid->index)) return;
751  }
752 
753  /* Right-click close is enabled and there is a closebox. */
755  w->Close();
756  } else if (_settings_client.gui.right_click_wnd_close == RCC_YES_EXCEPT_STICKY && (w->flags & WF_STICKY) == 0 && (w->window_desc.flags & WDF_NO_CLOSE) == 0) {
757  /* Right-click close is enabled, but excluding sticky windows. */
758  w->Close();
759  } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->index, TCC_RIGHT_CLICK) && wid->tool_tip != 0) {
760  GuiShowTooltips(w, wid->tool_tip, TCC_RIGHT_CLICK);
761  }
762 }
763 
770 static void DispatchHoverEvent(Window *w, int x, int y)
771 {
772  NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
773 
774  /* No widget to handle */
775  if (wid == nullptr) return;
776 
777  Point pt = { x, y };
778 
779  /* Show the tooltip if there is any */
780  if (!w->OnTooltip(pt, wid->index, TCC_HOVER) && wid->tool_tip != 0) {
781  GuiShowTooltips(w, wid->tool_tip, TCC_HOVER);
782  return;
783  }
784 
785  /* Widget has no index, so the window is not interested in it. */
786  if (wid->index < 0) return;
787 
788  w->OnHover(pt, wid->index);
789 }
790 
798 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
799 {
800  if (nwid == nullptr) return;
801 
802  /* Using wheel on caption/shade-box shades or unshades the window. */
803  if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
804  w->SetShaded(wheel < 0);
805  return;
806  }
807 
808  /* Wheeling a vertical scrollbar. */
809  if (nwid->type == NWID_VSCROLLBAR) {
810  NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
811  if (sb->GetCount() > sb->GetCapacity()) {
812  if (sb->UpdatePosition(wheel)) w->SetDirty();
813  }
814  return;
815  }
816 
817  /* Scroll the widget attached to the scrollbar. */
818  Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : nullptr);
819  if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) {
820  if (sb->UpdatePosition(wheel)) w->SetDirty();
821  }
822 }
823 
829 static bool MayBeShown(const Window *w)
830 {
831  /* If we're not modal, everything is okay. */
832  if (!HasModalProgress()) return true;
833 
834  switch (w->window_class) {
835  case WC_MAIN_WINDOW:
836  case WC_MODAL_PROGRESS:
838  return true;
839 
840  default:
841  return false;
842  }
843 }
844 
857 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
858 {
860  ++it;
861  for (; !it.IsEnd(); ++it) {
862  const Window *v = *it;
863  if (MayBeShown(v) &&
864  right > v->left &&
865  bottom > v->top &&
866  left < v->left + v->width &&
867  top < v->top + v->height) {
868  /* v and rectangle intersect with each other */
869  int x;
870 
871  if (left < (x = v->left)) {
872  DrawOverlappedWindow(w, left, top, x, bottom);
873  DrawOverlappedWindow(w, x, top, right, bottom);
874  return;
875  }
876 
877  if (right > (x = v->left + v->width)) {
878  DrawOverlappedWindow(w, left, top, x, bottom);
879  DrawOverlappedWindow(w, x, top, right, bottom);
880  return;
881  }
882 
883  if (top < (x = v->top)) {
884  DrawOverlappedWindow(w, left, top, right, x);
885  DrawOverlappedWindow(w, left, x, right, bottom);
886  return;
887  }
888 
889  if (bottom > (x = v->top + v->height)) {
890  DrawOverlappedWindow(w, left, top, right, x);
891  DrawOverlappedWindow(w, left, x, right, bottom);
892  return;
893  }
894 
895  return;
896  }
897  }
898 
899  /* Setup blitter, and dispatch a repaint event to window *wz */
900  DrawPixelInfo *dp = _cur_dpi;
901  dp->width = right - left;
902  dp->height = bottom - top;
903  dp->left = left - w->left;
904  dp->top = top - w->top;
905  dp->pitch = _screen.pitch;
906  dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
907  dp->zoom = ZOOM_LVL_MIN;
908  w->OnPaint();
909 }
910 
919 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
920 {
921  DrawPixelInfo bk;
922  AutoRestoreBackup dpi_backup(_cur_dpi, &bk);
923 
924  for (Window *w : Window::IterateFromBack()) {
925  if (MayBeShown(w) &&
926  right > w->left &&
927  bottom > w->top &&
928  left < w->left + w->width &&
929  top < w->top + w->height) {
930  /* Window w intersects with the rectangle => needs repaint */
931  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));
932  }
933  }
934 }
935 
940 void Window::SetDirty() const
941 {
942  AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
943 }
944 
952 void Window::ReInit(int rx, int ry, bool reposition)
953 {
954  this->SetDirty(); // Mark whole current window as dirty.
955 
956  /* Save current size. */
957  int window_width = this->width * _gui_scale / this->scale;
958  int window_height = this->height * _gui_scale / this->scale;
959  this->scale = _gui_scale;
960 
961  this->OnInit();
962  /* Re-initialize window smallest size. */
963  this->nested_root->SetupSmallestSize(this);
964  this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
965  this->width = this->nested_root->smallest_x;
966  this->height = this->nested_root->smallest_y;
967  this->resize.step_width = this->nested_root->resize_x;
968  this->resize.step_height = this->nested_root->resize_y;
969 
970  /* Resize as close to the original size + requested resize as possible. */
971  window_width = std::max(window_width + rx, this->width);
972  window_height = std::max(window_height + ry, this->height);
973  int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
974  int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
975  /* dx and dy has to go by step.. calculate it.
976  * The cast to int is necessary else dx/dy are implicitly casted to unsigned int, which won't work. */
977  if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
978  if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
979 
980  if (reposition) {
981  Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
982  this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
983  this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight());
984  }
985 
986  ResizeWindow(this, dx, dy, true, false);
987  /* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
988 }
989 
995 void Window::SetShaded(bool make_shaded)
996 {
997  if (this->shade_select == nullptr) return;
998 
999  int desired = make_shaded ? SZSP_HORIZONTAL : 0;
1000  if (this->shade_select->shown_plane != desired) {
1001  if (make_shaded) {
1002  if (this->nested_focus != nullptr) this->UnfocusFocusedWidget();
1003  this->unshaded_size.width = this->width;
1004  this->unshaded_size.height = this->height;
1005  this->shade_select->SetDisplayedPlane(desired);
1006  this->ReInit(0, -this->height);
1007  } else {
1008  this->shade_select->SetDisplayedPlane(desired);
1009  int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
1010  int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
1011  this->ReInit(dx, dy);
1012  }
1013  }
1014 }
1015 
1023 {
1024  for (Window *v : Window::Iterate()) {
1025  if ((wc == WC_INVALID || wc == v->window_class) && v->parent == this) return v;
1026  }
1027 
1028  return nullptr;
1029 }
1030 
1036 {
1037  Window *child = this->FindChildWindow(wc);
1038  while (child != nullptr) {
1039  child->Close();
1040  child = this->FindChildWindow(wc);
1041  }
1042 }
1043 
1047 void Window::Close([[maybe_unused]] int data)
1048 {
1049  /* Don't close twice. */
1050  if (*this->z_position == nullptr) return;
1051 
1052  *this->z_position = nullptr;
1053 
1054  if (_thd.window_class == this->window_class &&
1055  _thd.window_number == this->window_number) {
1057  }
1058 
1059  /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
1060  if (_mouseover_last_w == this) _mouseover_last_w = nullptr;
1061 
1062  /* We can't scroll the window when it's closed. */
1063  if (_last_scroll_window == this) _last_scroll_window = nullptr;
1064 
1065  /* Make sure we don't try to access non-existing query strings. */
1066  this->querystrings.clear();
1067 
1068  /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
1069  if (_focused_window == this) {
1070  this->OnFocusLost(true);
1071  _focused_window = nullptr;
1072  }
1073 
1074  this->CloseChildWindows();
1075 
1076  this->SetDirty();
1077 
1078  Window::closed_windows.push_back(this);
1079 }
1080 
1085 {
1086  /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */
1087  assert(*this->z_position == nullptr);
1088 
1089  if (this->viewport != nullptr) DeleteWindowViewport(this);
1090 }
1091 
1099 {
1100  for (Window *w : Window::Iterate()) {
1101  if (w->window_class == cls && w->window_number == number) return w;
1102  }
1103 
1104  return nullptr;
1105 }
1106 
1114 {
1115  for (Window *w : Window::Iterate()) {
1116  if (w->window_class == cls) return w;
1117  }
1118 
1119  return nullptr;
1120 }
1121 
1128 {
1130  assert(w != nullptr);
1131  return w;
1132 }
1133 
1140 void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
1141 {
1142  Window *w = FindWindowById(cls, number);
1143  if (w != nullptr && (force || (w->flags & WF_STICKY) == 0)) {
1144  w->Close(data);
1145  }
1146 }
1147 
1152 void CloseWindowByClass(WindowClass cls, int data)
1153 {
1154  /* Note: the container remains stable, even when deleting windows. */
1155  for (Window *w : Window::Iterate()) {
1156  if (w->window_class == cls) {
1157  w->Close(data);
1158  }
1159  }
1160 }
1161 
1169 {
1170  /* Note: the container remains stable, even when deleting windows. */
1171  for (Window *w : Window::Iterate()) {
1172  if (w->owner == id) {
1173  w->Close();
1174  }
1175  }
1176 
1177  /* Also delete the company specific windows that don't have a company-colour. */
1179 }
1180 
1188 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
1189 {
1190  for (Window *w : Window::Iterate()) {
1191  if (w->owner != old_owner) continue;
1192 
1193  switch (w->window_class) {
1194  case WC_COMPANY_COLOUR:
1195  case WC_FINANCES:
1196  case WC_STATION_LIST:
1197  case WC_TRAINS_LIST:
1198  case WC_ROADVEH_LIST:
1199  case WC_SHIPS_LIST:
1200  case WC_AIRCRAFT_LIST:
1201  case WC_BUY_COMPANY:
1202  case WC_COMPANY:
1204  case WC_VEHICLE_ORDERS: // Changing owner would also require changing WindowDesc, which is not possible; however keeping the old one crashes because of missing widgets etc.. See ShowOrdersWindow().
1205  continue;
1206 
1207  default:
1208  w->owner = new_owner;
1209  break;
1210  }
1211  }
1212 }
1213 
1214 static void BringWindowToFront(Window *w, bool dirty = true);
1215 
1224 {
1225  Window *w = FindWindowById(cls, number);
1226 
1227  if (w != nullptr) {
1228  if (w->IsShaded()) w->SetShaded(false); // Restore original window size if it was shaded.
1229 
1230  w->SetWhiteBorder();
1231  BringWindowToFront(w);
1232  w->SetDirty();
1233  }
1234 
1235  return w;
1236 }
1237 
1238 static inline bool IsVitalWindow(const Window *w)
1239 {
1240  switch (w->window_class) {
1241  case WC_MAIN_TOOLBAR:
1242  case WC_STATUS_BAR:
1243  case WC_NEWS_WINDOW:
1244  case WC_SEND_NETWORK_MSG:
1245  return true;
1246 
1247  default:
1248  return false;
1249  }
1250 }
1251 
1261 {
1262  assert(wc != WC_INVALID);
1263 
1264  uint z_priority = 0;
1265 
1266  switch (wc) {
1267  case WC_TOOLTIPS:
1268  ++z_priority;
1269  [[fallthrough]];
1270 
1271  case WC_ERRMSG:
1273  ++z_priority;
1274  [[fallthrough]];
1275 
1276  case WC_ENDSCREEN:
1277  ++z_priority;
1278  [[fallthrough]];
1279 
1280  case WC_HIGHSCORE:
1281  ++z_priority;
1282  [[fallthrough]];
1283 
1284  case WC_DROPDOWN_MENU:
1285  ++z_priority;
1286  [[fallthrough]];
1287 
1288  case WC_MAIN_TOOLBAR:
1289  case WC_STATUS_BAR:
1290  ++z_priority;
1291  [[fallthrough]];
1292 
1293  case WC_OSK:
1294  ++z_priority;
1295  [[fallthrough]];
1296 
1297  case WC_QUERY_STRING:
1298  case WC_SEND_NETWORK_MSG:
1299  ++z_priority;
1300  [[fallthrough]];
1301 
1302  case WC_NETWORK_ASK_RELAY:
1303  case WC_MODAL_PROGRESS:
1305  case WC_SAVE_PRESET:
1306  ++z_priority;
1307  [[fallthrough]];
1308 
1309  case WC_GENERATE_LANDSCAPE:
1310  case WC_SAVELOAD:
1311  case WC_GAME_OPTIONS:
1312  case WC_CUSTOM_CURRENCY:
1313  case WC_NETWORK_WINDOW:
1314  case WC_GRF_PARAMETERS:
1315  case WC_SCRIPT_LIST:
1316  case WC_SCRIPT_SETTINGS:
1317  case WC_TEXTFILE:
1318  ++z_priority;
1319  [[fallthrough]];
1320 
1321  case WC_CONSOLE:
1322  ++z_priority;
1323  [[fallthrough]];
1324 
1325  case WC_NEWS_WINDOW:
1326  ++z_priority;
1327  [[fallthrough]];
1328 
1329  default:
1330  ++z_priority;
1331  [[fallthrough]];
1332 
1333  case WC_MAIN_WINDOW:
1334  return z_priority;
1335  }
1336 }
1337 
1344 static void BringWindowToFront(Window *w, bool dirty)
1345 {
1346  auto priority = GetWindowZPriority(w->window_class);
1347  WindowList::iterator dest = _z_windows.begin();
1348  while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
1349 
1350  if (dest != w->z_position) {
1351  _z_windows.splice(dest, _z_windows, w->z_position);
1352  }
1353 
1354  if (dirty) w->SetDirty();
1355 }
1356 
1365 {
1366  /* Set up window properties; some of them are needed to set up smallest size below */
1367  this->window_class = this->window_desc.cls;
1368  this->SetWhiteBorder();
1369  if (this->window_desc.default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
1370  this->owner = INVALID_OWNER;
1371  this->nested_focus = nullptr;
1372  this->window_number = window_number;
1373 
1374  this->OnInit();
1375  /* Initialize smallest size. */
1376  this->nested_root->SetupSmallestSize(this);
1377  /* Initialize to smallest size. */
1378  this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
1379 
1380  /* Further set up window properties,
1381  * this->left, this->top, this->width, this->height, this->resize.width, and this->resize.height are initialized later. */
1382  this->resize.step_width = this->nested_root->resize_x;
1383  this->resize.step_height = this->nested_root->resize_y;
1384 
1385  /* Give focus to the opened window unless a dropdown menu has focus or a text box of the focused window has focus
1386  * (so we don't interrupt typing) unless the new window has a text box. */
1387  bool dropdown_active = _focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU;
1388  bool editbox_active = EditBoxInGlobalFocus() && this->nested_root->GetWidgetOfType(WWT_EDITBOX) == nullptr;
1389  if (!dropdown_active && !editbox_active) SetFocusedWindow(this);
1390 
1391  /* Insert the window into the correct location in the z-ordering. */
1392  BringWindowToFront(this, false);
1393 }
1394 
1402 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
1403 {
1404  this->left = x;
1405  this->top = y;
1406  this->width = sm_width;
1407  this->height = sm_height;
1408 }
1409 
1420 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
1421 {
1422  def_width = std::max(def_width, this->width); // Don't allow default size to be smaller than smallest size
1423  def_height = std::max(def_height, this->height);
1424  /* Try to make windows smaller when our window is too small.
1425  * w->(width|height) is normally the same as min_(width|height),
1426  * but this way the GUIs can be made a little more dynamic;
1427  * one can use the same spec for multiple windows and those
1428  * can then determine the real minimum size of the window. */
1429  if (this->width != def_width || this->height != def_height) {
1430  /* Think about the overlapping toolbars when determining the minimum window size */
1431  int free_height = _screen.height;
1432  const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
1433  if (wt != nullptr) free_height -= wt->height;
1435  if (wt != nullptr) free_height -= wt->height;
1436 
1437  int enlarge_x = std::max(std::min(def_width - this->width, _screen.width - this->width), 0);
1438  int enlarge_y = std::max(std::min(def_height - this->height, free_height - this->height), 0);
1439 
1440  /* X and Y has to go by step.. calculate it.
1441  * The cast to int is necessary else x/y are implicitly casted to
1442  * unsigned int, which won't work. */
1443  if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
1444  if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
1445 
1446  ResizeWindow(this, enlarge_x, enlarge_y, true, false);
1447  /* ResizeWindow() calls this->OnResize(). */
1448  } else {
1449  /* Always call OnResize; that way the scrollbars and matrices get initialized. */
1450  this->OnResize();
1451  }
1452 
1453  int nx = this->left;
1454  int ny = this->top;
1455 
1456  if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
1457 
1458  const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
1459  ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
1460  nx = std::max(nx, 0);
1461 
1462  if (this->viewport != nullptr) {
1463  this->viewport->left += nx - this->left;
1464  this->viewport->top += ny - this->top;
1465  }
1466  this->left = nx;
1467  this->top = ny;
1468 
1469  this->SetDirty();
1470 }
1471 
1484 static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
1485 {
1486  int right = width + left;
1487  int bottom = height + top;
1488 
1489  if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
1490 
1491  /* Make sure it is not obscured by any window. */
1492  for (const Window *w : Window::Iterate()) {
1493  if (w->window_class == WC_MAIN_WINDOW) continue;
1494 
1495  if (right > w->left &&
1496  w->left + w->width > left &&
1497  bottom > w->top &&
1498  w->top + w->height > top) {
1499  return false;
1500  }
1501  }
1502 
1503  pos.x = left;
1504  pos.y = top;
1505  return true;
1506 }
1507 
1520 static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolbar_y, Point &pos)
1521 {
1522  bool rtl = _current_text_dir == TD_RTL;
1523 
1524  /* Left part of the rectangle may be at most 1/4 off-screen,
1525  * right part of the rectangle may be at most 1/2 off-screen
1526  */
1527  if (rtl) {
1528  if (left < -(width >> 1) || left > _screen.width - (width >> 2)) return false;
1529  } else {
1530  if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
1531  }
1532 
1533  /* Bottom part of the rectangle may be at most 1/4 off-screen */
1534  if (top < toolbar_y || top > _screen.height - (height >> 2)) return false;
1535 
1536  /* Make sure it is not obscured by any window. */
1537  for (const Window *w : Window::Iterate()) {
1538  if (w->window_class == WC_MAIN_WINDOW) continue;
1539 
1540  if (left + width > w->left &&
1541  w->left + w->width > left &&
1542  top + height > w->top &&
1543  w->top + w->height > top) {
1544  return false;
1545  }
1546  }
1547 
1548  pos.x = left;
1549  pos.y = top;
1550  return true;
1551 }
1552 
1559 static Point GetAutoPlacePosition(int width, int height)
1560 {
1561  Point pt;
1562 
1563  bool rtl = _current_text_dir == TD_RTL;
1564 
1565  /* First attempt, try top-left of the screen */
1566  const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
1567  const int toolbar_y = main_toolbar != nullptr ? main_toolbar->height : 0;
1568  if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
1569 
1570  /* Second attempt, try around all existing windows.
1571  * The new window must be entirely on-screen, and not overlap with an existing window.
1572  * Eight starting points are tried, two at each corner.
1573  */
1574  for (const Window *w : Window::Iterate()) {
1575  if (w->window_class == WC_MAIN_WINDOW) continue;
1576 
1577  if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1578  if (IsGoodAutoPlace1(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1579  if (IsGoodAutoPlace1(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1580  if (IsGoodAutoPlace1(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1581  if (IsGoodAutoPlace1(w->left + w->width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1582  if (IsGoodAutoPlace1(w->left - width, w->top + w->height - height, width, height, toolbar_y, pt)) return pt;
1583  if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1584  if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height, width, height, toolbar_y, pt)) return pt;
1585  }
1586 
1587  /* Third attempt, try around all existing windows.
1588  * The new window may be partly off-screen, and must not overlap with an existing window.
1589  * Only four starting points are tried.
1590  */
1591  for (const Window *w : Window::Iterate()) {
1592  if (w->window_class == WC_MAIN_WINDOW) continue;
1593 
1594  if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt;
1595  if (IsGoodAutoPlace2(w->left - width, w->top, width, height, toolbar_y, pt)) return pt;
1596  if (IsGoodAutoPlace2(w->left, w->top + w->height, width, height, toolbar_y, pt)) return pt;
1597  if (IsGoodAutoPlace2(w->left, w->top - height, width, height, toolbar_y, pt)) return pt;
1598  }
1599 
1600  /* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
1601  * of the closebox
1602  */
1603  int left = rtl ? _screen.width - width : 0, top = toolbar_y;
1604  int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
1605  int offset_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1606 
1607 restart:
1608  for (const Window *w : Window::Iterate()) {
1609  if (w->left == left && w->top == top) {
1610  left += offset_x;
1611  top += offset_y;
1612  goto restart;
1613  }
1614  }
1615 
1616  pt.x = left;
1617  pt.y = top;
1618  return pt;
1619 }
1620 
1628 {
1629  const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
1630  assert(w != nullptr);
1631  Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
1632  return pt;
1633 }
1634 
1652 static Point LocalGetWindowPlacement(const WindowDesc &desc, int16_t sm_width, int16_t sm_height, int window_number)
1653 {
1654  Point pt;
1655  const Window *w;
1656 
1657  int16_t default_width = std::max(desc.GetDefaultWidth(), sm_width);
1658  int16_t default_height = std::max(desc.GetDefaultHeight(), sm_height);
1659 
1660  if (desc.parent_cls != WC_NONE && (w = FindWindowById(desc.parent_cls, window_number)) != nullptr) {
1661  bool rtl = _current_text_dir == TD_RTL;
1662  if (desc.parent_cls == WC_BUILD_TOOLBAR || desc.parent_cls == WC_SCEN_LAND_GEN) {
1663  pt.x = w->left + (rtl ? w->width - default_width : 0);
1664  pt.y = w->top + w->height;
1665  return pt;
1666  } else {
1667  /* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
1668  * - Y position: closebox of parent + closebox of child + statusbar
1669  * - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
1670  */
1671  int indent_y = std::max<int>(NWidgetLeaf::closebox_dimension.height, GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.captiontext.Vertical());
1672  if (w->top + 3 * indent_y < _screen.height) {
1673  pt.y = w->top + indent_y;
1674  int indent_close = NWidgetLeaf::closebox_dimension.width;
1675  int indent_resize = NWidgetLeaf::resizebox_dimension.width;
1676  if (_current_text_dir == TD_RTL) {
1677  pt.x = std::max(w->left + w->width - default_width - indent_close, 0);
1678  if (pt.x + default_width >= indent_close && pt.x + indent_resize <= _screen.width) return pt;
1679  } else {
1680  pt.x = std::min(w->left + indent_close, _screen.width - default_width);
1681  if (pt.x + default_width >= indent_resize && pt.x + indent_close <= _screen.width) return pt;
1682  }
1683  }
1684  }
1685  }
1686 
1687  switch (desc.default_pos) {
1688  case WDP_ALIGN_TOOLBAR: // Align to the toolbar
1689  return GetToolbarAlignedWindowPosition(default_width);
1690 
1691  case WDP_AUTO: // Find a good automatic position for the window
1692  return GetAutoPlacePosition(default_width, default_height);
1693 
1694  case WDP_CENTER: // Centre the window horizontally
1695  pt.x = (_screen.width - default_width) / 2;
1696  pt.y = (_screen.height - default_height) / 2;
1697  break;
1698 
1699  case WDP_MANUAL:
1700  pt.x = 0;
1701  pt.y = 0;
1702  break;
1703 
1704  default:
1705  NOT_REACHED();
1706  }
1707 
1708  return pt;
1709 }
1710 
1711 /* virtual */ Point Window::OnInitialPosition([[maybe_unused]]int16_t sm_width, [[maybe_unused]]int16_t sm_height, [[maybe_unused]]int window_number)
1712 {
1713  return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
1714 }
1715 
1724 {
1725  this->nested_root = MakeWindowNWidgetTree(this->window_desc.nwid_parts, &this->shade_select);
1726  this->nested_root->FillWidgetLookup(this->widget_lookup);
1727 }
1728 
1734 {
1735  this->InitializeData(window_number);
1736  this->ApplyDefaults();
1737  Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
1738  this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
1739  this->FindWindowPlacementAndResize(this->window_desc.GetDefaultWidth(), this->window_desc.GetDefaultHeight());
1740 }
1741 
1747 {
1748  this->CreateNestedTree();
1749  this->FinishInitNested(window_number);
1750 }
1751 
1756 Window::Window(WindowDesc &desc) : window_desc(desc), scale(_gui_scale), mouse_capture_widget(-1)
1757 {
1758  this->z_position = _z_windows.insert(_z_windows.end(), this);
1759 }
1760 
1768 Window *FindWindowFromPt(int x, int y)
1769 {
1770  for (Window *w : Window::IterateFromFront()) {
1771  if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
1772  return w;
1773  }
1774  }
1775 
1776  return nullptr;
1777 }
1778 
1783 {
1784  IConsoleClose();
1785 
1786  _focused_window = nullptr;
1787  _mouseover_last_w = nullptr;
1788  _last_scroll_window = nullptr;
1789  _scrolling_viewport = false;
1790  _mouse_hovering = false;
1791 
1793  NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
1794  NWidgetScrollbar::InvalidateDimensionCache();
1795 
1797 
1798  ShowFirstError();
1799 }
1800 
1805 {
1807 
1808  for (Window *w : Window::Iterate()) w->Close();
1809 
1811 
1812  assert(_z_windows.empty());
1813 }
1814 
1819 {
1821  InitWindowSystem();
1822  _thd.Reset();
1823 }
1824 
1825 static void DecreaseWindowCounters()
1826 {
1827  if (_scroller_click_timeout != 0) _scroller_click_timeout--;
1828 
1829  for (Window *w : Window::Iterate()) {
1830  if (_scroller_click_timeout == 0) {
1831  /* Unclick scrollbar buttons if they are pressed. */
1832  for (auto &pair : w->widget_lookup) {
1833  NWidgetBase *nwid = pair.second;
1834  if (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR) {
1835  NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
1838  w->mouse_capture_widget = -1;
1839  sb->SetDirty(w);
1840  }
1841  }
1842  }
1843  }
1844 
1845  /* Handle editboxes */
1846  for (auto &pair : w->querystrings) {
1847  pair.second->HandleEditBox(w, pair.first);
1848  }
1849 
1850  w->OnMouseLoop();
1851  }
1852 
1853  for (Window *w : Window::Iterate()) {
1854  if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
1855  CLRBITS(w->flags, WF_TIMEOUT);
1856 
1857  w->OnTimeout();
1858  w->RaiseButtons(true);
1859  }
1860  }
1861 }
1862 
1863 static void HandlePlacePresize()
1864 {
1865  if (_special_mouse_mode != WSM_PRESIZE) return;
1866 
1867  Window *w = _thd.GetCallbackWnd();
1868  if (w == nullptr) return;
1869 
1870  Point pt = GetTileBelowCursor();
1871  if (pt.x == -1) {
1872  _thd.selend.x = -1;
1873  return;
1874  }
1875 
1876  w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
1877 }
1878 
1884 {
1886 
1887  if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
1888 
1889  Window *w = _thd.GetCallbackWnd();
1890  if (w != nullptr) {
1891  /* Send an event in client coordinates. */
1892  Point pt;
1893  pt.x = _cursor.pos.x - w->left;
1894  pt.y = _cursor.pos.y - w->top;
1895  if (_left_button_down) {
1896  w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
1897  } else {
1898  w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
1899  }
1900  }
1901 
1902  if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
1903  return ES_HANDLED;
1904 }
1905 
1907 static void HandleMouseOver()
1908 {
1909  Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
1910 
1911  /* We changed window, put an OnMouseOver event to the last window */
1912  if (_mouseover_last_w != nullptr && _mouseover_last_w != w) {
1913  /* Reset mouse-over coordinates of previous window */
1914  Point pt = { -1, -1 };
1916  }
1917 
1918  /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
1919  _mouseover_last_w = w;
1920 
1921  if (w != nullptr) {
1922  /* send an event in client coordinates. */
1923  Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
1924  const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
1925  if (widget != nullptr) w->OnMouseOver(pt, widget->index);
1926  }
1927 }
1928 
1933 };
1934 
1945 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
1946 {
1947  if (v == nullptr) return;
1948 
1949  const int min_visible = rect.Height();
1950 
1951  int v_bottom = v->top + v->height - 1;
1952  int v_right = v->left + v->width - 1;
1953  int safe_y = (dir == PHD_UP) ? (v->top - min_visible - rect.top) : (v_bottom + min_visible - rect.bottom); // Compute safe vertical position.
1954 
1955  if (*ny + rect.top <= v->top - min_visible) return; // Above v is enough space
1956  if (*ny + rect.bottom >= v_bottom + min_visible) return; // Below v is enough space
1957 
1958  /* Vertically, the rectangle is hidden behind v. */
1959  if (*nx + rect.left + min_visible < v->left) { // At left of v.
1960  if (v->left < min_visible) *ny = safe_y; // But enough room, force it to a safe position.
1961  return;
1962  }
1963  if (*nx + rect.right - min_visible > v_right) { // At right of v.
1964  if (v_right > _screen.width - min_visible) *ny = safe_y; // Not enough room, force it to a safe position.
1965  return;
1966  }
1967 
1968  /* Horizontally also hidden, force movement to a safe area. */
1969  if (px + rect.left < v->left && v->left >= min_visible) { // Coming from the left, and enough room there.
1970  *nx = v->left - min_visible - rect.left;
1971  } else if (px + rect.right > v_right && v_right <= _screen.width - min_visible) { // Coming from the right, and enough room there.
1972  *nx = v_right + min_visible - rect.right;
1973  } else {
1974  *ny = safe_y;
1975  }
1976 }
1977 
1985 static void EnsureVisibleCaption(Window *w, int nx, int ny)
1986 {
1987  /* Search for the title bar rectangle. */
1988  const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
1989  if (caption != nullptr) {
1990  const Rect caption_rect = caption->GetCurrentRect();
1991 
1992  const int min_visible = caption_rect.Height();
1993 
1994  /* Make sure the window doesn't leave the screen */
1995  nx = Clamp(nx, min_visible - caption_rect.right, _screen.width - min_visible - caption_rect.left);
1996  ny = Clamp(ny, 0, _screen.height - min_visible);
1997 
1998  /* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
1999  PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
2000  PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
2001  }
2002 
2003  if (w->viewport != nullptr) {
2004  w->viewport->left += nx - w->left;
2005  w->viewport->top += ny - w->top;
2006  }
2007 
2008  w->left = nx;
2009  w->top = ny;
2010 }
2011 
2022 void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
2023 {
2024  if (delta_x != 0 || delta_y != 0) {
2025  if (clamp_to_screen) {
2026  /* Determine the new right/bottom position. If that is outside of the bounds of
2027  * the resolution clamp it in such a manner that it stays within the bounds. */
2028  int new_right = w->left + w->width + delta_x;
2029  int new_bottom = w->top + w->height + delta_y;
2030  if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x));
2031  if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
2032  }
2033 
2034  w->SetDirty();
2035 
2036  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);
2037  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);
2038  assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
2039  assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
2040 
2041  w->nested_root->AssignSizePosition(ST_RESIZE, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, _current_text_dir == TD_RTL);
2042  w->width = w->nested_root->current_x;
2043  w->height = w->nested_root->current_y;
2044  }
2045 
2046  EnsureVisibleCaption(w, w->left, w->top);
2047 
2048  /* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
2049  if (schedule_resize) {
2050  w->ScheduleResize();
2051  } else {
2052  w->OnResize();
2053  }
2054  w->SetDirty();
2055 }
2056 
2063 {
2065  return (w == nullptr) ? 0 : w->top + w->height;
2066 }
2067 
2074 {
2076  return (w == nullptr) ? _screen.height : w->top;
2077 }
2078 
2079 static bool _dragging_window;
2080 
2086 {
2087  /* Get out immediately if no window is being dragged at all. */
2088  if (!_dragging_window) return ES_NOT_HANDLED;
2089 
2090  /* If button still down, but cursor hasn't moved, there is nothing to do. */
2091  if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2092 
2093  /* Otherwise find the window... */
2094  for (Window *w : Window::Iterate()) {
2095  if (w->flags & WF_DRAGGING) {
2096  /* Stop the dragging if the left mouse button was released */
2097  if (!_left_button_down) {
2098  w->flags &= ~WF_DRAGGING;
2099  break;
2100  }
2101 
2102  w->SetDirty();
2103 
2104  int x = _cursor.pos.x + _drag_delta.x;
2105  int y = _cursor.pos.y + _drag_delta.y;
2106  int nx = x;
2107  int ny = y;
2108 
2112  int delta;
2113 
2114  for (const Window *v : Window::Iterate()) {
2115  if (v == w) continue; // Don't snap at yourself
2116 
2117  if (y + w->height > v->top && y < v->top + v->height) {
2118  /* Your left border <-> other right border */
2119  delta = abs(v->left + v->width - x);
2120  if (delta <= hsnap) {
2121  nx = v->left + v->width;
2122  hsnap = delta;
2123  }
2124 
2125  /* Your right border <-> other left border */
2126  delta = abs(v->left - x - w->width);
2127  if (delta <= hsnap) {
2128  nx = v->left - w->width;
2129  hsnap = delta;
2130  }
2131  }
2132 
2133  if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
2134  /* Your left border <-> other left border */
2135  delta = abs(v->left - x);
2136  if (delta <= hsnap) {
2137  nx = v->left;
2138  hsnap = delta;
2139  }
2140 
2141  /* Your right border <-> other right border */
2142  delta = abs(v->left + v->width - x - w->width);
2143  if (delta <= hsnap) {
2144  nx = v->left + v->width - w->width;
2145  hsnap = delta;
2146  }
2147  }
2148 
2149  if (x + w->width > v->left && x < v->left + v->width) {
2150  /* Your top border <-> other bottom border */
2151  delta = abs(v->top + v->height - y);
2152  if (delta <= vsnap) {
2153  ny = v->top + v->height;
2154  vsnap = delta;
2155  }
2156 
2157  /* Your bottom border <-> other top border */
2158  delta = abs(v->top - y - w->height);
2159  if (delta <= vsnap) {
2160  ny = v->top - w->height;
2161  vsnap = delta;
2162  }
2163  }
2164 
2165  if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
2166  /* Your top border <-> other top border */
2167  delta = abs(v->top - y);
2168  if (delta <= vsnap) {
2169  ny = v->top;
2170  vsnap = delta;
2171  }
2172 
2173  /* Your bottom border <-> other bottom border */
2174  delta = abs(v->top + v->height - y - w->height);
2175  if (delta <= vsnap) {
2176  ny = v->top + v->height - w->height;
2177  vsnap = delta;
2178  }
2179  }
2180  }
2181  }
2182 
2183  EnsureVisibleCaption(w, nx, ny);
2184 
2185  w->SetDirty();
2186  return ES_HANDLED;
2187  } else if (w->flags & WF_SIZING) {
2188  /* Stop the sizing if the left mouse button was released */
2189  if (!_left_button_down) {
2190  w->flags &= ~WF_SIZING;
2191  w->SetDirty();
2192  break;
2193  }
2194 
2195  /* Compute difference in pixels between cursor position and reference point in the window.
2196  * If resizing the left edge of the window, moving to the left makes the window bigger not smaller.
2197  */
2198  int x, y = _cursor.pos.y - _drag_delta.y;
2199  if (w->flags & WF_SIZING_LEFT) {
2200  x = _drag_delta.x - _cursor.pos.x;
2201  } else {
2202  x = _cursor.pos.x - _drag_delta.x;
2203  }
2204 
2205  /* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
2206  if (w->resize.step_width == 0) x = 0;
2207  if (w->resize.step_height == 0) y = 0;
2208 
2209  /* Check the resize button won't go past the bottom of the screen */
2210  if (w->top + w->height + y > _screen.height) {
2211  y = _screen.height - w->height - w->top;
2212  }
2213 
2214  /* X and Y has to go by step.. calculate it.
2215  * The cast to int is necessary else x/y are implicitly casted to
2216  * unsigned int, which won't work. */
2217  if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
2218  if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
2219 
2220  /* Check that we don't go below the minimum set size */
2221  if ((int)w->width + x < (int)w->nested_root->smallest_x) {
2222  x = w->nested_root->smallest_x - w->width;
2223  }
2224  if ((int)w->height + y < (int)w->nested_root->smallest_y) {
2225  y = w->nested_root->smallest_y - w->height;
2226  }
2227 
2228  /* Window already on size */
2229  if (x == 0 && y == 0) return ES_HANDLED;
2230 
2231  /* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2232  _drag_delta.y += y;
2233  if ((w->flags & WF_SIZING_LEFT) && x != 0) {
2234  _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
2235  w->SetDirty();
2236  w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
2237  /* ResizeWindow() below ensures marking new position as dirty. */
2238  } else {
2239  _drag_delta.x += x;
2240  }
2241 
2242  /* ResizeWindow sets both pre- and after-size to dirty for redrawal */
2243  ResizeWindow(w, x, y);
2244  return ES_HANDLED;
2245  }
2246  }
2247 
2248  _dragging_window = false;
2249  return ES_HANDLED;
2250 }
2251 
2256 static void StartWindowDrag(Window *w)
2257 {
2258  w->flags |= WF_DRAGGING;
2259  w->flags &= ~WF_CENTERED;
2260  _dragging_window = true;
2261 
2262  _drag_delta.x = w->left - _cursor.pos.x;
2263  _drag_delta.y = w->top - _cursor.pos.y;
2264 
2265  BringWindowToFront(w);
2266 }
2267 
2273 static void StartWindowSizing(Window *w, bool to_left)
2274 {
2275  w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
2276  w->flags &= ~WF_CENTERED;
2277  _dragging_window = true;
2278 
2279  _drag_delta.x = _cursor.pos.x;
2280  _drag_delta.y = _cursor.pos.y;
2281 
2282  BringWindowToFront(w);
2283 }
2284 
2290 {
2291  int i;
2293  bool rtl = false;
2294 
2295  if (sb->type == NWID_HSCROLLBAR) {
2296  i = _cursor.pos.x - _cursorpos_drag_start.x;
2297  rtl = _current_text_dir == TD_RTL;
2298  } else {
2299  i = _cursor.pos.y - _cursorpos_drag_start.y;
2300  }
2301 
2302  if (sb->disp_flags & ND_SCROLLBAR_BTN) {
2303  if (_scroller_click_timeout == 1) {
2304  _scroller_click_timeout = 3;
2305  if (sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1)) w->SetDirty();
2306  }
2307  return;
2308  }
2309 
2310  /* Find the item we want to move to. SetPosition will make sure it's inside bounds. */
2311  int pos = RoundDivSU((i + _scrollbar_start_pos) * sb->GetCount(), _scrollbar_size);
2312  if (rtl) pos = sb->GetCount() - sb->GetCapacity() - pos;
2313  if (sb->SetPosition(pos)) w->SetDirty();
2314 }
2315 
2321 {
2322  for (Window *w : Window::Iterate()) {
2323  if (w->mouse_capture_widget >= 0) {
2324  /* Abort if no button is clicked any more. */
2325  if (!_left_button_down) {
2327  w->mouse_capture_widget = -1;
2328  return ES_HANDLED;
2329  }
2330 
2331  /* Handle scrollbar internally, or dispatch click event */
2333  if (type == NWID_VSCROLLBAR || type == NWID_HSCROLLBAR) {
2335  } else {
2336  /* If cursor hasn't moved, there is nothing to do. */
2337  if (_cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
2338 
2339  Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
2340  w->OnClick(pt, w->mouse_capture_widget, 0);
2341  }
2342  return ES_HANDLED;
2343  }
2344  }
2345 
2346  return ES_NOT_HANDLED;
2347 }
2348 
2354 {
2355  bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
2356 
2357  if (!_scrolling_viewport) return ES_NOT_HANDLED;
2358 
2359  /* When we don't have a last scroll window we are starting to scroll.
2360  * When the last scroll window and this are not the same we went
2361  * outside of the window and should not left-mouse scroll anymore. */
2362  if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
2363 
2365  _cursor.fix_at = false;
2366  _scrolling_viewport = false;
2367  _last_scroll_window = nullptr;
2368  return ES_NOT_HANDLED;
2369  }
2370 
2372  /* If the main window is following a vehicle, then first let go of it! */
2374  ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true); // This also resets follow_vehicle
2375  return ES_NOT_HANDLED;
2376  }
2377 
2378  Point delta;
2379  if (scrollwheel_scrolling) {
2380  /* We are using scrollwheels for scrolling */
2381  delta.x = _cursor.h_wheel;
2382  delta.y = _cursor.v_wheel;
2383  _cursor.v_wheel = 0;
2384  _cursor.h_wheel = 0;
2385  } else {
2387  delta.x = -_cursor.delta.x;
2388  delta.y = -_cursor.delta.y;
2389  } else {
2390  delta.x = _cursor.delta.x;
2391  delta.y = _cursor.delta.y;
2392  }
2393  }
2394 
2395  /* Create a scroll-event and send it to the window */
2396  if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
2397 
2398  _cursor.delta.x = 0;
2399  _cursor.delta.y = 0;
2400  return ES_HANDLED;
2401 }
2402 
2414 {
2415  bool bring_to_front = false;
2416 
2417  if (w->window_class == WC_MAIN_WINDOW ||
2418  IsVitalWindow(w) ||
2419  w->window_class == WC_TOOLTIPS ||
2421  return true;
2422  }
2423 
2424  /* Use unshaded window size rather than current size for shaded windows. */
2425  int w_width = w->width;
2426  int w_height = w->height;
2427  if (w->IsShaded()) {
2428  w_width = w->unshaded_size.width;
2429  w_height = w->unshaded_size.height;
2430  }
2431 
2433  ++it;
2434  for (; !it.IsEnd(); ++it) {
2435  Window *u = *it;
2436  /* A modal child will prevent the activation of the parent window */
2437  if (u->parent == w && (u->window_desc.flags & WDF_MODAL)) {
2438  u->SetWhiteBorder();
2439  u->SetDirty();
2440  return false;
2441  }
2442 
2443  if (u->window_class == WC_MAIN_WINDOW ||
2444  IsVitalWindow(u) ||
2445  u->window_class == WC_TOOLTIPS ||
2447  continue;
2448  }
2449 
2450  /* Window sizes don't interfere, leave z-order alone */
2451  if (w->left + w_width <= u->left ||
2452  u->left + u->width <= w->left ||
2453  w->top + w_height <= u->top ||
2454  u->top + u->height <= w->top) {
2455  continue;
2456  }
2457 
2458  bring_to_front = true;
2459  }
2460 
2461  if (bring_to_front) BringWindowToFront(w);
2462  return true;
2463 }
2464 
2473 EventState Window::HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
2474 {
2475  QueryString *query = this->GetQueryString(wid);
2476  if (query == nullptr) return ES_NOT_HANDLED;
2477 
2478  int action = QueryString::ACTION_NOTHING;
2479 
2480  switch (query->text.HandleKeyPress(key, keycode)) {
2481  case HKPR_EDITING:
2482  this->SetWidgetDirty(wid);
2483  this->OnEditboxChanged(wid);
2484  break;
2485 
2486  case HKPR_CURSOR:
2487  this->SetWidgetDirty(wid);
2488  /* For the OSK also invalidate the parent window */
2489  if (this->window_class == WC_OSK) this->InvalidateData();
2490  break;
2491 
2492  case HKPR_CONFIRM:
2493  if (this->window_class == WC_OSK) {
2494  this->OnClick(Point(), WID_OSK_OK, 1);
2495  } else if (query->ok_button >= 0) {
2496  this->OnClick(Point(), query->ok_button, 1);
2497  } else {
2498  action = query->ok_button;
2499  }
2500  break;
2501 
2502  case HKPR_CANCEL:
2503  if (this->window_class == WC_OSK) {
2504  this->OnClick(Point(), WID_OSK_CANCEL, 1);
2505  } else if (query->cancel_button >= 0) {
2506  this->OnClick(Point(), query->cancel_button, 1);
2507  } else {
2508  action = query->cancel_button;
2509  }
2510  break;
2511 
2512  case HKPR_NOT_HANDLED:
2513  return ES_NOT_HANDLED;
2514 
2515  default: break;
2516  }
2517 
2518  switch (action) {
2520  this->UnfocusFocusedWidget();
2521  break;
2522 
2524  if (query->text.bytes <= 1) {
2525  /* If already empty, unfocus instead */
2526  this->UnfocusFocusedWidget();
2527  } else {
2528  query->text.DeleteAll();
2529  this->SetWidgetDirty(wid);
2530  this->OnEditboxChanged(wid);
2531  }
2532  break;
2533 
2534  default:
2535  break;
2536  }
2537 
2538  return ES_HANDLED;
2539 }
2540 
2545 void HandleToolbarHotkey(int hotkey)
2546 {
2547  assert(HasModalProgress() || IsLocalCompany());
2548 
2550  if (w != nullptr) {
2551  if (w->window_desc.hotkeys != nullptr) {
2552  if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2553  }
2554  }
2555 }
2556 
2562 void HandleKeypress(uint keycode, char32_t key)
2563 {
2564  /* World generation is multithreaded and messes with companies.
2565  * But there is no company related window open anyway, so _current_company is not used. */
2566  assert(HasModalProgress() || IsLocalCompany());
2567 
2568  /*
2569  * The Unicode standard defines an area called the private use area. Code points in this
2570  * area are reserved for private use and thus not portable between systems. For instance,
2571  * Apple defines code points for the arrow keys in this area, but these are only printable
2572  * on a system running OS X. We don't want these keys to show up in text fields and such,
2573  * and thus we have to clear the unicode character when we encounter such a key.
2574  */
2575  if (key >= 0xE000 && key <= 0xF8FF) key = 0;
2576 
2577  /*
2578  * If both key and keycode is zero, we don't bother to process the event.
2579  */
2580  if (key == 0 && keycode == 0) return;
2581 
2582  /* Check if the focused window has a focused editbox */
2583  if (EditBoxInGlobalFocus()) {
2584  /* All input will in this case go to the focused editbox */
2585  if (_focused_window->window_class == WC_CONSOLE) {
2586  if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
2587  } else {
2588  if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->index, key, keycode) == ES_HANDLED) return;
2589  }
2590  }
2591 
2592  /* Call the event, start with the uppermost window, but ignore the toolbar. */
2593  for (Window *w : Window::IterateFromFront()) {
2594  if (w->window_class == WC_MAIN_TOOLBAR) continue;
2595  if (w->window_desc.hotkeys != nullptr) {
2596  int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2597  if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2598  }
2599  if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2600  }
2601 
2603  /* When there is no toolbar w is null, check for that */
2604  if (w != nullptr) {
2605  if (w->window_desc.hotkeys != nullptr) {
2606  int hotkey = w->window_desc.hotkeys->CheckMatch(keycode);
2607  if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
2608  }
2609  if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
2610  }
2611 
2612  HandleGlobalHotkeys(key, keycode);
2613 }
2614 
2619 {
2620  /* Call the event, start with the uppermost window. */
2621  for (Window *w : Window::IterateFromFront()) {
2622  if (w->OnCTRLStateChange() == ES_HANDLED) return;
2623  }
2624 }
2625 
2631 /* virtual */ void Window::InsertTextString(WidgetID wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
2632 {
2633  QueryString *query = this->GetQueryString(wid);
2634  if (query == nullptr) return;
2635 
2636  if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
2637  this->SetWidgetDirty(wid);
2638  this->OnEditboxChanged(wid);
2639  }
2640 }
2641 
2648 void HandleTextInput(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
2649 {
2650  if (!EditBoxInGlobalFocus()) return;
2651 
2652  _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->index, str, marked, caret, insert_location, replacement_end);
2653 }
2654 
2662 
2667 static void HandleAutoscroll()
2668 {
2669  if (_game_mode == GM_MENU || HasModalProgress()) return;
2671  if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
2672 
2673  int x = _cursor.pos.x;
2674  int y = _cursor.pos.y;
2675  Window *w = FindWindowFromPt(x, y);
2676  if (w == nullptr || w->flags & WF_DISABLE_VP_SCROLL) return;
2678 
2679  Viewport *vp = IsPtInWindowViewport(w, x, y);
2680  if (vp == nullptr) return;
2681 
2682  x -= vp->left;
2683  y -= vp->top;
2684 
2685  /* here allows scrolling in both x and y axis */
2686  /* If we succeed at scrolling in any direction, stop following a vehicle. */
2687  static const int SCROLLSPEED = 3;
2688  if (x - 15 < 0) {
2690  w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
2691  } else if (15 - (vp->width - x) > 0) {
2693  w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
2694  }
2695  if (y - 15 < 0) {
2697  w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
2698  } else if (15 - (vp->height - y) > 0) {
2700  w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
2701  }
2702 }
2703 
2704 enum MouseClick {
2705  MC_NONE = 0,
2706  MC_LEFT,
2707  MC_RIGHT,
2708  MC_DOUBLE_LEFT,
2709  MC_HOVER,
2710 };
2711 
2712 static constexpr int MAX_OFFSET_DOUBLE_CLICK = 5;
2713 static constexpr int MAX_OFFSET_HOVER = 5;
2714 
2716 
2717 const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500);
2718 
2719 static void ScrollMainViewport(int x, int y)
2720 {
2721  if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2722  Window *w = GetMainWindow();
2725  }
2726 }
2727 
2737 static const int8_t scrollamt[16][2] = {
2738  { 0, 0},
2739  {-2, 0},
2740  { 0, -2},
2741  {-2, -1},
2742  { 2, 0},
2743  { 0, 0},
2744  { 2, -1},
2745  { 0, -2},
2746  { 0, 2},
2747  {-2, 1},
2748  { 0, 0},
2749  {-2, 0},
2750  { 2, 1},
2751  { 0, 2},
2752  { 2, 0},
2753  { 0, 0},
2754 };
2755 
2756 static void HandleKeyScrolling()
2757 {
2758  /*
2759  * Check that any of the dirkeys is pressed and that the focused window
2760  * doesn't have an edit-box as focused widget.
2761  */
2762  if (_dirkeys && !EditBoxInGlobalFocus()) {
2763  int factor = _shift_pressed ? 50 : 10;
2764 
2765  if (_game_mode != GM_MENU && _game_mode != GM_BOOTSTRAP) {
2766  /* Key scrolling stops following a vehicle. */
2768  }
2769 
2770  ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
2771  }
2772 }
2773 
2774 static void MouseLoop(MouseClick click, int mousewheel)
2775 {
2776  /* World generation is multithreaded and messes with companies.
2777  * But there is no company related window open anyway, so _current_company is not used. */
2778  assert(HasModalProgress() || IsLocalCompany());
2779 
2780  HandlePlacePresize();
2782 
2783  if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
2784  if (HandleMouseDragDrop() == ES_HANDLED) return;
2785  if (HandleWindowDragging() == ES_HANDLED) return;
2786  if (HandleActiveWidget() == ES_HANDLED) return;
2787  if (HandleViewportScroll() == ES_HANDLED) return;
2788 
2789  HandleMouseOver();
2790 
2791  bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
2792  if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
2793 
2794  int x = _cursor.pos.x;
2795  int y = _cursor.pos.y;
2796  Window *w = FindWindowFromPt(x, y);
2797  if (w == nullptr) return;
2798 
2799  if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
2800  Viewport *vp = IsPtInWindowViewport(w, x, y);
2801 
2802  /* Don't allow any action in a viewport if either in menu or when having a modal progress window */
2803  if (vp != nullptr && (_game_mode == GM_MENU || HasModalProgress())) return;
2804 
2805  if (mousewheel != 0) {
2806  /* Send mousewheel event to window, unless we're scrolling a viewport or the map */
2807  if (!scrollwheel_scrolling || (vp == nullptr && w->window_class != WC_SMALLMAP)) w->OnMouseWheel(mousewheel);
2808 
2809  /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2810  if (vp == nullptr) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
2811  }
2812 
2813  if (vp != nullptr) {
2814  if (scrollwheel_scrolling && !(w->flags & WF_DISABLE_VP_SCROLL)) {
2815  _scrolling_viewport = true;
2816  _cursor.fix_at = true;
2817  return;
2818  }
2819 
2820  switch (click) {
2821  case MC_DOUBLE_LEFT:
2822  case MC_LEFT:
2823  if (HandleViewportClicked(vp, x, y)) return;
2824  if (!(w->flags & WF_DISABLE_VP_SCROLL) &&
2826  _scrolling_viewport = true;
2827  _cursor.fix_at = false;
2828  return;
2829  }
2830  break;
2831 
2832  case MC_RIGHT:
2833  if (!(w->flags & WF_DISABLE_VP_SCROLL) &&
2835  _scrolling_viewport = true;
2838  DispatchRightClickEvent(w, x - w->left, y - w->top);
2839  return;
2840  }
2841  break;
2842 
2843  default:
2844  break;
2845  }
2846  }
2847 
2848  switch (click) {
2849  case MC_LEFT:
2850  case MC_DOUBLE_LEFT:
2851  DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
2852  return;
2853 
2854  default:
2855  if (!scrollwheel_scrolling || w == nullptr || w->window_class != WC_SMALLMAP) break;
2856  /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2857  * Simulate a right button click so we can get started. */
2858  [[fallthrough]];
2859 
2860  case MC_RIGHT:
2861  DispatchRightClickEvent(w, x - w->left, y - w->top);
2862  return;
2863 
2864  case MC_HOVER:
2865  DispatchHoverEvent(w, x - w->left, y - w->top);
2866  break;
2867  }
2868 
2869  /* We're not doing anything with 2D scrolling, so reset the value. */
2870  _cursor.h_wheel = 0;
2871  _cursor.v_wheel = 0;
2872 }
2873 
2878 {
2879  /* World generation is multithreaded and messes with companies.
2880  * But there is no company related window open anyway, so _current_company is not used. */
2881  assert(HasModalProgress() || IsLocalCompany());
2882 
2883  /* Handle sprite picker before any GUI interaction */
2884  if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _input_events_this_tick == 0) {
2885  /* We are done with the last draw-frame, so we know what sprites we
2886  * clicked on. Reset the picker mode and invalidate the window. */
2887  _newgrf_debug_sprite_picker.mode = SPM_NONE;
2889  }
2890 
2891  static std::chrono::steady_clock::time_point double_click_time = {};
2892  static Point double_click_pos = {0, 0};
2893 
2894  /* Mouse event? */
2895  MouseClick click = MC_NONE;
2897  click = MC_LEFT;
2898  if (std::chrono::steady_clock::now() <= double_click_time + TIME_BETWEEN_DOUBLE_CLICK &&
2899  double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
2900  double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
2901  click = MC_DOUBLE_LEFT;
2902  }
2903  double_click_time = std::chrono::steady_clock::now();
2904  double_click_pos = _cursor.pos;
2905  _left_button_clicked = true;
2907  } else if (_right_button_clicked) {
2908  _right_button_clicked = false;
2909  click = MC_RIGHT;
2911  }
2912 
2913  int mousewheel = 0;
2914  if (_cursor.wheel) {
2915  mousewheel = _cursor.wheel;
2916  _cursor.wheel = 0;
2918  }
2919 
2920  static std::chrono::steady_clock::time_point hover_time = {};
2921  static Point hover_pos = {0, 0};
2922 
2924  if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
2925  hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
2926  hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
2927  hover_pos = _cursor.pos;
2928  hover_time = std::chrono::steady_clock::now();
2929  _mouse_hovering = false;
2930  } else if (!_mouse_hovering) {
2931  if (std::chrono::steady_clock::now() > hover_time + std::chrono::milliseconds(_settings_client.gui.hover_delay_ms)) {
2932  click = MC_HOVER;
2934  _mouse_hovering = true;
2935  hover_time = std::chrono::steady_clock::now();
2936  }
2937  }
2938  }
2939 
2940  if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
2941  /* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
2943  _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
2945  _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
2947  } else {
2948  MouseLoop(click, mousewheel);
2949  }
2950 
2951  /* We have moved the mouse the required distance,
2952  * no need to move it at any later time. */
2953  _cursor.delta.x = 0;
2954  _cursor.delta.y = 0;
2955 }
2956 
2960 static void CheckSoftLimit()
2961 {
2962  if (_settings_client.gui.window_soft_limit == 0) return;
2963 
2964  for (;;) {
2965  uint deletable_count = 0;
2966  Window *last_deletable = nullptr;
2967  for (Window *w : Window::IterateFromFront()) {
2968  if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
2969 
2970  last_deletable = w;
2971  deletable_count++;
2972  }
2973 
2974  /* We've not reached the soft limit yet. */
2975  if (deletable_count <= _settings_client.gui.window_soft_limit) break;
2976 
2977  assert(last_deletable != nullptr);
2978  last_deletable->Close();
2979  }
2980 }
2981 
2986 {
2987  /* World generation is multithreaded and messes with companies.
2988  * But there is no company related window open anyway, so _current_company is not used. */
2989  assert(HasModalProgress() || IsLocalCompany());
2990 
2991  CheckSoftLimit();
2992 
2993  /* Process scheduled window deletion. */
2995 
2996  if (_input_events_this_tick != 0) {
2997  /* The input loop is called only once per GameLoop() - so we can clear the counter here */
2999  /* there were some inputs this tick, don't scroll ??? */
3000  return;
3001  }
3002 
3003  /* HandleMouseEvents was already called for this tick */
3005 }
3006 
3010 void CallWindowRealtimeTickEvent(uint delta_ms)
3011 {
3012  for (Window *w : Window::Iterate()) {
3013  w->OnRealtimeTick(delta_ms);
3014  }
3015 }
3016 
3018 static IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
3019  extern int _caret_timer;
3020  _caret_timer += 3;
3021  CursorTick();
3022 
3023  HandleKeyScrolling();
3024  HandleAutoscroll();
3025  DecreaseWindowCounters();
3026 });
3027 
3029 static IntervalTimer<TimerWindow> highlight_interval(std::chrono::milliseconds(450), [](auto) {
3031 });
3032 
3034 static IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
3035  if (_network_dedicated) return;
3036 
3037  for (Window *w : Window::Iterate()) {
3038  if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
3039  CLRBITS(w->flags, WF_WHITE_BORDER);
3040  w->SetDirty();
3041  }
3042  }
3043 });
3044 
3049 {
3050  static auto last_time = std::chrono::steady_clock::now();
3051  auto now = std::chrono::steady_clock::now();
3052  auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
3053 
3054  if (delta_ms.count() == 0) return;
3055 
3056  last_time = now;
3057 
3058  PerformanceMeasurer framerate(PFE_DRAWING);
3060 
3062 
3064  CallWindowRealtimeTickEvent(delta_ms.count());
3065 
3066  /* Process invalidations before anything else. */
3067  for (Window *w : Window::Iterate()) {
3071  }
3072 
3073  /* Skip the actual drawing on dedicated servers without screen.
3074  * But still empty the invalidation queues above. */
3075  if (_network_dedicated) return;
3076 
3077  DrawDirtyBlocks();
3078 
3079  for (Window *w : Window::Iterate()) {
3080  /* Update viewport only if window is not shaded. */
3081  if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
3082  }
3084  /* Redraw mouse cursor in case it was hidden */
3085  DrawMouseCursor();
3086 }
3087 
3094 {
3095  for (const Window *w : Window::Iterate()) {
3096  if (w->window_class == cls && w->window_number == number) w->SetDirty();
3097  }
3098 }
3099 
3107 {
3108  for (const Window *w : Window::Iterate()) {
3109  if (w->window_class == cls && w->window_number == number) {
3110  w->SetWidgetDirty(widget_index);
3111  }
3112  }
3113 }
3114 
3120 {
3121  for (const Window *w : Window::Iterate()) {
3122  if (w->window_class == cls) w->SetDirty();
3123  }
3124 }
3125 
3130 {
3131  this->scheduled_resize = true;
3132 }
3133 
3138 {
3139  /* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
3140  while (this->scheduled_resize) {
3141  this->scheduled_resize = false;
3142  this->OnResize();
3143  }
3144 }
3145 
3151 void Window::InvalidateData(int data, bool gui_scope)
3152 {
3153  this->SetDirty();
3154  if (!gui_scope) {
3155  /* Schedule GUI-scope invalidation for next redraw. */
3156  this->scheduled_invalidation_data.push_back(data);
3157  }
3158  this->OnInvalidateData(data, gui_scope);
3159 }
3160 
3165 {
3166  for (int data : this->scheduled_invalidation_data) {
3167  if (this->window_class == WC_INVALID) break;
3168  this->OnInvalidateData(data, true);
3169  }
3170  this->scheduled_invalidation_data.clear();
3171 }
3172 
3177 {
3178  if ((this->flags & WF_HIGHLIGHTED) == 0) return;
3179 
3180  for (const auto &pair : this->widget_lookup) {
3181  if (pair.second->IsHighlighted()) pair.second->SetDirty(this);
3182  }
3183 }
3184 
3211 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
3212 {
3213  for (Window *w : Window::Iterate()) {
3214  if (w->window_class == cls && w->window_number == number) {
3215  w->InvalidateData(data, gui_scope);
3216  }
3217  }
3218 }
3219 
3228 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
3229 {
3230  for (Window *w : Window::Iterate()) {
3231  if (w->window_class == cls) {
3232  w->InvalidateData(data, gui_scope);
3233  }
3234  }
3235 }
3236 
3241 {
3242  for (Window *w : Window::Iterate()) {
3243  w->OnGameTick();
3244  }
3245 }
3246 
3254 {
3255  /* Note: the container remains stable, even when deleting windows. */
3256  for (Window *w : Window::Iterate()) {
3257  if ((w->window_desc.flags & WDF_NO_CLOSE) == 0 &&
3258  (w->flags & WF_STICKY) == 0) { // do not delete windows which are 'pinned'
3259 
3260  w->Close();
3261  }
3262  }
3263 }
3264 
3273 {
3274  /* Note: the container remains stable, even when closing windows. */
3275  for (Window *w : Window::Iterate()) {
3276  if ((w->window_desc.flags & WDF_NO_CLOSE) == 0) {
3277  w->Close();
3278  }
3279  }
3280 }
3281 
3286 {
3288  InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
3289  InvalidateWindowData(WC_MESSAGE_HISTORY, 0); // invalidate the message history
3290  CloseWindowById(WC_NEWS_WINDOW, 0); // close newspaper or general message window if shown
3291 }
3292 
3298 {
3299  /* Note: the container remains stable, even when deleting windows. */
3300  for (Window *w : Window::Iterate()) {
3301  if (w->window_desc.flags & WDF_CONSTRUCTION) {
3302  w->Close();
3303  }
3304  }
3305 
3306  for (const Window *w : Window::Iterate()) w->SetDirty();
3307 }
3308 
3311 {
3314 }
3315 
3316 void ReInitWindow(Window *w, bool zoom_changed)
3317 {
3318  if (w == nullptr) return;
3319  if (zoom_changed) {
3320  w->nested_root->AdjustPaddingForZoom();
3321  w->UpdateQueryStringSize();
3322  }
3323  w->ReInit();
3324 }
3325 
3327 void ReInitAllWindows(bool zoom_changed)
3328 {
3330  NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets.
3331  NWidgetScrollbar::InvalidateDimensionCache();
3332 
3334 
3335  /* When _gui_zoom has changed, we need to resize toolbar and statusbar first,
3336  * so EnsureVisibleCaption uses the updated size information. */
3337  ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed);
3338  ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed);
3339  for (Window *w : Window::Iterate()) {
3340  if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue;
3341  ReInitWindow(w, zoom_changed);
3342  }
3343 
3346 
3347  /* Make sure essential parts of all windows are visible */
3348  RelocateAllWindows(_screen.width, _screen.height);
3350 }
3351 
3359 static int PositionWindow(Window *w, WindowClass clss, int setting)
3360 {
3361  if (w == nullptr || w->window_class != clss) {
3362  w = FindWindowById(clss, 0);
3363  }
3364  if (w == nullptr) return 0;
3365 
3366  int old_left = w->left;
3367  switch (setting) {
3368  case 1: w->left = (_screen.width - w->width) / 2; break;
3369  case 2: w->left = _screen.width - w->width; break;
3370  default: w->left = 0; break;
3371  }
3372  if (w->viewport != nullptr) w->viewport->left += w->left - old_left;
3373  AddDirtyBlock(0, w->top, _screen.width, w->top + w->height); // invalidate the whole row
3374  return w->left;
3375 }
3376 
3383 {
3384  Debug(misc, 5, "Repositioning Main Toolbar...");
3386 }
3387 
3394 {
3395  Debug(misc, 5, "Repositioning statusbar...");
3397 }
3398 
3405 {
3406  Debug(misc, 5, "Repositioning news message...");
3408 }
3409 
3416 {
3417  Debug(misc, 5, "Repositioning network chat window...");
3419 }
3420 
3421 
3427 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
3428 {
3429  for (const Window *w : Window::Iterate()) {
3430  if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
3431  w->viewport->follow_vehicle = to_index;
3432  w->SetDirty();
3433  }
3434  }
3435 }
3436 
3437 
3443 void RelocateAllWindows(int neww, int newh)
3444 {
3446 
3447  /* Reposition toolbar then status bar before other all windows. */
3448  if (Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); wt != nullptr) {
3449  ResizeWindow(wt, std::min<uint>(neww, _toolbar_width) - wt->width, 0, false);
3450  wt->left = PositionMainToolbar(wt);
3451  }
3452 
3453  if (Window *ws = FindWindowById(WC_STATUS_BAR, 0); ws != nullptr) {
3454  ResizeWindow(ws, std::min<uint>(neww, _toolbar_width) - ws->width, 0, false);
3455  ws->top = newh - ws->height;
3456  ws->left = PositionStatusbar(ws);
3457  }
3458 
3459  for (Window *w : Window::Iterate()) {
3460  int left, top;
3461  /* XXX - this probably needs something more sane. For example specifying
3462  * in a 'backup'-desc that the window should always be centered. */
3463  switch (w->window_class) {
3464  case WC_MAIN_WINDOW:
3465  case WC_BOOTSTRAP:
3466  case WC_HIGHSCORE:
3467  case WC_ENDSCREEN:
3468  ResizeWindow(w, neww, newh);
3469  continue;
3470 
3471  case WC_MAIN_TOOLBAR:
3472  case WC_STATUS_BAR:
3473  continue;
3474 
3475  case WC_NEWS_WINDOW:
3476  top = newh - w->height;
3477  left = PositionNewsMessage(w);
3478  break;
3479 
3480  case WC_SEND_NETWORK_MSG:
3481  ResizeWindow(w, std::min<uint>(neww, _toolbar_width) - w->width, 0, false);
3482 
3483  top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
3484  left = PositionNetworkChatWindow(w);
3485  break;
3486 
3487  case WC_CONSOLE:
3488  IConsoleResize(w);
3489  continue;
3490 
3491  default: {
3492  if (w->flags & WF_CENTERED) {
3493  top = (newh - w->height) >> 1;
3494  left = (neww - w->width) >> 1;
3495  break;
3496  }
3497 
3498  left = w->left;
3499  if (left + (w->width >> 1) >= neww) left = neww - w->width;
3500  if (left < 0) left = 0;
3501 
3502  top = w->top;
3503  if (top + (w->height >> 1) >= newh) top = newh - w->height;
3504  break;
3505  }
3506  }
3507 
3508  EnsureVisibleCaption(w, left, top);
3509  }
3510 }
3511 
3516 void PickerWindowBase::Close([[maybe_unused]] int data)
3517 {
3519  this->Window::Close();
3520 }
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
#define CLRBITS(x, y)
Clears several bits in a variable.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition: factory.hpp:138
How all blitters should look like.
Definition: base.hpp:29
virtual void * MoveTo(void *video, int x, int y)=0
Move the destination pointer the requested amount x and y, keeping in mind any pitch and bpp of the r...
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
Definition: game_core.cpp:146
Baseclass for nested widgets.
Definition: widget_type.h:144
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition: widget.cpp:903
WidgetType type
Type of the widget / nested widget.
Definition: widget_type.h:232
int pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:249
int pos_x
Horizontal position of top-left corner of the widget in the window.
Definition: widget_type.h:248
uint resize_y
Vertical resize step (0 means not resizable).
Definition: widget_type.h:236
Base class for a 'real' widget.
Definition: widget_type.h:370
bool IsDisabled() const
Return whether the widget is disabled.
Definition: widget_type.h:449
NWidgetDisplay disp_flags
Flags that affect display and interaction with the widget.
Definition: widget_type.h:390
const WidgetID index
Index of the nested widget (-1 means 'not used').
Definition: widget_type.h:392
WidgetID scrollbar_index
Index of an attached scrollbar.
Definition: widget_type.h:395
void SetLowered(bool lowered)
Lower or raise the widget.
Definition: widget_type.h:428
StringID tool_tip
Tooltip of the widget.
Definition: widget_type.h:394
bool IsLowered() const
Return whether the widget is lowered.
Definition: widget_type.h:434
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition: widget.cpp:2537
static Dimension resizebox_dimension
Cached size of a resizebox widget.
Definition: widget_type.h:928
static Dimension closebox_dimension
Cached size of a closebox widget.
Definition: widget_type.h:929
Nested widget to display and control a scrollbar in a window.
Definition: widget_type.h:896
int shown_plane
Plane being displayed (for NWID_SELECTION only).
Definition: widget_type.h:512
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition: widget.cpp:1342
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([[maybe_unused]] int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition: window.cpp:3516
Scrollbar data structure.
Definition: widget_type.h:694
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:731
bool UpdatePosition(int difference, ScrollbarStepping unit=SS_SMALL)
Updates the position of the first visible element by the given amount.
Definition: widget_type.h:824
bool SetPosition(size_type position)
Sets the position of the first visible element.
Definition: widget_type.h:810
size_type GetCount() const
Gets the number of elements in the list.
Definition: widget_type.h:722
static bool Elapsed(TElapsed value)
Called when time for this timer elapsed.
virtual void EditBoxLostFocus()
An edit box lost the input focus.
virtual void EditBoxGainedFocus()
An edit box gained the input focus.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition: window_gui.h:68
Functions related to companies.
bool IsLocalCompany()
Is the current company the local company?
Definition: company_func.h:47
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ INVALID_OWNER
An invalid owner.
Definition: company_type.h:29
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,...)
Ouptut 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...
Definition: depot_gui.cpp:218
Functions related to errors.
void UnshowCriticalError()
Unshow the critical error.
Definition: error_gui.cpp:349
void ShowFirstError()
Show the first error of the queue.
Definition: error_gui.cpp:335
Factory to 'query' all available blitters.
@ NO_DIRECTORY
A path without any base directory.
Definition: fileio_type.h:133
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition: fontcache.cpp:77
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:39
bool _left_button_down
Is left mouse button pressed?
Definition: gfx.cpp:41
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:38
uint8_t _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
Definition: gfx.cpp:34
bool _left_button_clicked
Is left mouse button clicked?
Definition: gfx.cpp:42
bool _right_button_clicked
Is right mouse button clicked?
Definition: gfx.cpp:44
bool _right_button_down
Is right mouse button pressed?
Definition: gfx.cpp:43
int _gui_scale
GUI scale, 100 is 100%.
Definition: gfx.cpp:63
Functions related to the gfx engine.
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:209
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition: gfx_type.h:260
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:3258
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition: window.cpp:940
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:1486
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition: gfx.cpp:1397
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition: gfx.cpp:1529
Hotkey related functions.
Types related to reading/writing '*.ini' files.
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition: map_func.h:404
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.
Definition: math_func.hpp:252
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.
Definition: math_func.hpp:342
constexpr uint Ceil(uint a, uint b)
Computes ceil(a / b) * b for non-negative a and b.
Definition: math_func.hpp:331
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, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
Shows a tooltip.
Definition: misc_gui.cpp:760
bool _networking
are we in networking mode?
Definition: network.cpp:65
bool _network_dedicated
are we a dedicated server?
Definition: network.cpp:68
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.
uint8_t valid
Bits indicating what variable is valid (for each bit, 0 is invalid, 1 is valid).
Functions related to news.
void InitNewsItemStructs()
Initialize the news-items data structures.
Definition: news_gui.cpp:651
@ 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, const char *grpname, void *desc)
Load a WindowDesc from config.
Definition: settings.cpp:862
void IniSaveWindowSettings(IniFile &ini, const char *grpname, void *desc)
Save a WindowDesc to config.
Definition: settings.cpp:873
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:56
Functions related to setting/changing the settings.
Types related to global configuration settings.
@ VSM_VIEWPORT_RMB_FIXED
Viewport moves with mouse movement on holding right mouse button, cursor position is fixed.
@ VSM_MAP_RMB_FIXED
Map moves with mouse movement on holding right mouse button, cursor position is fixed.
@ VSM_MAP_LMB
Map moves with mouse movement on holding left mouse button, cursor moves.
@ SWS_SCROLL_MAP
Scroll wheel scrolls the map.
bool ScrollMainWindowTo(int x, int y, int z, bool instant)
Scrolls the main window to given coordinates.
Functions, definitions and such used only by the GUI.
@ SBI_NEWS_DELETED
abort current news display (active news were deleted)
Definition: statusbar_gui.h:19
Definition of base types and functions in a cross-platform compatible way.
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:56
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
Definition: strings_type.h:24
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
GUISettings gui
settings related to the GUI
bool fix_at
mouse is moving, but cursor is not (used for scrolling)
Definition: gfx_type.h:128
Point pos
logical mouse position
Definition: gfx_type.h:125
bool in_window
mouse inside this window, determines drawing logic
Definition: gfx_type.h:147
int wheel
mouse wheel movement
Definition: gfx_type.h:127
Point delta
relative mouse movement in this tick
Definition: gfx_type.h:126
Data about how and where to blit pixels.
Definition: gfx_type.h:157
uint8_t window_snap_radius
windows snap at each other if closer than this
uint16_t hover_delay_ms
time required to activate a hover event, in milliseconds
uint8_t auto_scrolling
scroll when moving mouse to the edge (see ViewportAutoscrolling)
uint8_t scroll_mode
viewport scroll mode
RightClickClose right_click_wnd_close
close window with right click
uint8_t window_soft_limit
soft limit of maximum number of non-stickied non-vital windows (0 = no limit)
uint8_t toolbar_pos
position of toolbars, 0=left, 1=center, 2=right
uint8_t statusbar_pos
position of statusbar, 0=left, 1=center, 2=right
uint8_t scrollwheel_scrolling
scrolling using the scroll wheel?
List of hotkeys for a window.
Definition: hotkeys.h:37
int CheckMatch(uint16_t keycode, bool global_only=false) const
Check if a keycode is bound to something.
Definition: hotkeys.cpp:309
Ini file that supports both loading and saving.
Definition: ini_type.h:88
bool SaveToDisk(const std::string &filename)
Save the Ini file's data to the disk.
Definition: ini.cpp:42
void LoadFromDisk(const std::string &filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition: ini_load.cpp:187
NewGrfDebugSpritePickerMode mode
Current state.
Definition: newgrf_debug.h:26
void * clicked_pixel
Clicked pixel (pointer to blitter buffer)
Definition: newgrf_debug.h:27
std::vector< SpriteID > sprites
Sprites found.
Definition: newgrf_debug.h:28
Coordinates of a point in 2D.
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
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:918
Rect GetBoundingRect(const Window *w, WidgetID wid, const char *from, const char *to) const
Get the bounding rectangle for a range of the query string.
Definition: misc_gui.cpp:888
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:860
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:214
uint step_width
Step-size of width resize changes.
Definition: window_gui.h:213
Helper/buffer for input fields.
Definition: textbuf_type.h:30
void DeleteAll()
Delete every character in the textbuffer.
Definition: textbuf.cpp:114
bool InsertString(const char *str, bool marked, const char *caret=nullptr, const char *insert_location=nullptr, const char *replacement_end=nullptr)
Insert a string into the text buffer.
Definition: textbuf.cpp:160
uint16_t bytes
the current size of the string in bytes (including terminating '\0')
Definition: textbuf_type.h:35
Window * GetCallbackWnd()
Get the window that started the current highlighting.
Definition: viewport.cpp:2588
WindowClass window_class
The WindowClass of the window that is responsible for the selection mode.
void Reset()
Reset tile highlighting.
Definition: viewport.cpp:2567
Point selend
The location where the drag currently ends.
WindowNumber window_number
The WindowNumber of the window that is responsible for the selection mode.
Vehicle data structure.
Definition: vehicle_base.h:244
int32_t z_pos
z coordinate.
Definition: vehicle_base.h:306
int32_t y_pos
y coordinate.
Definition: vehicle_base.h:305
int32_t x_pos
x coordinate.
Definition: vehicle_base.h:304
int32_t dest_scrollpos_y
Current destination y coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition: window_gui.h:257
int32_t dest_scrollpos_x
Current destination x coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition: window_gui.h:256
VehicleID follow_vehicle
VehicleID to follow if following a vehicle, INVALID_VEHICLE otherwise.
Definition: window_gui.h:253
Data structure for viewport, display of a part of the world.
Definition: viewport_type.h:22
int top
Screen coordinate top edge of the viewport.
Definition: viewport_type.h:24
int width
Screen width of the viewport.
Definition: viewport_type.h:25
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:33
int left
Screen coordinate left edge of the viewport.
Definition: viewport_type.h:23
int height
Screen height of the viewport.
Definition: viewport_type.h:26
High level window description.
Definition: window_gui.h:159
uint32_t flags
Flags.
Definition: window_gui.h:173
int16_t GetDefaultWidth() const
Determine default width of window.
Definition: window.cpp:137
static void SaveToConfig()
Save all WindowDesc settings to _windows_file.
Definition: window.cpp:177
int16_t pref_width
User-preferred width of the window. Zero if unset.
Definition: window_gui.h:178
bool pref_sticky
Preferred stickyness.
Definition: window_gui.h:177
HotkeyList * hotkeys
Hotkeys for the window.
Definition: window_gui.h:175
int16_t pref_height
User-preferred height of the window. Zero if unset.
Definition: window_gui.h:179
int16_t GetDefaultHeight() const
Determine default height of window.
Definition: window.cpp:147
WindowClass cls
Class of the window,.
Definition: window_gui.h:170
WindowDesc(WindowPosition default_pos, const char *ini_key, int16_t def_width_trad, int16_t def_height_trad, WindowClass window_class, WindowClass parent_class, uint32_t 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:105
WindowPosition default_pos
Preferred position of the window.
Definition: window_gui.h:169
const char * ini_key
Key to store window defaults in openttd.cfg. nullptr if nothing shall be stored.
Definition: window_gui.h:172
int16_t default_height_trad
Preferred initial height of the window (pixels at 1x zoom).
Definition: window_gui.h:189
WindowClass parent_cls
Class of the parent window.
Definition: window_gui.h:171
int16_t default_width_trad
Preferred initial width of the window (pixels at 1x zoom).
Definition: window_gui.h:188
static void LoadFromConfig()
Load all WindowDesc settings from _windows_file.
Definition: window.cpp:155
const std::span< const NWidgetPart > nwid_parts
Span of nested widget parts describing the window.
Definition: window_gui.h:174
Iterable ensemble of all valid Windows.
Definition: window_gui.h:913
Iterator to iterate all valid Windows.
Definition: window_gui.h:868
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:365
void SetWidgetHighlight(WidgetID widget_index, TextColour highlighted_colour)
Sets the highlighted status of a widget.
Definition: window.cpp:242
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition: window.cpp:952
virtual void OnDragDrop([[maybe_unused]] Point pt, [[maybe_unused]] WidgetID widget)
A dragged 'object' has been released.
Definition: window_gui.h:708
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition: window.cpp:1047
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition: window.cpp:1733
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:194
uint8_t white_border_timer
Timer value of the WF_WHITE_BORDER 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:1402
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:3151
Window * parent
Parent window.
Definition: window_gui.h:328
virtual ~Window()
Remove window and all its child windows from the window stack.
Definition: window.cpp:1084
virtual bool OnTooltip([[maybe_unused]] Point pt, [[maybe_unused]] WidgetID widget, [[maybe_unused]] TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Definition: window_gui.h:694
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition: window_gui.h:475
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition: window.cpp:551
uint8_t timeout_timer
Timer value of the WF_TIMEOUT for flags.
Definition: window_gui.h:306
virtual EventState OnKeyPress([[maybe_unused]] char32_t key, [[maybe_unused]] uint16_t keycode)
A key has been pressed.
Definition: window_gui.h:652
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
Definition: window_gui.h:326
virtual void OnMouseOver([[maybe_unused]] Point pt, [[maybe_unused]] WidgetID widget)
The mouse is currently moving over the window or has just moved outside of the window.
Definition: window_gui.h:722
virtual void OnGameTick()
Called once per (game) tick.
Definition: window_gui.h:739
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
Definition: window_gui.h:861
void ProcessScheduledInvalidations()
Process all scheduled invalidations.
Definition: window.cpp:3164
virtual void OnHover([[maybe_unused]] Point pt, [[maybe_unused]] WidgetID widget)
The mouse is hovering over a widget in the window, perform an action for it.
Definition: window_gui.h:686
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition: window.cpp:1035
ResizeInfo resize
Resize information.
Definition: window_gui.h:314
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition: window.cpp:470
virtual void OnMouseLoop()
Called for every mouse loop run, which is at least once per (game) tick.
Definition: window_gui.h:734
void SetShaded(bool make_shaded)
Set the shaded state of the window to make_shaded.
Definition: window.cpp:995
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:3129
virtual void OnPaint()
The window must be repainted.
Definition: window_gui.h:599
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition: window.cpp:1723
WindowDesc & window_desc
Window description.
Definition: window_gui.h:299
WindowClass window_class
Window class.
Definition: window_gui.h:301
ViewportData * viewport
Pointer to viewport data, if present.
Definition: window_gui.h:318
void SetWhiteBorder()
Set the timeout flag of the window and initiate the timer.
Definition: window_gui.h:370
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition: window.cpp:486
virtual void OnFocusLost(bool closing)
The window has lost focus.
Definition: window.cpp:516
virtual void OnClick([[maybe_unused]] Point pt, [[maybe_unused]] WidgetID widget, [[maybe_unused]] int click_count)
A click with the left mouse button has been made on the window.
Definition: window_gui.h:670
virtual void OnDropdownClose(Point pt, WidgetID widget, int index, bool instant_close)
A dropdown window associated to this window has been closed.
Definition: window.cpp:287
static std::vector< Window * > closed_windows
List of closed windows to delete.
Definition: window_gui.h:275
virtual void OnPlacePresize([[maybe_unused]] Point pt, [[maybe_unused]] 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:845
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition: window.cpp:525
virtual void OnMouseWheel([[maybe_unused]] int wheel)
The mouse wheel has been turned.
Definition: window_gui.h:728
virtual Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number)
Compute the initial position of the window.
Definition: window.cpp:1711
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:378
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:563
Window * FindChildWindow(WindowClass wc=WC_INVALID) const
Find the Window whose parent pointer points to this window.
Definition: window.cpp:1022
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition: window_gui.h:361
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition: window_gui.h:319
void UpdateQueryStringSize()
Update size of all QueryStrings of this window.
Definition: window.cpp:354
int top
y position of top edge of the window
Definition: window_gui.h:310
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition: window.cpp:334
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition: window_gui.h: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:409
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:1364
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition: window.cpp:1756
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:213
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition: window_gui.h:977
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition: window_gui.h:466
virtual EventState OnCTRLStateChange()
The state of the control key has changed.
Definition: window_gui.h:661
void ProcessScheduledResize()
Process scheduled OnResize() event.
Definition: window.cpp:3137
EventState HandleEditBoxKey(WidgetID wid, char32_t key, uint16_t keycode)
Process keypress for editbox widget.
Definition: window.cpp:2473
virtual void InsertTextString(WidgetID wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
Insert a text string at the cursor position into the edit box widget.
Definition: window.cpp:2631
virtual void OnEditboxChanged([[maybe_unused]] WidgetID widget)
The text in an editbox has been edited.
Definition: window_gui.h:771
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:590
virtual void OnResize()
Called after the window got resized.
Definition: window_gui.h:756
virtual bool OnRightClick([[maybe_unused]] Point pt, [[maybe_unused]] WidgetID widget)
A click with the right mouse button has been made on the window.
Definition: window_gui.h:679
virtual void OnFocus()
The window has gained focus.
Definition: window.cpp:508
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition: window.cpp:1746
virtual void OnDropdownSelect([[maybe_unused]] WidgetID widget, [[maybe_unused]] int index)
A dropdown option associated to this window has been selected.
Definition: window_gui.h:763
virtual void OnTimeout()
Called when this window's timeout has been reached.
Definition: window_gui.h:749
virtual void OnMouseDrag([[maybe_unused]] Point pt, [[maybe_unused]] WidgetID widget)
An 'object' is being dragged at the provided position, highlight the target if possible.
Definition: window_gui.h:701
WindowFlags flags
Window flags.
Definition: window_gui.h:300
virtual void OnRealtimeTick([[maybe_unused]] uint delta_ms)
Called periodically.
Definition: window_gui.h:744
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition: window.cpp:314
virtual void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true)
Some data on this window has become invalid.
Definition: window_gui.h:786
void ProcessHighlightedInvalidations()
Process all invalidation of highlighted widgets.
Definition: window.cpp:3176
virtual Rect GetTextBoundingRect(const char *from, const char *to) const
Get the bounding rectangle for a text range if an edit box has the focus.
Definition: window.cpp:394
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition: window.cpp:565
static void DeleteClosedWindows()
Delete all closed windows.
Definition: window.cpp:65
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
bool IsWidgetHighlighted(WidgetID widget_index) const
Gets the highlighted status of a widget.
Definition: window.cpp:272
void DisableAllWidgetHighlight()
Disable the highlighted status of all widgets.
Definition: window.cpp:224
virtual void OnScroll([[maybe_unused]] Point delta)
Handle the request for (viewport) scrolling.
Definition: window_gui.h:714
int height
Height of the window (number of pixels down in y direction)
Definition: window_gui.h:312
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:582
virtual void FindWindowPlacementAndResize(int def_width, int def_height)
Resize window towards the default size.
Definition: window.cpp:1420
WindowNumber window_number
Window number within the window class.
Definition: window_gui.h:302
@ HKPR_NOT_HANDLED
Key does not affect editboxes.
Definition: textbuf_type.h:26
@ HKPR_CANCEL
Escape key pressed.
Definition: textbuf_type.h:25
@ HKPR_EDITING
Textbuf content changed.
Definition: textbuf_type.h:22
@ HKPR_CONFIRM
Return or enter key pressed.
Definition: textbuf_type.h:24
@ HKPR_CURSOR
Non-text change, e.g. cursor position.
Definition: textbuf_type.h:23
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
Definition: viewport.cpp:3498
void UpdateTileSelection()
Updates tile highlighting for all cases.
Definition: viewport.cpp:2602
Definition of Interval and OneShot timers.
Definition of the Window system.
uint _toolbar_width
Width of the toolbar, shared by statusbar.
Definition: toolbar_gui.cpp:74
Stuff related to the (main) toolbar.
Base class for all vehicles.
uint32_t VehicleID
The type all our vehicle IDs have.
Definition: vehicle_type.h:16
static const VehicleID INVALID_VEHICLE
Constant representing a non-existing vehicle.
Definition: vehicle_type.h:54
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:411
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
Update the viewport position being displayed.
Definition: viewport.cpp:1916
Functions related to (drawing on) viewports.
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition: widget.cpp:66
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:266
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition: widget.cpp:242
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
Definition: widget_type.h:482
@ ND_DROPDOWN_ACTIVE
Bit value of the 'dropdown active' flag.
Definition: widget_type.h:358
@ ND_SCROLLBAR_UP
Bit value of the 'scrollbar up' flag.
Definition: widget_type.h:359
@ NDB_SCROLLBAR_UP
Up-button is lowered bit.
Definition: widget_type.h:346
@ ND_SCROLLBAR_BTN
Bit value of the 'scrollbar up' or 'scrollbar down' flag.
Definition: widget_type.h:361
@ NDB_DROPDOWN_CLOSED
Dropdown menu of the dropdown widget has closed.
Definition: widget_type.h:350
@ ND_SCROLLBAR_DOWN
Bit value of the 'scrollbar down' flag.
Definition: widget_type.h:360
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition: widget_type.h:46
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition: widget_type.h:82
@ WWT_EDITBOX
a textbox for typing
Definition: widget_type.h:71
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:66
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition: widget_type.h:84
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition: widget_type.h:69
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition: widget_type.h:48
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition: widget_type.h:72
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition: widget_type.h:83
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition: widget_type.h:68
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition: widget_type.h:65
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition: widget_type.h:63
@ ST_RESIZE
Resize the nested widget tree.
Definition: widget_type.h:121
@ ST_SMALLEST
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
Definition: widget_type.h:120
int PositionStatusbar(Window *w)
(Re)position statusbar window at the screen.
Definition: window.cpp:3393
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:1945
static bool _dragging_window
A window is being dragged or resized.
Definition: window.cpp:2079
void CloseConstructionWindows()
Close all windows that are used for construction of vehicle etc.
Definition: window.cpp:3297
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:1140
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:1652
static Point _drag_delta
delta between mouse cursor and upper left corner of dragged window
Definition: window.cpp:52
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition: window.cpp:1223
static bool MayBeShown(const Window *w)
Returns whether a window may be shown or not.
Definition: window.cpp:829
void CloseCompanyWindows(CompanyID id)
Close all windows of a company.
Definition: window.cpp:1168
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition: window.cpp:2618
static IntervalTimer< TimerWindow > white_border_interval(std::chrono::milliseconds(30), [](auto) { if(_network_dedicated) return;for(Window *w :Window::Iterate()) { if((w->flags &WF_WHITE_BORDER) &&--w->white_border_timer==0) { CLRBITS(w->flags, WF_WHITE_BORDER);w->SetDirty();} } })
Blink all windows marked with a white border.
static 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 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:857
bool _scrolling_viewport
A viewport is being scrolled with the mouse.
Definition: window.cpp:90
void InputLoop()
Regular call from the global game loop.
Definition: window.cpp:2985
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition: window.cpp:3048
int PositionMainToolbar(Window *w)
(Re)position main toolbar window at the screen.
Definition: window.cpp:3382
void CloseNonVitalWindows()
Try to close a non-vital window.
Definition: window.cpp:3253
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:919
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition: window.cpp:2022
static IntervalTimer< TimerWindow > highlight_interval(std::chrono::milliseconds(450), [](auto) { _window_highlight_colour=!_window_highlight_colour;})
Blink the window highlight colour constantly.
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:1520
int PositionNetworkChatWindow(Window *w)
(Re)position network chat window at the screen.
Definition: window.cpp:3415
static void StartWindowSizing(Window *w, bool to_left)
Start resizing a window.
Definition: window.cpp:2273
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:1559
static void HandleAutoscroll()
If needed and switched on, perform auto scrolling (automatically moving window contents when mouse is...
Definition: window.cpp:2667
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition: window.cpp:1098
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition: window.cpp:75
void HandleToolbarHotkey(int hotkey)
Handle Toolbar hotkey events - can come from a source like the MacBook Touch Bar.
Definition: window.cpp:2545
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition: window.cpp:1113
static int PositionWindow(Window *w, WindowClass clss, int setting)
(Re)position a window at the screen.
Definition: window.cpp:3359
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition: window.cpp:422
static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolbar_y, Point &pos)
Decide whether a given rectangle is a good place to open a completely visible new window.
Definition: window.cpp:1484
static constexpr int MAX_OFFSET_DOUBLE_CLICK
How much the mouse is allowed to move to call it a double click.
Definition: window.cpp:2712
static int _input_events_this_tick
Local counter that is incremented each time an mouse input event is detected.
Definition: window.cpp:2661
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition: window.cpp:3427
const std::chrono::milliseconds TIME_BETWEEN_DOUBLE_CLICK(500)
Time between 2 left clicks before it becoming a double click.
static EventState HandleViewportScroll()
Handle viewport scrolling with the mouse.
Definition: window.cpp:2353
static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
Dispatch the mousewheel-action to the window.
Definition: window.cpp:798
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
Dispatch left mouse-button (possibly double) click in window.
Definition: window.cpp:607
void DeleteAllMessages()
Delete all messages and close their corresponding window (if any).
Definition: window.cpp:3285
static Window * _last_scroll_window
Window of the last scroll event.
Definition: window.cpp:54
int GetMainViewTop()
Return the top of the main view available for general use.
Definition: window.cpp:2062
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:1344
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition: window.cpp:3327
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:1985
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition: window.cpp:1152
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition: window.cpp:2877
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:1627
Window * GetMainWindow()
Get the main window, i.e.
Definition: window.cpp:1127
static EventState HandleWindowDragging()
Handle dragging/resizing of a window.
Definition: window.cpp:2085
int PositionNewsMessage(Window *w)
(Re)position news message window at the screen.
Definition: window.cpp:3404
int GetMainViewBottom()
Return the bottom of the main view available for general use.
Definition: window.cpp:2073
static void DispatchHoverEvent(Window *w, int x, int y)
Dispatch hover of the mouse over a window.
Definition: window.cpp:770
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:1188
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
Definition: window.cpp:2562
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition: window.cpp:3119
void HandleTextInput(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
Handle text input.
Definition: window.cpp:2648
EventState VpHandlePlaceSizingDrag()
Handle the mouse while dragging for placement/resizing.
Definition: viewport.cpp:3381
PreventHideDirection
Direction for moving the window.
Definition: window.cpp:1930
@ PHD_DOWN
Below v is a safe position.
Definition: window.cpp:1932
@ PHD_UP
Above v is a safe position.
Definition: window.cpp:1931
static constexpr int MAX_OFFSET_HOVER
Maximum mouse movement before stopping a hover event.
Definition: window.cpp:2713
bool FocusedWindowIsConsole()
Check if a console is focused.
Definition: window.cpp:462
void ResetWindowSystem()
Reset the windowing system, by means of shutting it down followed by re-initialization.
Definition: window.cpp:1818
static uint GetWindowZPriority(WindowClass wc)
Get the z-priority for a given window.
Definition: window.cpp:1260
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition: window.cpp:448
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition: window.cpp:3211
void HideVitalWindows()
Close all always on-top windows to get an empty screen.
Definition: window.cpp:3310
static bool DescSorter(WindowDesc *const &a, WindowDesc *const &b)
Sort WindowDesc by ini_key.
Definition: window.cpp:168
void RelocateAllWindows(int neww, int newh)
Relocate all windows to fit the new size of the game application screen.
Definition: window.cpp:3443
std::string _windows_file
Config file to store WindowDesc.
Definition: window.cpp:102
static void HandleScrollbarScrolling(Window *w)
Handle scrollbar scrolling with the mouse.
Definition: window.cpp:2289
bool _mouse_hovering
The mouse is hovering over the same point.
Definition: window.cpp:91
static void StartWindowDrag(Window *w)
Start window dragging.
Definition: window.cpp:2256
void CallWindowGameTickEvent()
Dispatch OnGameTick event over all windows.
Definition: window.cpp:3240
static EventState HandleActiveWidget()
Handle active widget (mouse draggin on widget) with the mouse.
Definition: window.cpp:2320
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition: window.cpp:1768
std::vector< WindowDesc * > * _window_descs
List of all WindowDescs.
Definition: window.cpp:99
static void DispatchRightClickEvent(Window *w, int x, int y)
Dispatch right mouse-button click in window.
Definition: window.cpp:741
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:2737
static bool MaybeBringWindowToFront(Window *w)
Check if a window can be made relative top-most window, and if so do it.
Definition: window.cpp:2413
WindowList _z_windows
List of windows opened at the screen sorted from the front to back.
Definition: window.cpp:57
void CallWindowRealtimeTickEvent(uint delta_ms)
Dispatch OnRealtimeTick event over all windows.
Definition: window.cpp:3010
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition: window.cpp:93
ViewportAutoscrolling
Values for _settings_client.gui.auto_scrolling.
Definition: window.cpp:45
@ VA_MAIN_VIEWPORT_FULLSCREEN
Scroll main viewport at edge when using fullscreen.
Definition: window.cpp:47
@ VA_MAIN_VIEWPORT
Scroll main viewport at edge.
Definition: window.cpp:48
@ VA_EVERY_VIEWPORT
Scroll all viewports at their edges.
Definition: window.cpp:49
@ VA_DISABLED
Do not autoscroll when mouse is at edge of viewport.
Definition: window.cpp:46
static EventState HandleMouseDragDrop()
Handle dragging and dropping in mouse dragging mode (WSM_DRAGDROP).
Definition: window.cpp:1883
void CloseAllNonVitalWindows()
It is possible that a stickied window gets to a position where the 'close' button is outside the gami...
Definition: window.cpp:3272
static void HandleMouseOver()
Report position of the mouse to the underlying window.
Definition: window.cpp:1907
static Window * _mouseover_last_w
Window of the last OnMouseOver event.
Definition: window.cpp:53
static void CheckSoftLimit()
Check the soft limit of deletable (non vital, non sticky) windows.
Definition: window.cpp:2960
void UnInitWindowSystem()
Close down the windowing system.
Definition: window.cpp:1804
void InitWindowSystem()
(re)initialize the windowing system
Definition: window.cpp:1782
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:3106
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition: window.cpp:3093
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:3228
Window functions not directly related to making/drawing windows.
SpecialMouseMode
Mouse modes.
Definition: window_gui.h:1039
@ WSM_DRAGDROP
Drag&drop an object.
Definition: window_gui.h:1041
@ WSM_PRESIZE
Presizing mode (docks, tunnels).
Definition: window_gui.h:1043
@ WF_DRAGGING
Window is being dragged.
Definition: window_gui.h:230
@ WF_WHITE_BORDER
Window white border counter bit mask.
Definition: window_gui.h:236
@ WF_TIMEOUT
Window timeout counter.
Definition: window_gui.h:228
@ WF_SIZING_LEFT
Window is being resized towards the left.
Definition: window_gui.h:232
@ WF_DISABLE_VP_SCROLL
Window does not do autoscroll,.
Definition: window_gui.h:235
@ WF_SIZING
Window is being resized.
Definition: window_gui.h:233
@ WF_HIGHLIGHTED
Window has a widget that has a highlight.
Definition: window_gui.h:237
@ WF_STICKY
Window is made sticky by user.
Definition: window_gui.h:234
@ WF_SIZING_RIGHT
Window is being resized towards the right.
Definition: window_gui.h:231
@ WF_CENTERED
Window is centered and shall stay centered after ReInit.
Definition: window_gui.h:238
@ WDF_NO_CLOSE
This window can't be interactively closed.
Definition: window_gui.h:206
@ WDF_CONSTRUCTION
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:203
@ WDF_MODAL
The window is a modal child of some other window, meaning the parent is 'inactive'.
Definition: window_gui.h:204
@ WDF_NO_FOCUS
This window won't get focus/make any other window lose focus when click.
Definition: window_gui.h:205
WindowPosition
How do we the window to be placed?
Definition: window_gui.h:145
@ WDP_CENTER
Center the window.
Definition: window_gui.h:148
@ WDP_AUTO
Find a place automatically.
Definition: window_gui.h:147
@ WDP_ALIGN_TOOLBAR
Align toward the toolbar.
Definition: window_gui.h:149
@ WDP_MANUAL
Manually align the window (so no automatic location finding)
Definition: window_gui.h:146
int WidgetID
Widget ID.
Definition: window_type.h:18
int32_t WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:737
EventState
State of handling an event.
Definition: window_type.h:743
@ ES_HANDLED
The passed event is handled.
Definition: window_type.h:744
@ ES_NOT_HANDLED
The passed event is not handled.
Definition: window_type.h:745
WindowClass
Window classes.
Definition: window_type.h:44
@ WC_INVALID
Invalid window.
Definition: window_type.h:724
@ WC_OSK
On Screen Keyboard; Window numbers:
Definition: window_type.h:162
@ WC_CONSOLE
Console; Window numbers:
Definition: window_type.h:649
@ WC_NEWS_WINDOW
News window; Window numbers:
Definition: window_type.h:248
@ WC_HIGHSCORE
Highscore; Window numbers:
Definition: window_type.h:661
@ WC_BUY_COMPANY
Buyout company (merger); Window numbers:
Definition: window_type.h:594
@ WC_SPRITE_ALIGNER
Sprite aligner (debug); Window numbers:
Definition: window_type.h:686
@ WC_COMPANY_INFRASTRUCTURE
Company infrastructure overview; Window numbers:
Definition: window_type.h:587
@ WC_ENDSCREEN
Endscreen; Window numbers:
Definition: window_type.h:667
@ WC_COMPANY_COLOUR
Company colour selection; Window numbers:
Definition: window_type.h:230
@ WC_STATION_LIST
Station list; Window numbers:
Definition: window_type.h:302
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
Definition: window_type.h:314
@ WC_STATUS_BAR
Statusbar (at the bottom of your screen); Window numbers:
Definition: window_type.h:64
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
Definition: window_type.h:212
@ WC_SEND_NETWORK_MSG
Chatbox; Window numbers:
Definition: window_type.h:509
@ WC_ERRMSG
Error message; Window numbers:
Definition: window_type.h:110
@ WC_BUILD_TOOLBAR
Build toolbar; Window numbers:
Definition: window_type.h:73
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
Definition: window_type.h:497
@ WC_SCRIPT_SETTINGS
Script settings; Window numbers:
Definition: window_type.h:175
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:45
@ WC_SCEN_LAND_GEN
Landscape generation (in Scenario Editor); Window numbers:
Definition: window_type.h:455
@ WC_SCRIPT_LIST
Scripts list; Window numbers:
Definition: window_type.h:284
@ WC_SAVE_PRESET
Save preset; Window numbers:
Definition: window_type.h:698
@ WC_SHIPS_LIST
Ships list; Window numbers:
Definition: window_type.h:320
@ WC_MESSAGE_HISTORY
News history list; Window numbers:
Definition: window_type.h:272
@ WC_CONFIRM_POPUP_QUERY
Popup with confirm question; Window numbers:
Definition: window_type.h:130
@ WC_GENERATE_LANDSCAPE
Generate landscape (newgame); Window numbers:
Definition: window_type.h:462
@ WC_CUSTOM_CURRENCY
Custom currency; Window numbers:
Definition: window_type.h:630
@ WC_MAIN_WINDOW
Main window; Window numbers:
Definition: window_type.h:51
@ WC_TRAINS_LIST
Trains list; Window numbers:
Definition: window_type.h:308
@ WC_GAME_OPTIONS
Game options window; Window numbers:
Definition: window_type.h:624
@ WC_NETWORK_WINDOW
Network window; Window numbers:
Definition: window_type.h:478
@ WC_FINANCES
Finances of a company; Window numbers:
Definition: window_type.h:527
@ WC_TEXTFILE
textfile; Window numbers:
Definition: window_type.h:187
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
Definition: window_type.h:156
@ WC_QUERY_STRING
Query string window; Window numbers:
Definition: window_type.h:123
@ WC_SMALLMAP
Small map; Window numbers:
Definition: window_type.h:104
@ WC_BOOTSTRAP
Bootstrap; Window numbers:
Definition: window_type.h:655
@ WC_SAVELOAD
Saveload window; Window numbers:
Definition: window_type.h:144
@ WC_GRF_PARAMETERS
NewGRF parameters; Window numbers:
Definition: window_type.h:181
@ WC_MODAL_PROGRESS
Progress report of landscape generation; Window numbers:
Definition: window_type.h:469
@ WC_MAIN_TOOLBAR
Main toolbar (the long bar at the top); Window numbers:
Definition: window_type.h:58
@ WC_TOOLTIPS
Tooltip window; Window numbers:
Definition: window_type.h:116
@ WC_COMPANY
Company view; Window numbers:
Definition: window_type.h:369
@ WC_NETWORK_STATUS_WINDOW
Network status window; Window numbers:
Definition: window_type.h:491
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Definition: window_type.h:326
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_MIN) When shifting right,...
Definition: zoom_func.h:22
int ScaleGUITrad(int value)
Scale traditional pixel dimensions to GUI zoom level.
Definition: zoom_func.h:117
@ ZOOM_LVL_MIN
Minimum zoom level.
Definition: zoom_type.h:41