OpenTTD Source  20241120-master-g6d3adc6169
story_gui.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 "window_gui.h"
12 #include "strings_func.h"
13 #include "gui.h"
14 #include "story_base.h"
15 #include "core/geometry_func.hpp"
16 #include "company_func.h"
17 #include "command_func.h"
18 #include "dropdown_type.h"
19 #include "dropdown_func.h"
20 #include "sortlist_type.h"
21 #include "goal_base.h"
22 #include "viewport_func.h"
23 #include "window_func.h"
24 #include "company_base.h"
25 #include "tilehighlight_func.h"
26 #include "vehicle_base.h"
27 #include "story_cmd.h"
28 
29 #include "widgets/story_widget.h"
30 
31 #include "table/strings.h"
32 #include "table/sprites.h"
33 
34 #include "safeguards.h"
35 
36 static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor);
37 
40 
42 protected:
44  const StoryPageElement *pe;
45  Rect bounds;
46  };
47  typedef std::vector<LayoutCacheElement> LayoutCache;
48 
49  enum class ElementFloat {
50  None,
51  Left,
52  Right,
53  };
54 
56  mutable LayoutCache layout_cache;
57 
61  std::string selected_generic_title;
62 
64 
65  static const std::initializer_list<GUIStoryPageList::SortFunction * const> page_sorter_funcs;
66  static const std::initializer_list<GUIStoryPageElementList::SortFunction * const> page_element_sorter_funcs;
67 
70  {
71  if (this->story_pages.NeedRebuild()) {
72  this->story_pages.clear();
73 
74  for (const StoryPage *p : StoryPage::Iterate()) {
75  if (this->IsPageAvailable(p)) {
76  this->story_pages.push_back(p);
77  }
78  }
79 
80  this->story_pages.RebuildDone();
81  }
82 
83  this->story_pages.Sort();
84  }
85 
87  static bool PageOrderSorter(const StoryPage * const &a, const StoryPage * const &b)
88  {
89  return a->sort_value < b->sort_value;
90  }
91 
94  {
95  if (this->story_page_elements.NeedRebuild()) {
96  this->story_page_elements.clear();
97 
98  const StoryPage *p = GetSelPage();
99  if (p != nullptr) {
100  for (const StoryPageElement *pe : StoryPageElement::Iterate()) {
101  if (pe->page == p->index) {
102  this->story_page_elements.push_back(pe);
103  }
104  }
105  }
106 
107  this->story_page_elements.RebuildDone();
108  }
109 
110  this->story_page_elements.Sort();
112  }
113 
115  static bool PageElementOrderSorter(const StoryPageElement * const &a, const StoryPageElement * const &b)
116  {
117  return a->sort_value < b->sort_value;
118  }
119 
120  /*
121  * Checks if a given page should be visible in the story book.
122  * @param page The page to check.
123  * @return True if the page should be visible, otherwise false.
124  */
125  bool IsPageAvailable(const StoryPage *page) const
126  {
127  return page->company == INVALID_COMPANY || page->company == this->window_number;
128  }
129 
135  {
136  if (!_story_page_pool.IsValidID(selected_page_id)) return nullptr;
137  return _story_page_pool.Get(selected_page_id);
138  }
139 
144  int GetSelPageNum() const
145  {
146  int page_number = 0;
147  for (const StoryPage *p : this->story_pages) {
148  if (p->index == this->selected_page_id) {
149  return page_number;
150  }
151  page_number++;
152  }
153  return -1;
154  }
155 
160  {
161  /* Verify that the selected page exist. */
162  if (!_story_page_pool.IsValidID(this->selected_page_id)) return false;
163 
164  return this->story_pages.front()->index == this->selected_page_id;
165  }
166 
171  {
172  /* Verify that the selected page exist. */
173  if (!_story_page_pool.IsValidID(this->selected_page_id)) return false;
174 
175  if (this->story_pages.size() <= 1) return true;
176  const StoryPage *last = this->story_pages.back();
177  return last->index == this->selected_page_id;
178  }
179 
184  {
185  /* Generate generic title if selected page have no custom title. */
186  StoryPage *page = this->GetSelPage();
187  if (page != nullptr && page->title.empty()) {
188  SetDParam(0, GetSelPageNum() + 1);
189  selected_generic_title = GetString(STR_STORY_BOOK_GENERIC_PAGE_ITEM);
190  }
191 
192  this->story_page_elements.ForceRebuild();
194 
195  if (this->active_button_id != INVALID_STORY_PAGE_ELEMENT) ResetObjectToPlace();
196 
197  this->vscroll->SetCount(this->GetContentHeight());
201  }
202 
207  {
208  if (!_story_page_pool.IsValidID(this->selected_page_id)) return;
209 
210  /* Find the last available page which is previous to the current selected page. */
211  const StoryPage *last_available;
212  last_available = nullptr;
213  for (const StoryPage *p : this->story_pages) {
214  if (p->index == this->selected_page_id) {
215  if (last_available == nullptr) return; // No previous page available.
216  this->SetSelectedPage(last_available->index);
217  return;
218  }
219  last_available = p;
220  }
221  }
222 
227  {
228  if (!_story_page_pool.IsValidID(this->selected_page_id)) return;
229 
230  /* Find selected page. */
231  for (auto iter = this->story_pages.begin(); iter != this->story_pages.end(); iter++) {
232  const StoryPage *p = *iter;
233  if (p->index == this->selected_page_id) {
234  /* Select the page after selected page. */
235  iter++;
236  if (iter != this->story_pages.end()) {
237  this->SetSelectedPage((*iter)->index);
238  }
239  return;
240  }
241  }
242  }
243 
248  {
249  DropDownList list;
250  uint16_t page_num = 1;
251  for (const StoryPage *p : this->story_pages) {
252  bool current_page = p->index == this->selected_page_id;
253  if (!p->title.empty()) {
254  list.push_back(MakeDropDownListStringItem(p->title, p->index, current_page));
255  } else {
256  /* No custom title => use a generic page title with page number. */
257  SetDParam(0, page_num);
258  list.push_back(MakeDropDownListStringItem(STR_STORY_BOOK_GENERIC_PAGE_ITEM, p->index, current_page));
259  }
260  page_num++;
261  }
262 
263  return list;
264  }
265 
270  {
271  return this->GetWidget<NWidgetCore>(WID_SB_PAGE_PANEL)->current_x - WidgetDimensions::scaled.frametext.Horizontal() - 1;
272  }
273 
281  uint GetHeadHeight(int max_width) const
282  {
283  StoryPage *page = this->GetSelPage();
284  if (page == nullptr) return 0;
285  int height = 0;
286 
287  /* Title lines */
288  height += GetCharacterHeight(FS_NORMAL); // Date always use exactly one line.
289  SetDParamStr(0, !page->title.empty() ? page->title : this->selected_generic_title);
290  height += GetStringHeight(STR_STORY_BOOK_TITLE, max_width);
291 
292  return height;
293  }
294 
302  {
303  switch (pe.type) {
304  case SPET_GOAL: {
305  Goal *g = Goal::Get((GoalID) pe.referenced_id);
306  if (g == nullptr) return SPR_IMG_GOAL_BROKEN_REF;
307  return g->completed ? SPR_IMG_GOAL_COMPLETED : SPR_IMG_GOAL;
308  }
309  case SPET_LOCATION:
310  return SPR_IMG_VIEW_LOCATION;
311  default:
312  NOT_REACHED();
313  }
314  }
315 
322  uint GetPageElementHeight(const StoryPageElement &pe, int max_width) const
323  {
324  switch (pe.type) {
325  case SPET_TEXT:
326  SetDParamStr(0, pe.text);
327  return GetStringHeight(STR_JUST_RAW_STRING, max_width);
328 
329  case SPET_GOAL:
330  case SPET_LOCATION: {
331  Dimension sprite_dim = GetSpriteSize(GetPageElementSprite(pe));
332  return sprite_dim.height;
333  }
334 
335  case SPET_BUTTON_PUSH:
336  case SPET_BUTTON_TILE:
337  case SPET_BUTTON_VEHICLE: {
340  }
341 
342  default:
343  NOT_REACHED();
344  }
345  return 0;
346  }
347 
353  ElementFloat GetPageElementFloat(const StoryPageElement &pe) const
354  {
355  switch (pe.type) {
356  case SPET_BUTTON_PUSH:
357  case SPET_BUTTON_TILE:
358  case SPET_BUTTON_VEHICLE: {
360  if (flags & SPBF_FLOAT_LEFT) return ElementFloat::Left;
361  if (flags & SPBF_FLOAT_RIGHT) return ElementFloat::Right;
362  return ElementFloat::None;
363  }
364 
365  default:
366  return ElementFloat::None;
367  }
368  }
369 
376  {
377  switch (pe.type) {
378  case SPET_BUTTON_PUSH:
379  case SPET_BUTTON_TILE:
380  case SPET_BUTTON_VEHICLE: {
383  }
384 
385  default:
386  NOT_REACHED(); // only buttons can float
387  }
388  }
389 
392  {
393  this->layout_cache.clear();
394  }
395 
398  {
399  /* Assume if the layout cache has contents it is valid */
400  if (!this->layout_cache.empty()) return;
401 
402  StoryPage *page = this->GetSelPage();
403  if (page == nullptr) return;
404  int max_width = GetAvailablePageContentWidth();
405  int element_dist = GetCharacterHeight(FS_NORMAL);
406 
407  /* Make space for the header */
408  int main_y = GetHeadHeight(max_width) + element_dist;
409 
410  /* Current bottom of left/right column */
411  int left_y = main_y;
412  int right_y = main_y;
413  /* Current width of left/right column, 0 indicates no content in column */
414  int left_width = 0;
415  int right_width = 0;
416  /* Indexes into element cache for yet unresolved floats */
417  std::vector<size_t> left_floats;
418  std::vector<size_t> right_floats;
419 
420  /* Build layout */
421  for (const StoryPageElement *pe : this->story_page_elements) {
422  ElementFloat fl = this->GetPageElementFloat(*pe);
423 
424  if (fl == ElementFloat::None) {
425  /* Verify available width */
426  const int min_required_width = 10 * GetCharacterHeight(FS_NORMAL);
427  int left_offset = (left_width == 0) ? 0 : (left_width + element_dist);
428  int right_offset = (right_width == 0) ? 0 : (right_width + element_dist);
429  if (left_offset + right_offset + min_required_width >= max_width) {
430  /* Width of floats leave too little for main content, push down */
431  main_y = std::max(main_y, left_y);
432  main_y = std::max(main_y, right_y);
433  left_width = right_width = 0;
434  left_offset = right_offset = 0;
435  /* Do not add element_dist here, to keep together elements which were supposed to float besides each other. */
436  }
437  /* Determine height */
438  const int available_width = max_width - left_offset - right_offset;
439  const int height = GetPageElementHeight(*pe, available_width);
440  /* Check for button that needs extra margin */
441  if (left_offset == 0 && right_offset == 0) {
442  switch (pe->type) {
443  case SPET_BUTTON_PUSH:
444  case SPET_BUTTON_TILE:
445  case SPET_BUTTON_VEHICLE:
446  left_offset = right_offset = available_width / 5;
447  break;
448  default:
449  break;
450  }
451  }
452  /* Position element in main column */
453  LayoutCacheElement ce{ pe, {} };
454  ce.bounds.left = left_offset;
455  ce.bounds.right = max_width - right_offset;
456  ce.bounds.top = main_y;
457  main_y += height;
458  ce.bounds.bottom = main_y;
459  this->layout_cache.push_back(ce);
460  main_y += element_dist;
461  /* Clear all floats */
462  left_width = right_width = 0;
463  left_y = right_y = main_y = std::max({main_y, left_y, right_y});
464  left_floats.clear();
465  right_floats.clear();
466  } else {
467  /* Prepare references to correct column */
468  int &cur_width = (fl == ElementFloat::Left) ? left_width : right_width;
469  int &cur_y = (fl == ElementFloat::Left) ? left_y : right_y;
470  std::vector<size_t> &cur_floats = (fl == ElementFloat::Left) ? left_floats : right_floats;
471  /* Position element */
472  cur_width = std::max(cur_width, this->GetPageElementFloatWidth(*pe));
473  LayoutCacheElement ce{ pe, {} };
474  ce.bounds.left = (fl == ElementFloat::Left) ? 0 : (max_width - cur_width);
475  ce.bounds.right = (fl == ElementFloat::Left) ? cur_width : max_width;
476  ce.bounds.top = cur_y;
477  cur_y += GetPageElementHeight(*pe, cur_width);
478  ce.bounds.bottom = cur_y;
479  cur_floats.push_back(this->layout_cache.size());
480  this->layout_cache.push_back(ce);
481  cur_y += element_dist;
482  /* Update floats in column to all have the same width */
483  for (size_t index : cur_floats) {
484  LayoutCacheElement &ce = this->layout_cache[index];
485  ce.bounds.left = (fl == ElementFloat::Left) ? 0 : (max_width - cur_width);
486  ce.bounds.right = (fl == ElementFloat::Left) ? cur_width : max_width;
487  }
488  }
489  }
490  }
491 
497  {
499 
500  /* The largest bottom coordinate of any element is the height of the content */
501  int32_t max_y = std::accumulate(this->layout_cache.begin(), this->layout_cache.end(), 0, [](int32_t max_y, const LayoutCacheElement &ce) -> int32_t { return std::max<int32_t>(max_y, ce.bounds.bottom); });
502 
503  return max_y;
504  }
505 
517  void DrawActionElement(int &y_offset, int width, int line_height, SpriteID action_sprite, StringID string_id = STR_JUST_RAW_STRING) const
518  {
519  Dimension sprite_dim = GetSpriteSize(action_sprite);
520  uint element_height = std::max(sprite_dim.height, (uint)line_height);
521 
522  uint sprite_top = y_offset + (element_height - sprite_dim.height) / 2;
523  uint text_top = y_offset + (element_height - line_height) / 2;
524 
525  DrawSprite(action_sprite, PAL_NONE, 0, sprite_top);
526  DrawString(sprite_dim.width + WidgetDimensions::scaled.frametext.left, width, text_top, string_id, TC_BLACK);
527 
528  y_offset += element_height;
529  }
530 
536  {
537  switch (pe.type) {
538  case SPET_TEXT:
539  /* Do nothing. */
540  break;
541 
542  case SPET_LOCATION:
543  if (_ctrl_pressed) {
545  } else {
547  }
548  break;
549 
550  case SPET_GOAL:
552  break;
553 
554  case SPET_BUTTON_PUSH:
555  if (this->active_button_id != INVALID_STORY_PAGE_ELEMENT) ResetObjectToPlace();
556  this->active_button_id = pe.index;
557  this->SetTimeout();
559 
561  break;
562 
563  case SPET_BUTTON_TILE:
564  if (this->active_button_id == pe.index) {
566  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
567  } else {
568  CursorID cursor = TranslateStoryPageButtonCursor(StoryPageButtonData{ pe.referenced_id }.GetCursor());
569  SetObjectToPlaceWnd(cursor, PAL_NONE, HT_RECT, this);
570  this->active_button_id = pe.index;
571  }
573  break;
574 
575  case SPET_BUTTON_VEHICLE:
576  if (this->active_button_id == pe.index) {
578  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
579  } else {
580  CursorID cursor = TranslateStoryPageButtonCursor(StoryPageButtonData{ pe.referenced_id }.GetCursor());
581  SetObjectToPlaceWnd(cursor, PAL_NONE, HT_VEHICLE, this);
582  this->active_button_id = pe.index;
583  }
585  break;
586 
587  default:
588  NOT_REACHED();
589  }
590  }
591 
592 public:
594  {
595  this->CreateNestedTree();
596  this->vscroll = this->GetScrollbar(WID_SB_SCROLLBAR);
597  this->vscroll->SetStepSize(GetCharacterHeight(FS_NORMAL));
598 
599  /* Initialize page sort. */
600  this->story_pages.SetSortFuncs(StoryBookWindow::page_sorter_funcs);
601  this->story_pages.ForceRebuild();
602  this->BuildStoryPageList();
603  this->story_page_elements.SetSortFuncs(StoryBookWindow::page_element_sorter_funcs);
604  /* story_page_elements will get built by SetSelectedPage */
605 
606  this->FinishInitNested(window_number);
607  this->owner = (Owner)this->window_number;
608 
609  /* Initialize selected vars. */
610  this->selected_generic_title.clear();
611  this->selected_page_id = INVALID_STORY_PAGE;
612 
613  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
614 
615  this->OnInvalidateData(-1);
616  }
617 
622  {
623  this->SetWidgetDisabledState(WID_SB_PREV_PAGE, story_pages.empty() || this->IsFirstPageSelected());
624  this->SetWidgetDisabledState(WID_SB_NEXT_PAGE, story_pages.empty() || this->IsLastPageSelected());
627  }
628 
633  void SetSelectedPage(uint16_t page_index)
634  {
635  if (this->selected_page_id != page_index) {
636  if (this->active_button_id) ResetObjectToPlace();
637  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
638  this->selected_page_id = page_index;
639  this->RefreshSelectedPage();
641  }
642  }
643 
644  void SetStringParameters(WidgetID widget) const override
645  {
646  switch (widget) {
647  case WID_SB_SEL_PAGE: {
648  StoryPage *page = this->GetSelPage();
649  SetDParamStr(0, page != nullptr && !page->title.empty() ? page->title : this->selected_generic_title);
650  break;
651  }
652  case WID_SB_CAPTION:
653  if (this->window_number == INVALID_COMPANY) {
654  SetDParam(0, STR_STORY_BOOK_SPECTATOR_CAPTION);
655  } else {
656  SetDParam(0, STR_STORY_BOOK_CAPTION);
657  SetDParam(1, this->window_number);
658  }
659  break;
660  }
661  }
662 
663  void OnPaint() override
664  {
665  /* Detect if content has changed height. This can happen if a
666  * multi-line text contains eg. {COMPANY} and that company is
667  * renamed.
668  */
669  if (this->vscroll->GetCount() != this->GetContentHeight()) {
670  this->vscroll->SetCount(this->GetContentHeight());
673  }
674 
675  this->DrawWidgets();
676  }
677 
678  void DrawWidget(const Rect &r, WidgetID widget) const override
679  {
680  if (widget != WID_SB_PAGE_PANEL) return;
681 
682  StoryPage *page = this->GetSelPage();
683  if (page == nullptr) return;
684 
685  Rect fr = r.Shrink(WidgetDimensions::scaled.frametext);
686 
687  /* Set up a clipping region for the panel. */
688  DrawPixelInfo tmp_dpi;
689  if (!FillDrawPixelInfo(&tmp_dpi, fr)) return;
690 
691  AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
692 
693  /* Draw content (now coordinates given to Draw** are local to the new clipping region). */
694  fr = fr.Translate(-fr.left, -fr.top);
695  int line_height = GetCharacterHeight(FS_NORMAL);
696  const int scrollpos = this->vscroll->GetPosition();
697  int y_offset = -scrollpos;
698 
699  /* Date */
700  if (page->date != CalendarTime::INVALID_DATE) {
701  SetDParam(0, page->date);
702  DrawString(0, fr.right, y_offset, STR_JUST_DATE_LONG, TC_BLACK);
703  }
704  y_offset += line_height;
705 
706  /* Title */
707  SetDParamStr(0, !page->title.empty() ? page->title : this->selected_generic_title);
708  y_offset = DrawStringMultiLine(0, fr.right, y_offset, fr.bottom, STR_STORY_BOOK_TITLE, TC_BLACK, SA_TOP | SA_HOR_CENTER);
709 
710  /* Page elements */
712  for (const LayoutCacheElement &ce : this->layout_cache) {
713  y_offset = ce.bounds.top - scrollpos;
714  switch (ce.pe->type) {
715  case SPET_TEXT:
716  SetDParamStr(0, ce.pe->text);
717  y_offset = DrawStringMultiLine(ce.bounds.left, ce.bounds.right, ce.bounds.top - scrollpos, ce.bounds.bottom - scrollpos, STR_JUST_RAW_STRING, TC_BLACK, SA_TOP | SA_LEFT);
718  break;
719 
720  case SPET_GOAL: {
721  Goal *g = Goal::Get((GoalID) ce.pe->referenced_id);
722  StringID string_id = g == nullptr ? STR_STORY_BOOK_INVALID_GOAL_REF : STR_JUST_RAW_STRING;
723  if (g != nullptr) SetDParamStr(0, g->text);
724  DrawActionElement(y_offset, ce.bounds.right - ce.bounds.left, line_height, GetPageElementSprite(*ce.pe), string_id);
725  break;
726  }
727 
728  case SPET_LOCATION:
729  SetDParamStr(0, ce.pe->text);
730  DrawActionElement(y_offset, ce.bounds.right - ce.bounds.left, line_height, GetPageElementSprite(*ce.pe));
731  break;
732 
733  case SPET_BUTTON_PUSH:
734  case SPET_BUTTON_TILE:
735  case SPET_BUTTON_VEHICLE: {
737  const FrameFlags frame = this->active_button_id == ce.pe->index ? FR_LOWERED : FR_NONE;
738  const Colours bgcolour = StoryPageButtonData{ ce.pe->referenced_id }.GetColour();
739 
740  DrawFrameRect(ce.bounds.left, ce.bounds.top - scrollpos, ce.bounds.right, ce.bounds.bottom - scrollpos - 1, bgcolour, frame);
741 
742  SetDParamStr(0, ce.pe->text);
743  DrawString(ce.bounds.left + WidgetDimensions::scaled.bevel.left, ce.bounds.right - WidgetDimensions::scaled.bevel.right, ce.bounds.top + tmargin - scrollpos, STR_JUST_RAW_STRING, TC_WHITE, SA_CENTER);
744  break;
745  }
746 
747  default: NOT_REACHED();
748  }
749  }
750  }
751 
752  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
753  {
754  if (widget != WID_SB_SEL_PAGE && widget != WID_SB_PAGE_PANEL) return;
755 
756  Dimension d;
757  d.height = GetCharacterHeight(FS_NORMAL);
758  d.width = 0;
759 
760  switch (widget) {
761  case WID_SB_SEL_PAGE: {
762 
763  /* Get max title width. */
764  for (size_t i = 0; i < this->story_pages.size(); i++) {
765  const StoryPage *s = this->story_pages[i];
766 
767  if (!s->title.empty()) {
768  SetDParamStr(0, s->title);
769  } else {
770  SetDParamStr(0, this->selected_generic_title);
771  }
772  Dimension title_d = GetStringBoundingBox(STR_JUST_RAW_STRING);
773 
774  if (title_d.width > d.width) {
775  d.width = title_d.width;
776  }
777  }
778 
779  d.width += padding.width;
780  d.height += padding.height;
781  size = maxdim(size, d);
782  break;
783  }
784 
785  case WID_SB_PAGE_PANEL: {
786  d.height *= 5;
787  d.height += padding.height + WidgetDimensions::scaled.frametext.Vertical();
788  size = maxdim(size, d);
789  break;
790  }
791  }
792 
793  }
794 
795  void OnResize() override
796  {
798  this->vscroll->SetCapacityFromWidget(this, WID_SB_PAGE_PANEL, WidgetDimensions::scaled.frametext.Vertical());
799  this->vscroll->SetCount(this->GetContentHeight());
800  }
801 
802  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
803  {
804  switch (widget) {
805  case WID_SB_SEL_PAGE: {
806  DropDownList list = this->BuildDropDownList();
807  if (!list.empty()) {
808  /* Get the index of selected page. */
809  int selected = 0;
810  for (size_t i = 0; i < this->story_pages.size(); i++) {
811  const StoryPage *p = this->story_pages[i];
812  if (p->index == this->selected_page_id) break;
813  selected++;
814  }
815 
816  ShowDropDownList(this, std::move(list), selected, widget);
817  }
818  break;
819  }
820 
821  case WID_SB_PREV_PAGE:
822  this->SelectPrevPage();
823  break;
824 
825  case WID_SB_NEXT_PAGE:
826  this->SelectNextPage();
827  break;
828 
829  case WID_SB_PAGE_PANEL: {
830  int clicked_y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SB_PAGE_PANEL, WidgetDimensions::scaled.frametext.top);
832 
833  for (const LayoutCacheElement &ce : this->layout_cache) {
834  if (clicked_y >= ce.bounds.top && clicked_y < ce.bounds.bottom && pt.x >= ce.bounds.left && pt.x < ce.bounds.right) {
835  this->OnPageElementClick(*ce.pe);
836  return;
837  }
838  }
839  }
840  }
841  }
842 
843  void OnDropdownSelect(WidgetID widget, int index) override
844  {
845  if (widget != WID_SB_SEL_PAGE) return;
846 
847  /* index (which is set in BuildDropDownList) is the page id. */
848  this->SetSelectedPage(index);
849  }
850 
858  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
859  {
860  if (!gui_scope) return;
861 
862  /* If added/removed page, force rebuild. Sort order never change so just a
863  * re-sort is never needed.
864  */
865  if (data == -1) {
866  this->story_pages.ForceRebuild();
867  this->BuildStoryPageList();
868 
869  /* Was the last page removed? */
870  if (this->story_pages.empty()) {
871  this->selected_generic_title.clear();
872  }
873 
874  /* Verify page selection. */
875  if (!_story_page_pool.IsValidID(this->selected_page_id)) {
876  this->selected_page_id = INVALID_STORY_PAGE;
877  }
878  if (this->selected_page_id == INVALID_STORY_PAGE && !this->story_pages.empty()) {
879  /* No page is selected, but there exist at least one available.
880  * => Select first page.
881  */
882  this->SetSelectedPage(this->story_pages[0]->index);
883  }
884 
885  this->SetWidgetDisabledState(WID_SB_SEL_PAGE, this->story_pages.empty());
888  } else if (data >= 0 && this->selected_page_id == data) {
889  this->RefreshSelectedPage();
890  }
891  }
892 
893  void OnTimeout() override
894  {
895  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
897  }
898 
899  void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
900  {
901  const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
902  if (pe == nullptr || pe->type != SPET_BUTTON_TILE) {
904  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
906  return;
907  }
908 
911  }
912 
913  bool OnVehicleSelect(const Vehicle *v) override
914  {
915  const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
916  if (pe == nullptr || pe->type != SPET_BUTTON_VEHICLE) {
918  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
920  return false;
921  }
922 
923  /* Check that the vehicle matches the requested type */
925  VehicleType wanted_vehtype = data.GetVehicleType();
926  if (wanted_vehtype != VEH_INVALID && wanted_vehtype != v->type) return false;
927 
930  return true;
931  }
932 
933  void OnPlaceObjectAbort() override
934  {
935  this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
937  }
938 };
939 
940 const std::initializer_list<GUIStoryPageList::SortFunction * const> StoryBookWindow::page_sorter_funcs = {
941  &PageOrderSorter,
942 };
943 
944 const std::initializer_list<GUIStoryPageElementList::SortFunction * const> StoryBookWindow::page_element_sorter_funcs = {
945  &PageElementOrderSorter,
946 };
947 
948 static constexpr NWidgetPart _nested_story_book_widgets[] = {
950  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
951  NWidget(WWT_CAPTION, COLOUR_BROWN, WID_SB_CAPTION), SetDataTip(STR_JUST_STRING1, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
952  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
953  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
954  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
955  EndContainer(),
958  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_SB_SCROLLBAR),
959  EndContainer(),
961  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_SB_PREV_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetDataTip(STR_STORY_BOOK_PREV_PAGE, STR_STORY_BOOK_PREV_PAGE_TOOLTIP),
962  NWidget(NWID_BUTTON_DROPDOWN, COLOUR_BROWN, WID_SB_SEL_PAGE), SetMinimalSize(93, 12), SetFill(1, 0),
963  SetDataTip(STR_JUST_RAW_STRING, STR_STORY_BOOK_SEL_PAGE_TOOLTIP), SetResize(1, 0),
964  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_SB_NEXT_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetDataTip(STR_STORY_BOOK_NEXT_PAGE, STR_STORY_BOOK_NEXT_PAGE_TOOLTIP),
965  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
966  EndContainer(),
967 };
968 
969 static WindowDesc _story_book_desc(
970  WDP_AUTO, "view_story", 400, 300,
972  0,
973  _nested_story_book_widgets
974 );
975 
976 static WindowDesc _story_book_gs_desc(
977  WDP_CENTER, "view_story_gs", 400, 300,
979  0,
980  _nested_story_book_widgets
981 );
982 
983 static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor)
984 {
985  switch (cursor) {
986  case SPBC_MOUSE: return SPR_CURSOR_MOUSE;
987  case SPBC_ZZZ: return SPR_CURSOR_ZZZ;
988  case SPBC_BUOY: return SPR_CURSOR_BUOY;
989  case SPBC_QUERY: return SPR_CURSOR_QUERY;
990  case SPBC_HQ: return SPR_CURSOR_HQ;
991  case SPBC_SHIP_DEPOT: return SPR_CURSOR_SHIP_DEPOT;
992  case SPBC_SIGN: return SPR_CURSOR_SIGN;
993  case SPBC_TREE: return SPR_CURSOR_TREE;
994  case SPBC_BUY_LAND: return SPR_CURSOR_BUY_LAND;
995  case SPBC_LEVEL_LAND: return SPR_CURSOR_LEVEL_LAND;
996  case SPBC_TOWN: return SPR_CURSOR_TOWN;
997  case SPBC_INDUSTRY: return SPR_CURSOR_INDUSTRY;
998  case SPBC_ROCKY_AREA: return SPR_CURSOR_ROCKY_AREA;
999  case SPBC_DESERT: return SPR_CURSOR_DESERT;
1000  case SPBC_TRANSMITTER: return SPR_CURSOR_TRANSMITTER;
1001  case SPBC_AIRPORT: return SPR_CURSOR_AIRPORT;
1002  case SPBC_DOCK: return SPR_CURSOR_DOCK;
1003  case SPBC_CANAL: return SPR_CURSOR_CANAL;
1004  case SPBC_LOCK: return SPR_CURSOR_LOCK;
1005  case SPBC_RIVER: return SPR_CURSOR_RIVER;
1006  case SPBC_AQUEDUCT: return SPR_CURSOR_AQUEDUCT;
1007  case SPBC_BRIDGE: return SPR_CURSOR_BRIDGE;
1008  case SPBC_RAIL_STATION: return SPR_CURSOR_RAIL_STATION;
1009  case SPBC_TUNNEL_RAIL: return SPR_CURSOR_TUNNEL_RAIL;
1010  case SPBC_TUNNEL_ELRAIL: return SPR_CURSOR_TUNNEL_ELRAIL;
1011  case SPBC_TUNNEL_MONO: return SPR_CURSOR_TUNNEL_MONO;
1012  case SPBC_TUNNEL_MAGLEV: return SPR_CURSOR_TUNNEL_MAGLEV;
1013  case SPBC_AUTORAIL: return SPR_CURSOR_AUTORAIL;
1014  case SPBC_AUTOELRAIL: return SPR_CURSOR_AUTOELRAIL;
1015  case SPBC_AUTOMONO: return SPR_CURSOR_AUTOMONO;
1016  case SPBC_AUTOMAGLEV: return SPR_CURSOR_AUTOMAGLEV;
1017  case SPBC_WAYPOINT: return SPR_CURSOR_WAYPOINT;
1018  case SPBC_RAIL_DEPOT: return SPR_CURSOR_RAIL_DEPOT;
1019  case SPBC_ELRAIL_DEPOT: return SPR_CURSOR_ELRAIL_DEPOT;
1020  case SPBC_MONO_DEPOT: return SPR_CURSOR_MONO_DEPOT;
1021  case SPBC_MAGLEV_DEPOT: return SPR_CURSOR_MAGLEV_DEPOT;
1022  case SPBC_CONVERT_RAIL: return SPR_CURSOR_CONVERT_RAIL;
1023  case SPBC_CONVERT_ELRAIL: return SPR_CURSOR_CONVERT_ELRAIL;
1024  case SPBC_CONVERT_MONO: return SPR_CURSOR_CONVERT_MONO;
1025  case SPBC_CONVERT_MAGLEV: return SPR_CURSOR_CONVERT_MAGLEV;
1026  case SPBC_AUTOROAD: return SPR_CURSOR_AUTOROAD;
1027  case SPBC_AUTOTRAM: return SPR_CURSOR_AUTOTRAM;
1028  case SPBC_ROAD_DEPOT: return SPR_CURSOR_ROAD_DEPOT;
1029  case SPBC_BUS_STATION: return SPR_CURSOR_BUS_STATION;
1030  case SPBC_TRUCK_STATION: return SPR_CURSOR_TRUCK_STATION;
1031  case SPBC_ROAD_TUNNEL: return SPR_CURSOR_ROAD_TUNNEL;
1032  case SPBC_CLONE_TRAIN: return SPR_CURSOR_CLONE_TRAIN;
1033  case SPBC_CLONE_ROADVEH: return SPR_CURSOR_CLONE_ROADVEH;
1034  case SPBC_CLONE_SHIP: return SPR_CURSOR_CLONE_SHIP;
1035  case SPBC_CLONE_AIRPLANE: return SPR_CURSOR_CLONE_AIRPLANE;
1036  case SPBC_DEMOLISH: return ANIMCURSOR_DEMOLISH;
1037  case SPBC_LOWERLAND: return ANIMCURSOR_LOWERLAND;
1038  case SPBC_RAISELAND: return ANIMCURSOR_RAISELAND;
1039  case SPBC_PICKSTATION: return ANIMCURSOR_PICKSTATION;
1040  case SPBC_BUILDSIGNALS: return ANIMCURSOR_BUILDSIGNALS;
1041  default: return SPR_CURSOR_QUERY;
1042  }
1043 }
1044 
1051 void ShowStoryBook(CompanyID company, uint16_t page_id, bool centered)
1052 {
1053  if (!Company::IsValidID(company)) company = (CompanyID)INVALID_COMPANY;
1054 
1055  StoryBookWindow *w = AllocateWindowDescFront<StoryBookWindow>(centered ? _story_book_gs_desc : _story_book_desc, company, true);
1056  if (page_id != INVALID_STORY_PAGE) w->SetSelectedPage(page_id);
1057 }
void RebuildDone()
Notify the sortlist that the rebuild is done.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
Scrollbar data structure.
Definition: widget_type.h:694
void SetCount(size_t num)
Sets the number of elements in the list.
Definition: widget_type.h:780
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition: widget.cpp:2320
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition: widget.cpp:2394
size_type GetCount() const
Gets the number of elements in the list.
Definition: widget_type.h:722
void SetStepSize(size_t stepsize)
Set the distance to scroll when using the buttons or the wheel.
Definition: widget_type.h:768
size_type GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:740
static constexpr TimerGame< struct Calendar >::Date INVALID_DATE
Representation of an invalid date.
RectPadding framerect
Standard padding inside many panels.
Definition: window_gui.h:42
RectPadding frametext
Padding inside frame with text.
Definition: window_gui.h:43
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition: window_gui.h:68
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition: window_gui.h:40
Functions related to commands.
Definition of stuff that is very close to a company, like the company struct itself.
Functions related to companies.
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ INVALID_COMPANY
An invalid company.
Definition: company_type.h:30
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition: dropdown.cpp:404
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
Definition: dropdown_type.h:50
@ None
No flag set.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition: fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition: gfx.cpp:704
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition: gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:851
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition: gfx.cpp:657
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:38
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition: gfx.cpp:988
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition: gfx.cpp:774
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition: gfx.cpp:1548
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition: gfx_type.h:18
@ SA_TOP
Top align the text.
Definition: gfx_type.h:348
@ SA_LEFT
Left align the text.
Definition: gfx_type.h:343
@ SA_HOR_CENTER
Horizontally center the text.
Definition: gfx_type.h:344
@ SA_CENTER
Center both horizontally and vertically.
Definition: gfx_type.h:353
uint32_t CursorID
The number of the cursor (sprite)
Definition: gfx_type.h:20
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:209
Goal base class.
void ShowGoalsList(CompanyID company)
Open a goal list window.
Definition: goal_gui.cpp:316
uint16_t GoalID
ID of a goal.
Definition: goal_type.h:37
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
Definition: widget_type.h:1181
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1284
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1202
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
Definition: widget_type.h:1137
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
Definition: widget_type.h:1309
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition: widget_type.h:1191
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition: widget_type.h:1126
GUI functions that shouldn't be here.
void ShowExtraViewportWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
A number of safeguards to prevent using unsafe methods.
Base types for having sorted lists in GUIs.
This file contains all sprite-related enums and defines.
static const CursorID ANIMCURSOR_PICKSTATION
716 - 718 - goto-order icon
Definition: sprites.h:1511
static const CursorID ANIMCURSOR_BUILDSIGNALS
1292 - 1293 - build signal
Definition: sprites.h:1512
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition: sprites.h:1390
static const CursorID ANIMCURSOR_DEMOLISH
704 - 707 - demolish dynamite
Definition: sprites.h:1508
static const CursorID ANIMCURSOR_LOWERLAND
699 - 701 - lower land tool
Definition: sprites.h:1509
static const CursorID ANIMCURSOR_RAISELAND
696 - 698 - raise land tool
Definition: sprites.h:1510
Definition of base types and functions in a cross-platform compatible way.
StoryPage base class.
StoryPageButtonFlags
Flags available for buttons.
Definition: story_base.h:42
@ SPET_LOCATION
An element that references a tile along with a one-line text.
Definition: story_base.h:32
@ SPET_GOAL
An element that references a goal.
Definition: story_base.h:33
@ SPET_BUTTON_PUSH
A push button that triggers an immediate event.
Definition: story_base.h:34
@ SPET_BUTTON_TILE
A button that allows the player to select a tile, and triggers an event with the tile.
Definition: story_base.h:35
@ SPET_TEXT
A text element.
Definition: story_base.h:31
@ SPET_BUTTON_VEHICLE
A button that allows the player to select a vehicle, and triggers an event wih the vehicle.
Definition: story_base.h:36
StoryPageButtonCursor
Mouse cursors usable by story page buttons.
Definition: story_base.h:50
Command definitions related to stories.
void ShowStoryBook(CompanyID company, uint16_t page_id, bool centered)
Raise or create the story book window for company, at page page_id.
Definition: story_gui.cpp:1051
uint16_t StoryPageID
ID of a story page.
Definition: story_type.h:16
uint16_t StoryPageElementID
ID of a story page element.
Definition: story_type.h:15
Types related to the story widgets.
@ WID_SB_PAGE_PANEL
Page body.
Definition: story_widget.h:18
@ WID_SB_SEL_PAGE
Page selector.
Definition: story_widget.h:17
@ WID_SB_SCROLLBAR
Scrollbar of the goal list.
Definition: story_widget.h:19
@ WID_SB_CAPTION
Caption of the window.
Definition: story_widget.h:16
@ WID_SB_PREV_PAGE
Prev button.
Definition: story_widget.h:20
@ WID_SB_NEXT_PAGE
Next button.
Definition: story_widget.h:21
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings.cpp:104
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition: strings.cpp:319
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition: strings.cpp:357
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
VehicleType type
Type of vehicle.
Definition: vehicle_type.h:51
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition: gfx_type.h:157
Struct about goals, current and completed.
Definition: goal_base.h:21
bool completed
Is the goal completed or not?
Definition: goal_base.h:27
std::string text
Text of the goal.
Definition: goal_base.h:25
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:1075
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:328
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:350
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:123
Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:112
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
void OnTimeout() override
Called when this window's timeout has been reached.
Definition: story_gui.cpp:893
void BuildStoryPageList()
(Re)Build story page list.
Definition: story_gui.cpp:69
void OnPlaceObjectAbort() override
The user cancelled a tile highlight mode that has been set.
Definition: story_gui.cpp:933
std::string selected_generic_title
If the selected page doesn't have a custom title, this buffer is used to store a generic page title.
Definition: story_gui.cpp:61
void OnPageElementClick(const StoryPageElement &pe)
Internal event handler for when a page element is clicked.
Definition: story_gui.cpp:535
void OnResize() override
Called after the window got resized.
Definition: story_gui.cpp:795
static bool PageElementOrderSorter(const StoryPageElement *const &a, const StoryPageElement *const &b)
Sort story page elements by order value.
Definition: story_gui.cpp:115
uint GetAvailablePageContentWidth() const
Get the width available for displaying content on the page panel.
Definition: story_gui.cpp:269
void OnPaint() override
The window must be repainted.
Definition: story_gui.cpp:663
StoryPage * GetSelPage() const
Get instance of selected page.
Definition: story_gui.cpp:134
bool IsFirstPageSelected()
Check if the selected page is also the first available page.
Definition: story_gui.cpp:159
DropDownList BuildDropDownList() const
Builds the page selector drop down list.
Definition: story_gui.cpp:247
int GetPageElementFloatWidth(const StoryPageElement &pe) const
Get the width a page element would use if it was floating left or right.
Definition: story_gui.cpp:375
LayoutCache layout_cache
Cached element layout.
Definition: story_gui.cpp:56
void UpdatePrevNextDisabledState()
Updates the disabled state of the prev/next buttons.
Definition: story_gui.cpp:621
Scrollbar * vscroll
Scrollbar of the page text.
Definition: story_gui.cpp:55
void SelectNextPage()
Selects the next available page after the currently selected page.
Definition: story_gui.cpp:226
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
Definition: story_gui.cpp:858
void SelectPrevPage()
Selects the previous available page before the currently selected page.
Definition: story_gui.cpp:206
void DrawActionElement(int &y_offset, int width, int line_height, SpriteID action_sprite, StringID string_id=STR_JUST_RAW_STRING) const
Draws a page element that is composed of a sprite to the left and a single line of text after that.
Definition: story_gui.cpp:517
void RefreshSelectedPage()
Updates the content of selected page.
Definition: story_gui.cpp:183
StoryPageID selected_page_id
Pool index of selected page.
Definition: story_gui.cpp:60
bool IsLastPageSelected()
Check if the selected page is also the last available page.
Definition: story_gui.cpp:170
GUIStoryPageList story_pages
Sorted list of pages.
Definition: story_gui.cpp:58
void InvalidateStoryPageElementLayout()
Invalidate the current page layout.
Definition: story_gui.cpp:391
void BuildStoryPageElementList()
(Re)Build story page element list.
Definition: story_gui.cpp:93
static bool PageOrderSorter(const StoryPage *const &a, const StoryPage *const &b)
Sort story pages by order value.
Definition: story_gui.cpp:87
StoryPageElementID active_button_id
Which button element the player is currently using.
Definition: story_gui.cpp:63
int32_t GetContentHeight()
Get the total height of the content displayed in this window.
Definition: story_gui.cpp:496
uint GetHeadHeight(int max_width) const
Counts how many pixels of height that are used by Date and Title (excluding marginal after Title,...
Definition: story_gui.cpp:281
uint GetPageElementHeight(const StoryPageElement &pe, int max_width) const
Get the height in pixels used by a page element.
Definition: story_gui.cpp:322
int GetSelPageNum() const
Get the page number of selected page.
Definition: story_gui.cpp:144
void SetSelectedPage(uint16_t page_index)
Sets the selected page.
Definition: story_gui.cpp:633
SpriteID GetPageElementSprite(const StoryPageElement &pe) const
Decides which sprite to display for a given page element.
Definition: story_gui.cpp:301
void EnsureStoryPageElementLayout() const
Create the page layout if it is missing.
Definition: story_gui.cpp:397
GUIStoryPageElementList story_page_elements
Sorted list of page elements that belong to the current page.
Definition: story_gui.cpp:59
ElementFloat GetPageElementFloat(const StoryPageElement &pe) const
Get the float style of a page element.
Definition: story_gui.cpp:353
Helper to construct packed "id" values for button-type StoryPageElement.
Definition: story_base.h:122
Struct about story page elements.
Definition: story_base.h:144
uint32_t referenced_id
Id of referenced object (location, goal etc.)
Definition: story_base.h:149
std::string text
Static content text of page element.
Definition: story_base.h:150
uint32_t sort_value
A number that increases for every created story page element. Used for sorting. The id of a story pag...
Definition: story_base.h:145
StoryPageElementType type
Type of page element.
Definition: story_base.h:147
Struct about stories, current and completed.
Definition: story_base.h:164
uint32_t sort_value
A number that increases for every created story page. Used for sorting. The id of a story page is the...
Definition: story_base.h:165
CompanyID company
StoryPage is for a specific company; INVALID_COMPANY if it is global.
Definition: story_base.h:167
std::string title
Title of story page.
Definition: story_base.h:169
TimerGameCalendar::Date date
Date when the page was created.
Definition: story_base.h:166
Vehicle data structure.
Definition: vehicle_base.h:244
High level window description.
Definition: window_gui.h:159
Data structure for an opened window.
Definition: window_gui.h:273
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition: window.cpp:1733
void DrawWidgets() const
Paint all widgets of a window.
Definition: widget.cpp:731
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition: window.cpp:551
ResizeInfo resize
Resize information.
Definition: window_gui.h:314
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition: window.cpp:1723
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition: window_gui.h:316
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition: window_gui.h:361
WindowFlags flags
Window flags.
Definition: window_gui.h:300
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition: window.cpp:314
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition: window_gui.h:387
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
WindowNumber window_number
Window number within the window class.
Definition: window_gui.h:302
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 SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Window *w)
Change the cursor and mouse click/drag handling to a mode for performing special operations like tile...
Definition: viewport.cpp:3435
@ HT_RECT
rectangle (stations, depots, ...)
@ HT_VEHICLE
vehicle is accepted as target as well (bitmask)
Base class for all vehicles.
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
@ VEH_INVALID
Non-existing type of vehicle.
Definition: vehicle_type.h:35
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp:2515
Functions related to (drawing on) viewports.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition: widget.cpp:281
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition: widget_type.h:82
@ NWID_HORIZONTAL
Horizontal container.
Definition: widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition: widget_type.h:55
@ WWT_PANEL
Simple depressed panel.
Definition: widget_type.h:50
@ 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_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
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
FrameFlags
Flags to describe the look of the frame.
Definition: window_gui.h:24
@ FR_LOWERED
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
Definition: window_gui.h:28
@ WDP_CENTER
Center the window.
Definition: window_gui.h:148
@ WDP_AUTO
Find a place automatically.
Definition: window_gui.h:147
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
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:45
@ WC_STORY_BOOK
Story book; Window numbers:
Definition: window_type.h:296