OpenTTD Source 20260621-master-g720d10536d
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "window_gui.h"
12#include "strings_func.h"
13#include "gui.h"
14#include "story_base.h"
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 "goal_gui.h"
23#include "viewport_func.h"
24#include "window_func.h"
25#include "company_base.h"
26#include "tilehighlight_func.h"
27#include "vehicle_base.h"
28#include "story_cmd.h"
29
31
32#include "table/strings.h"
33#include "table/sprites.h"
34
35#include "safeguards.h"
36
37static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor);
38
39typedef GUIList<const StoryPage*> GUIStoryPageList;
40typedef GUIList<const StoryPageElement*> GUIStoryPageElementList;
41
42struct StoryBookWindow : Window {
43protected:
45 const StoryPageElement *pe;
46 Rect bounds;
47 };
48 typedef std::vector<LayoutCacheElement> LayoutCache;
49
50 enum class ElementFloat : uint8_t {
51 None,
52 Left,
53 Right,
54 };
55
56 Scrollbar *vscroll = nullptr;
57 mutable LayoutCache layout_cache{};
58
59 GUIStoryPageList story_pages{};
60 GUIStoryPageElementList story_page_elements{};
62 std::string selected_generic_title{};
63
65
66 static const std::initializer_list<GUIStoryPageList::SortFunction * const> page_sorter_funcs;
67 static const std::initializer_list<GUIStoryPageElementList::SortFunction * const> page_element_sorter_funcs;
68
71 {
72 if (this->story_pages.NeedRebuild()) {
73 this->story_pages.clear();
74
75 for (const StoryPage *p : StoryPage::Iterate()) {
76 if (this->IsPageAvailable(p)) {
77 this->story_pages.push_back(p);
78 }
79 }
80
81 this->story_pages.RebuildDone();
82 }
83
84 this->story_pages.Sort();
85 }
86
88 static bool PageOrderSorter(const StoryPage * const &a, const StoryPage * const &b)
89 {
90 return a->sort_value < b->sort_value;
91 }
92
95 {
96 if (this->story_page_elements.NeedRebuild()) {
97 this->story_page_elements.clear();
98
99 const StoryPage *p = GetSelPage();
100 if (p != nullptr) {
101 for (const StoryPageElement *pe : StoryPageElement::Iterate()) {
102 if (pe->page == p->index) {
103 this->story_page_elements.push_back(pe);
104 }
105 }
106 }
107
108 this->story_page_elements.RebuildDone();
109 }
110
111 this->story_page_elements.Sort();
113 }
114
116 static bool PageElementOrderSorter(const StoryPageElement * const &a, const StoryPageElement * const &b)
117 {
118 return a->sort_value < b->sort_value;
119 }
120
126 bool IsPageAvailable(const StoryPage *page) const
127 {
128 return page->company == CompanyID::Invalid() || page->company == this->window_number;
129 }
130
136 {
137 if (!StoryPage::IsValidID(selected_page_id)) return nullptr;
139 }
140
145 int GetSelPageNum() const
146 {
147 int page_number = 0;
148 for (const StoryPage *p : this->story_pages) {
149 if (p->index == this->selected_page_id) {
150 return page_number;
151 }
152 page_number++;
153 }
154 return -1;
155 }
156
162 {
163 /* Verify that the selected page exist. */
164 if (!StoryPage::IsValidID(this->selected_page_id)) return false;
165
166 return this->story_pages.front()->index == this->selected_page_id;
167 }
168
174 {
175 /* Verify that the selected page exist. */
176 if (!StoryPage::IsValidID(this->selected_page_id)) return false;
177
178 if (this->story_pages.size() <= 1) return true;
179 const StoryPage *last = this->story_pages.back();
180 return last->index == this->selected_page_id;
181 }
182
187 {
188 /* Generate generic title if selected page have no custom title. */
189 StoryPage *page = this->GetSelPage();
190 if (page != nullptr && page->title.empty()) {
191 selected_generic_title = GetString(STR_STORY_BOOK_GENERIC_PAGE_ITEM, GetSelPageNum() + 1);
192 }
193
194 this->story_page_elements.ForceRebuild();
196
197 if (this->active_button_id != StoryPageElementID::Invalid()) ResetObjectToPlace();
198
199 this->vscroll->SetCount(this->GetContentHeight());
203 }
204
209 {
210 if (!StoryPage::IsValidID(this->selected_page_id)) return;
211
212 /* Find the last available page which is previous to the current selected page. */
213 const StoryPage *last_available;
214 last_available = nullptr;
215 for (const StoryPage *p : this->story_pages) {
216 if (p->index == this->selected_page_id) {
217 if (last_available == nullptr) return; // No previous page available.
218 this->SetSelectedPage(last_available->index);
219 return;
220 }
221 last_available = p;
222 }
223 }
224
229 {
230 if (!StoryPage::IsValidID(this->selected_page_id)) return;
231
232 /* Find selected page. */
233 for (auto iter = this->story_pages.begin(); iter != this->story_pages.end(); iter++) {
234 const StoryPage *p = *iter;
235 if (p->index == this->selected_page_id) {
236 /* Select the page after selected page. */
237 iter++;
238 if (iter != this->story_pages.end()) {
239 this->SetSelectedPage((*iter)->index);
240 }
241 return;
242 }
243 }
244 }
245
251 {
252 DropDownList list;
253 uint16_t page_num = 1;
254 for (const StoryPage *p : this->story_pages) {
255 bool current_page = p->index == this->selected_page_id;
256 if (!p->title.empty()) {
257 list.push_back(MakeDropDownListStringItem(p->title.GetDecodedString(), p->index.base(), current_page));
258 } else {
259 /* No custom title => use a generic page title with page number. */
260 list.push_back(MakeDropDownListStringItem(GetString(STR_STORY_BOOK_GENERIC_PAGE_ITEM, page_num), p->index.base(), current_page));
261 }
262 page_num++;
263 }
264
265 return list;
266 }
267
273 {
274 return this->GetWidget<NWidgetCore>(WID_SB_PAGE_PANEL)->current_x - WidgetDimensions::scaled.frametext.Horizontal() - 1;
275 }
276
284 uint GetHeadHeight(int max_width) const
285 {
286 StoryPage *page = this->GetSelPage();
287 if (page == nullptr) return 0;
288 int height = 0;
289
290 /* Title lines */
291 height += GetCharacterHeight(FontSize::Normal); // Date always use exactly one line.
292 height += GetStringHeight(GetString(STR_STORY_BOOK_TITLE, !page->title.empty() ? page->title.GetDecodedString() : this->selected_generic_title), max_width);
293
294 return height;
295 }
296
304 {
305 switch (pe.type) {
308 if (g == nullptr) return SPR_IMG_GOAL_BROKEN_REF;
309 return g->completed ? SPR_IMG_GOAL_COMPLETED : SPR_IMG_GOAL;
310 }
312 return SPR_IMG_VIEW_LOCATION;
313 default:
314 NOT_REACHED();
315 }
316 }
317
324 uint GetPageElementHeight(const StoryPageElement &pe, int max_width) const
325 {
326 switch (pe.type) {
328 return GetStringHeight(pe.text.GetDecodedString(), max_width);
329
333 return sprite_dim.height;
334 }
335
340 return dim.height + WidgetDimensions::scaled.framerect.Vertical() + WidgetDimensions::scaled.frametext.Vertical();
341 }
342
343 default:
344 NOT_REACHED();
345 }
346 return 0;
347 }
348
354 ElementFloat GetPageElementFloat(const StoryPageElement &pe) const
355 {
356 switch (pe.type) {
361 if (flags.Test(StoryPageButtonFlag::FloatLeft)) return ElementFloat::Left;
362 if (flags.Test(StoryPageButtonFlag::FloatRight)) return ElementFloat::Right;
363 return ElementFloat::None;
364 }
365
366 default:
367 return ElementFloat::None;
368 }
369 }
370
377 {
378 switch (pe.type) {
383 return dim.width + WidgetDimensions::scaled.framerect.Vertical() + WidgetDimensions::scaled.frametext.Vertical();
384 }
385
386 default:
387 NOT_REACHED(); // only buttons can float
388 }
389 }
390
393 {
394 this->layout_cache.clear();
395 }
396
399 {
400 /* Assume if the layout cache has contents it is valid */
401 if (!this->layout_cache.empty()) return;
402
403 StoryPage *page = this->GetSelPage();
404 if (page == nullptr) return;
405 int max_width = GetAvailablePageContentWidth();
406 int element_dist = GetCharacterHeight(FontSize::Normal);
407
408 /* Make space for the header */
409 int main_y = GetHeadHeight(max_width) + element_dist;
410
411 /* Current bottom of left/right column */
412 int left_y = main_y;
413 int right_y = main_y;
414 /* Current width of left/right column, 0 indicates no content in column */
415 int left_width = 0;
416 int right_width = 0;
417 /* Indexes into element cache for yet unresolved floats */
418 std::vector<size_t> left_floats;
419 std::vector<size_t> right_floats;
420
421 /* Build layout */
422 for (const StoryPageElement *pe : this->story_page_elements) {
423 ElementFloat fl = this->GetPageElementFloat(*pe);
424
425 if (fl == ElementFloat::None) {
426 /* Verify available width */
427 const int min_required_width = 10 * GetCharacterHeight(FontSize::Normal);
428 int left_offset = (left_width == 0) ? 0 : (left_width + element_dist);
429 int right_offset = (right_width == 0) ? 0 : (right_width + element_dist);
430 if (left_offset + right_offset + min_required_width >= max_width) {
431 /* Width of floats leave too little for main content, push down */
432 main_y = std::max(main_y, left_y);
433 main_y = std::max(main_y, right_y);
434 left_width = right_width = 0;
435 left_offset = right_offset = 0;
436 /* Do not add element_dist here, to keep together elements which were supposed to float besides each other. */
437 }
438 /* Determine height */
439 const int available_width = max_width - left_offset - right_offset;
440 const int height = GetPageElementHeight(*pe, available_width);
441 /* Check for button that needs extra margin */
442 if (left_offset == 0 && right_offset == 0) {
443 switch (pe->type) {
447 left_offset = right_offset = available_width / 5;
448 break;
449 default:
450 break;
451 }
452 }
453 /* Position element in main column */
454 LayoutCacheElement ce{ pe, {} };
455 ce.bounds.left = left_offset;
456 ce.bounds.right = max_width - right_offset;
457 ce.bounds.top = main_y;
458 main_y += height;
459 ce.bounds.bottom = main_y;
460 this->layout_cache.push_back(ce);
461 main_y += element_dist;
462 /* Clear all floats */
463 left_width = right_width = 0;
464 left_y = right_y = main_y = std::max({main_y, left_y, right_y});
465 left_floats.clear();
466 right_floats.clear();
467 } else {
468 /* Prepare references to correct column */
469 int &cur_width = (fl == ElementFloat::Left) ? left_width : right_width;
470 int &cur_y = (fl == ElementFloat::Left) ? left_y : right_y;
471 std::vector<size_t> &cur_floats = (fl == ElementFloat::Left) ? left_floats : right_floats;
472 /* Position element */
473 cur_width = std::max(cur_width, this->GetPageElementFloatWidth(*pe));
474 LayoutCacheElement ce{ pe, {} };
475 ce.bounds.left = (fl == ElementFloat::Left) ? 0 : (max_width - cur_width);
476 ce.bounds.right = (fl == ElementFloat::Left) ? cur_width : max_width;
477 ce.bounds.top = cur_y;
478 cur_y += GetPageElementHeight(*pe, cur_width);
479 ce.bounds.bottom = cur_y;
480 cur_floats.push_back(this->layout_cache.size());
481 this->layout_cache.push_back(ce);
482 cur_y += element_dist;
483 /* Update floats in column to all have the same width */
484 for (size_t index : cur_floats) {
485 LayoutCacheElement &ce = this->layout_cache[index];
486 ce.bounds.left = (fl == ElementFloat::Left) ? 0 : (max_width - cur_width);
487 ce.bounds.right = (fl == ElementFloat::Left) ? cur_width : max_width;
488 }
489 }
490 }
491 }
492
498 {
500
501 /* The largest bottom coordinate of any element is the height of the content */
502 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); });
503
504 return max_y;
505 }
506
517 void DrawActionElement(int &y_offset, int width, int line_height, SpriteID action_sprite, const std::string &text) 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, text, TextColour::Black);
527
528 y_offset += element_height;
529 }
530
536 {
537 switch (pe.type) {
539 /* Do nothing. */
540 break;
541
543 if (_ctrl_pressed) {
545 } else {
547 }
548 break;
549
552 break;
553
555 if (this->active_button_id != StoryPageElementID::Invalid()) ResetObjectToPlace();
556 this->active_button_id = pe.index;
557 this->SetTimeout();
559
560 Command<Commands::StoryPageButton>::Post(TileIndex{}, pe.index, VehicleID::Invalid());
561 break;
562
564 if (this->active_button_id == pe.index) {
566 this->active_button_id = StoryPageElementID::Invalid();
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
576 if (this->active_button_id == pe.index) {
578 this->active_button_id = StoryPageElementID::Invalid();
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
592public:
594 {
595 this->CreateNestedTree();
596 this->vscroll = this->GetScrollbar(WID_SB_SCROLLBAR);
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 = this->window_number;
608
609 /* Initialize selected vars. */
610 this->selected_generic_title.clear();
611 this->selected_page_id = StoryPageID::Invalid();
612
613 this->active_button_id = StoryPageElementID::Invalid();
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
634 {
635 if (this->selected_page_id != page_index) {
636 if (this->active_button_id != 0) ResetObjectToPlace();
637 this->active_button_id = StoryPageElementID::Invalid();
638 this->selected_page_id = page_index;
639 this->RefreshSelectedPage();
641 }
642 }
643
644 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
645 {
646 switch (widget) {
647 case WID_SB_SEL_PAGE: {
648 const StoryPage *page = this->GetSelPage();
649 return page != nullptr && !page->title.empty() ? page->title.GetDecodedString() : this->selected_generic_title;
650 }
651
652 case WID_SB_CAPTION:
653 if (this->window_number == CompanyID::Invalid()) {
654 return GetString(STR_STORY_BOOK_SPECTATOR_CAPTION);
655 }
656 return GetString(STR_STORY_BOOK_CAPTION, this->window_number);
657
658 default:
659 return this->Window::GetWidgetString(widget, stringid);
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(FontSize::Normal);
696 const int scrollpos = this->vscroll->GetPosition();
697 int y_offset = -scrollpos;
698
699 /* Date */
700 if (page->date != CalendarTime::INVALID_DATE) {
701 DrawString(0, fr.right, y_offset, GetString(STR_JUST_DATE_LONG, page->date), TextColour::Black);
702 }
703 y_offset += line_height;
704
705 /* Title */
706 y_offset = DrawStringMultiLine(0, fr.right, y_offset, fr.bottom,
707 GetString(STR_STORY_BOOK_TITLE, !page->title.empty() ? page->title.GetDecodedString() : this->selected_generic_title), TextColour::Black, SA_TOP | SA_HOR_CENTER);
708
709 /* Page elements */
711 for (const LayoutCacheElement &ce : this->layout_cache) {
712 if (ce.bounds.bottom - scrollpos < fr.top) continue;
713
714 y_offset = ce.bounds.top - scrollpos;
715 if (y_offset > fr.bottom) return;
716
717 switch (ce.pe->type) {
719 DrawStringMultiLineWithClipping(ce.bounds.left, ce.bounds.right, ce.bounds.top - scrollpos, ce.bounds.bottom - scrollpos,
720 ce.pe->text.GetDecodedString(), TextColour::Black, SA_TOP | SA_LEFT);
721 break;
722
724 Goal *g = Goal::Get((GoalID) ce.pe->referenced_id);
725 DrawActionElement(y_offset, ce.bounds.right - ce.bounds.left, line_height, GetPageElementSprite(*ce.pe),
726 g == nullptr ? GetString(STR_STORY_BOOK_INVALID_GOAL_REF) : g->text.GetDecodedString());
727 break;
728 }
729
731 DrawActionElement(y_offset, ce.bounds.right - ce.bounds.left, line_height, GetPageElementSprite(*ce.pe),
732 ce.pe->text.GetDecodedString());
733 break;
734
738 const int tmargin = WidgetDimensions::scaled.bevel.top + WidgetDimensions::scaled.frametext.top;
739 const FrameFlags frame = this->active_button_id == ce.pe->index ? FrameFlag::Lowered : FrameFlags{};
740 const Colours bgcolour = StoryPageButtonData{ ce.pe->referenced_id }.GetColour();
741
742 DrawFrameRect(ce.bounds.left, ce.bounds.top - scrollpos, ce.bounds.right, ce.bounds.bottom - scrollpos - 1, bgcolour, frame);
743
744 DrawString(ce.bounds.left + WidgetDimensions::scaled.bevel.left, ce.bounds.right - WidgetDimensions::scaled.bevel.right, ce.bounds.top + tmargin - scrollpos,
745 ce.pe->text.GetDecodedString(), TextColour::White, SA_CENTER);
746 break;
747 }
748
749 default: NOT_REACHED();
750 }
751 }
752 }
753
754 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
755 {
756 if (widget != WID_SB_SEL_PAGE && widget != WID_SB_PAGE_PANEL) return;
757
758 Dimension d;
760 d.width = 0;
761
762 switch (widget) {
763 case WID_SB_SEL_PAGE: {
764
765 /* Get max title width. */
766 for (size_t i = 0; i < this->story_pages.size(); i++) {
767 const StoryPage *s = this->story_pages[i];
768 Dimension title_d = GetStringBoundingBox(s->title.empty() ? this->selected_generic_title : s->title.GetDecodedString());
769
770 if (title_d.width > d.width) {
771 d.width = title_d.width;
772 }
773 }
774
775 d.width += padding.width;
776 d.height += padding.height;
777 size = maxdim(size, d);
778 break;
779 }
780
781 case WID_SB_PAGE_PANEL: {
782 d.height *= 5;
783 d.height += padding.height + WidgetDimensions::scaled.frametext.Vertical();
784 size = maxdim(size, d);
785 break;
786 }
787 }
788
789 }
790
791 void OnResize() override
792 {
794 this->vscroll->SetCapacityFromWidget(this, WID_SB_PAGE_PANEL, WidgetDimensions::scaled.frametext.Vertical());
795 this->vscroll->SetCount(this->GetContentHeight());
796 }
797
798 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
799 {
800 switch (widget) {
801 case WID_SB_SEL_PAGE: {
802 DropDownList list = this->BuildDropDownList();
803 if (!list.empty()) {
804 /* Get the index of selected page. */
805 int selected = 0;
806 for (size_t i = 0; i < this->story_pages.size(); i++) {
807 const StoryPage *p = this->story_pages[i];
808 if (p->index == this->selected_page_id) break;
809 selected++;
810 }
811
812 ShowDropDownList(this, std::move(list), selected, widget);
813 }
814 break;
815 }
816
817 case WID_SB_PREV_PAGE:
818 this->SelectPrevPage();
819 break;
820
821 case WID_SB_NEXT_PAGE:
822 this->SelectNextPage();
823 break;
824
825 case WID_SB_PAGE_PANEL: {
826 int clicked_y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SB_PAGE_PANEL, WidgetDimensions::scaled.frametext.top);
828
829 for (const LayoutCacheElement &ce : this->layout_cache) {
830 if (clicked_y >= ce.bounds.top && clicked_y < ce.bounds.bottom && pt.x >= ce.bounds.left && pt.x < ce.bounds.right) {
831 this->OnPageElementClick(*ce.pe);
832 return;
833 }
834 }
835 }
836 }
837 }
838
839 void OnDropdownSelect(WidgetID widget, int index, int) override
840 {
841 if (widget != WID_SB_SEL_PAGE) return;
842
843 /* index (which is set in BuildDropDownList) is the page id. */
844 this->SetSelectedPage(static_cast<StoryPageID>(index));
845 }
846
854 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
855 {
856 if (!gui_scope) return;
857
858 /* If added/removed page, force rebuild. Sort order never change so just a
859 * re-sort is never needed.
860 */
861 if (data == -1) {
862 this->story_pages.ForceRebuild();
863 this->BuildStoryPageList();
864
865 /* Was the last page removed? */
866 if (this->story_pages.empty()) {
867 this->selected_generic_title.clear();
868 }
869
870 /* Verify page selection. */
871 if (!StoryPage::IsValidID(this->selected_page_id)) {
872 this->selected_page_id = StoryPageID::Invalid();
873 }
874 if (this->selected_page_id == StoryPageID::Invalid() && !this->story_pages.empty()) {
875 /* No page is selected, but there exist at least one available.
876 * => Select first page.
877 */
878 this->SetSelectedPage(this->story_pages[0]->index);
879 }
880
881 this->SetWidgetDisabledState(WID_SB_SEL_PAGE, this->story_pages.empty());
884 } else if (data >= 0 && this->selected_page_id == data) {
885 this->RefreshSelectedPage();
886 }
887 }
888
889 void OnTimeout() override
890 {
891 this->active_button_id = StoryPageElementID::Invalid();
893 }
894
895 void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
896 {
897 const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
898 if (pe == nullptr || pe->type != StoryPageElementType::ButtonTile) {
900 this->active_button_id = StoryPageElementID::Invalid();
902 return;
903 }
904
905 Command<Commands::StoryPageButton>::Post(tile, pe->index, VehicleID::Invalid());
907 }
908
909 bool OnVehicleSelect(const Vehicle *v) override
910 {
911 const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
912 if (pe == nullptr || pe->type != StoryPageElementType::ButtonVehicle) {
914 this->active_button_id = StoryPageElementID::Invalid();
916 return false;
917 }
918
919 /* Check that the vehicle matches the requested type */
920 StoryPageButtonData data{ pe->referenced_id };
921 VehicleType wanted_vehtype = data.GetVehicleType();
922 if (wanted_vehtype != VehicleType::Invalid && wanted_vehtype != v->type) return false;
923
924 Command<Commands::StoryPageButton>::Post(TileIndex{}, pe->index, v->index);
926 return true;
927 }
928
929 void OnPlaceObjectAbort() override
930 {
931 this->active_button_id = StoryPageElementID::Invalid();
933 }
934};
935
936const std::initializer_list<GUIStoryPageList::SortFunction * const> StoryBookWindow::page_sorter_funcs = {
937 &PageOrderSorter,
938};
939
940const std::initializer_list<GUIStoryPageElementList::SortFunction * const> StoryBookWindow::page_element_sorter_funcs = {
941 &PageElementOrderSorter,
942};
943
944static constexpr std::initializer_list<NWidgetPart> _nested_story_book_widgets = {
951 EndContainer(),
955 EndContainer(),
957 NWidget(WWT_TEXTBTN, Colours::Brown, WID_SB_PREV_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetStringTip(STR_STORY_BOOK_PREV_PAGE, STR_STORY_BOOK_PREV_PAGE_TOOLTIP),
959 SetToolTip(STR_STORY_BOOK_SEL_PAGE_TOOLTIP), SetResize(1, 0),
960 NWidget(WWT_TEXTBTN, Colours::Brown, WID_SB_NEXT_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetStringTip(STR_STORY_BOOK_NEXT_PAGE, STR_STORY_BOOK_NEXT_PAGE_TOOLTIP),
962 EndContainer(),
963};
964
967 WindowPosition::Automatic, "view_story", 400, 300,
968 WindowClass::StoryBook, WindowClass::None,
969 {},
970 _nested_story_book_widgets
971);
972
975 WindowPosition::Center, "view_story_gs", 400, 300,
976 WindowClass::StoryBook, WindowClass::None,
977 {},
978 _nested_story_book_widgets
979);
980
981static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor)
982{
983 switch (cursor) {
985 case StoryPageButtonCursor::Zzz: return SPR_CURSOR_ZZZ;
986 case StoryPageButtonCursor::Buoy: return SPR_CURSOR_BUOY;
987 case StoryPageButtonCursor::Query: return SPR_CURSOR_QUERY;
988 case StoryPageButtonCursor::HQ: return SPR_CURSOR_HQ;
989 case StoryPageButtonCursor::ShipDepot: return SPR_CURSOR_SHIP_DEPOT;
990 case StoryPageButtonCursor::Sign: return SPR_CURSOR_SIGN;
991 case StoryPageButtonCursor::Tree: return SPR_CURSOR_TREE;
992 case StoryPageButtonCursor::BuyLand: return SPR_CURSOR_BUY_LAND;
993 case StoryPageButtonCursor::LevelLand: return SPR_CURSOR_LEVEL_LAND;
994 case StoryPageButtonCursor::Town: return SPR_CURSOR_TOWN;
995 case StoryPageButtonCursor::Industry: return SPR_CURSOR_INDUSTRY;
996 case StoryPageButtonCursor::RockyArea: return SPR_CURSOR_ROCKY_AREA;
997 case StoryPageButtonCursor::Desert: return SPR_CURSOR_DESERT;
998 case StoryPageButtonCursor::Transmitter: return SPR_CURSOR_TRANSMITTER;
999 case StoryPageButtonCursor::Airport: return SPR_CURSOR_AIRPORT;
1000 case StoryPageButtonCursor::Dock: return SPR_CURSOR_DOCK;
1001 case StoryPageButtonCursor::Canal: return SPR_CURSOR_CANAL;
1002 case StoryPageButtonCursor::Lock: return SPR_CURSOR_LOCK;
1003 case StoryPageButtonCursor::River: return SPR_CURSOR_RIVER;
1004 case StoryPageButtonCursor::Aqueduct: return SPR_CURSOR_AQUEDUCT;
1005 case StoryPageButtonCursor::Bridge: return SPR_CURSOR_BRIDGE;
1006 case StoryPageButtonCursor::RailStation: return SPR_CURSOR_RAIL_STATION;
1007 case StoryPageButtonCursor::TunnelRail: return SPR_CURSOR_TUNNEL_RAIL;
1008 case StoryPageButtonCursor::TunnelElrail: return SPR_CURSOR_TUNNEL_ELRAIL;
1009 case StoryPageButtonCursor::TunnelMono: return SPR_CURSOR_TUNNEL_MONO;
1010 case StoryPageButtonCursor::TunnelMaglev: return SPR_CURSOR_TUNNEL_MAGLEV;
1011 case StoryPageButtonCursor::AutoRail: return SPR_CURSOR_AUTORAIL;
1012 case StoryPageButtonCursor::AutoElrail: return SPR_CURSOR_AUTOELRAIL;
1013 case StoryPageButtonCursor::AutoMono: return SPR_CURSOR_AUTOMONO;
1014 case StoryPageButtonCursor::AutoMaglev: return SPR_CURSOR_AUTOMAGLEV;
1015 case StoryPageButtonCursor::Waypoint: return SPR_CURSOR_WAYPOINT;
1016 case StoryPageButtonCursor::RailDepot: return SPR_CURSOR_RAIL_DEPOT;
1017 case StoryPageButtonCursor::ElrailDepot: return SPR_CURSOR_ELRAIL_DEPOT;
1018 case StoryPageButtonCursor::MonoDepot: return SPR_CURSOR_MONO_DEPOT;
1019 case StoryPageButtonCursor::MaglevDepot: return SPR_CURSOR_MAGLEV_DEPOT;
1020 case StoryPageButtonCursor::ConvertRail: return SPR_CURSOR_CONVERT_RAIL;
1021 case StoryPageButtonCursor::ConvertElrail: return SPR_CURSOR_CONVERT_ELRAIL;
1022 case StoryPageButtonCursor::ConvertMono: return SPR_CURSOR_CONVERT_MONO;
1023 case StoryPageButtonCursor::ConvertMaglev: return SPR_CURSOR_CONVERT_MAGLEV;
1024 case StoryPageButtonCursor::AutoRoad: return SPR_CURSOR_AUTOROAD;
1025 case StoryPageButtonCursor::AutoTram: return SPR_CURSOR_AUTOTRAM;
1026 case StoryPageButtonCursor::RoadDepot: return SPR_CURSOR_ROAD_DEPOT;
1027 case StoryPageButtonCursor::BusStation: return SPR_CURSOR_BUS_STATION;
1028 case StoryPageButtonCursor::TruckStation: return SPR_CURSOR_TRUCK_STATION;
1029 case StoryPageButtonCursor::RoadTunnel: return SPR_CURSOR_ROAD_TUNNEL;
1030 case StoryPageButtonCursor::CloneTrain: return SPR_CURSOR_CLONE_TRAIN;
1031 case StoryPageButtonCursor::CloneRoadVeh: return SPR_CURSOR_CLONE_ROADVEH;
1032 case StoryPageButtonCursor::CloneShip: return SPR_CURSOR_CLONE_SHIP;
1033 case StoryPageButtonCursor::CloneAirplane: return SPR_CURSOR_CLONE_AIRPLANE;
1039 default: return SPR_CURSOR_QUERY;
1040 }
1041}
1042
1049void ShowStoryBook(CompanyID company, StoryPageID page_id, bool centered)
1050{
1051 if (!Company::IsValidID(company)) company = (CompanyID)CompanyID::Invalid();
1052
1054 if (page_id != StoryPageID::Invalid()) w->SetSelectedPage(page_id);
1055}
@ None
Tile is not animated.
std::string GetDecodedString() const
Decode the encoded string.
Definition strings.cpp:207
List template of 'things' T to sort in a GUI.
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.
void SetCount(size_t num)
Sets the number of elements in the list.
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:2456
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:2530
size_type GetCount() const
Gets the number of elements in the list.
void SetStepSize(size_t stepsize)
Set the distance to scroll when using the buttons or the wheel.
size_type GetPosition() const
Gets the position of the first visible element in the list.
static constexpr TimerGame< struct Calendar >::Date INVALID_DATE
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
Functions related to commands.
Definition of stuff that is very close to a company, like the company struct itself.
Functions related to companies.
std::unique_ptr< DropDownListItem > MakeDropDownListStringItem(StringID str, int value, bool masked, bool shaded)
Creates new DropDownListStringItem.
Definition dropdown.cpp:49
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options, std::string *const persistent_filter_text)
Show a drop down list.
Definition dropdown.cpp:587
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.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:88
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:716
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:971
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:899
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:787
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
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:1037
bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw a multiline string, possibly over multiple lines, if the region is within the current display cl...
Definition gfx.cpp:872
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:1572
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ Normal
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ Right
Right.
Definition gfx_type.h:456
@ Left
Left.
Definition gfx_type.h:454
uint32_t CursorID
The number of the cursor (sprite).
Definition gfx_type.h:19
@ SA_TOP
Top align the text.
Definition gfx_type.h:441
@ SA_LEFT
Left align the text.
Definition gfx_type.h:436
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:437
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:446
Colours
One of 16 base colours used for companies and windows/widgets.
Definition gfx_type.h:284
@ Brown
Brown.
Definition gfx_type.h:299
@ White
White colour.
Definition gfx_type.h:331
@ Black
Black colour.
Definition gfx_type.h:335
Goal base class.
void ShowGoalsList(CompanyID company)
Open a goal list window.
Definition goal_gui.cpp:312
Goal GUI functions.
PoolID< uint16_t, struct GoalIDTag, 64000, 0xFFFF > GoalID
ID of a goal.
Definition goal_type.h:63
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
GUI functions that shouldn't be here.
void ShowExtraViewportWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
#define Point
Macro that prevents name conflicts between included headers.
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:1525
static const CursorID ANIMCURSOR_BUILDSIGNALS
1292 - 1293 - build signal
Definition sprites.h:1526
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1404
static const CursorID ANIMCURSOR_DEMOLISH
704 - 707 - demolish dynamite
Definition sprites.h:1522
static const CursorID ANIMCURSOR_LOWERLAND
699 - 701 - lower land tool
Definition sprites.h:1523
static const CursorID ANIMCURSOR_RAISELAND
696 - 698 - raise land tool
Definition sprites.h:1524
Definition of base types and functions in a cross-platform compatible way.
StoryPage base class.
@ Goal
An element that references a goal.
Definition story_base.h:32
@ ButtonTile
A button that allows the player to select a tile, and triggers an event with the tile.
Definition story_base.h:34
@ ButtonVehicle
A button that allows the player to select a vehicle, and triggers an event with the vehicle.
Definition story_base.h:35
@ ButtonPush
A push button that triggers an immediate event.
Definition story_base.h:33
@ Text
A text element.
Definition story_base.h:30
@ Location
An element that references a tile along with a one-line text.
Definition story_base.h:31
StoryPageButtonCursor
Mouse cursors usable by story page buttons.
Definition story_base.h:49
@ Desert
Use the Desert cursor.
Definition story_base.h:63
@ Dock
Use the Dock cursor.
Definition story_base.h:66
@ BuyLand
Use the Buy Land cursor.
Definition story_base.h:58
@ River
Use the River cursor.
Definition story_base.h:69
@ AutoRail
Use the Auto Rail cursor.
Definition story_base.h:77
@ LowerLand
Use the Lower Land cursor.
Definition story_base.h:101
@ Waypoint
Use the Waypoint cursor.
Definition story_base.h:81
@ TruckStation
Use the Truck Station cursor.
Definition story_base.h:94
@ ConvertMono
Use the Convert Mono cursor.
Definition story_base.h:88
@ PickStation
Use the Pick Station cursor.
Definition story_base.h:103
@ MonoDepot
Use the Mono Depot cursor.
Definition story_base.h:84
@ Sign
Use the Sign cursor.
Definition story_base.h:56
@ Transmitter
Use the Transmitter cursor.
Definition story_base.h:64
@ Tree
Use the Tree cursor.
Definition story_base.h:57
@ Demolish
Use the Demolish cursor.
Definition story_base.h:100
@ Industry
Use the Industry cursor.
Definition story_base.h:61
@ AutoMono
Use the Auto Mono cursor.
Definition story_base.h:79
@ BuildSignals
Use the Build Signals cursor.
Definition story_base.h:104
@ ConvertRail
Use the Convert Rail cursor.
Definition story_base.h:86
@ RockyArea
Use the RockyArea cursor.
Definition story_base.h:62
@ AutoTram
Use the Auto Tram cursor.
Definition story_base.h:91
@ RailDepot
Use the Rail Depot cursor.
Definition story_base.h:82
@ Query
Use the Query cursor.
Definition story_base.h:53
@ RailStation
Use the Rail Station cursor.
Definition story_base.h:72
@ RoadDepot
Use the Road Depot cursor.
Definition story_base.h:92
@ TunnelElrail
Use the Tunnel Elrail cursor.
Definition story_base.h:74
@ MaglevDepot
Use the Maglev Depot cursor.
Definition story_base.h:85
@ CloneAirplane
Use the Clone Airplane cursor.
Definition story_base.h:99
@ CloneRoadVeh
Use the Clone Road Veh cursor.
Definition story_base.h:97
@ RaiseLand
Use the Raise Land cursor.
Definition story_base.h:102
@ ConvertMaglev
Use the Convert Maglev cursor.
Definition story_base.h:89
@ CloneShip
Use the Clone Ship cursor.
Definition story_base.h:98
@ RoadTunnel
Use the Road Tunnel cursor.
Definition story_base.h:95
@ HQ
Use the HQ cursor.
Definition story_base.h:54
@ ShipDepot
Use the Ship Depot cursor.
Definition story_base.h:55
@ TunnelRail
Use the Tunnel Rail cursor.
Definition story_base.h:73
@ Canal
Use the Canal cursor.
Definition story_base.h:67
@ CloneTrain
Use the Clone Train cursor.
Definition story_base.h:96
@ TunnelMaglev
Use the Tunnel Maglev cursor.
Definition story_base.h:76
@ Buoy
Use the Buoy cursor.
Definition story_base.h:52
@ AutoRoad
Use the Auto Road cursor.
Definition story_base.h:90
@ Lock
Use the Lock cursor.
Definition story_base.h:68
@ BusStation
Use the Bus Station cursor.
Definition story_base.h:93
@ Aqueduct
Use the Aqueduct cursor.
Definition story_base.h:70
@ Town
Use the Town cursor.
Definition story_base.h:60
@ TunnelMono
Use the Tunnel Mono cursor.
Definition story_base.h:75
@ Zzz
Use the Zzz cursor.
Definition story_base.h:51
@ ConvertElrail
Use the Convert Elrail cursor.
Definition story_base.h:87
@ AutoElrail
Use the Auto Elrail cursor.
Definition story_base.h:78
@ LevelLand
Use the Level Land cursor.
Definition story_base.h:59
@ Bridge
Use the Bridge cursor.
Definition story_base.h:71
@ Airport
Use the Airport cursor.
Definition story_base.h:65
@ ElrailDepot
Use the Elrail Depot cursor.
Definition story_base.h:83
@ Mouse
Use the Mouse cursor.
Definition story_base.h:50
@ AutoMaglev
Use the Auto Maglev cursor.
Definition story_base.h:80
@ FloatLeft
Button will float on the left.
Definition story_base.h:41
@ FloatRight
Button will float on the right.
Definition story_base.h:42
EnumBitSet< StoryPageButtonFlag, uint8_t > StoryPageButtonFlags
Bitset of StoryPageButtonFlag elements.
Definition story_base.h:46
Command definitions related to stories.
void ShowStoryBook(CompanyID company, StoryPageID page_id, bool centered)
Raise or create the story book window for company, at page page_id.
static WindowDesc _story_book_desc(WindowPosition::Automatic, "view_story", 400, 300, WindowClass::StoryBook, WindowClass::None, {}, _nested_story_book_widgets)
Window definition for the story book window.
static WindowDesc _story_book_gs_desc(WindowPosition::Center, "view_story_gs", 400, 300, WindowClass::StoryBook, WindowClass::None, {}, _nested_story_book_widgets)
Window definition for the game script window.
PoolID< uint16_t, struct StoryPageIDTag, 64000, 0xFFFF > StoryPageID
ID of a story page.
Definition story_type.h:16
PoolID< uint16_t, struct StoryPageElementIDTag, 64000, 0xFFFF > StoryPageElementID
ID of a story page element.
Definition story_type.h:15
Types related to the story widgets.
@ WID_SB_PAGE_PANEL
Page body.
@ WID_SB_SEL_PAGE
Page selector.
@ WID_SB_SCROLLBAR
Scrollbar of the goal list.
@ WID_SB_CAPTION
Caption of the window.
@ WID_SB_PREV_PAGE
Prev button.
@ WID_SB_NEXT_PAGE
Next button.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
VehicleType type
Type of vehicle.
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:22
EncodedString text
Text of the goal.
Definition goal_base.h:26
bool completed
Is the goal completed or not?
Definition goal_base.h:28
static Pool::IterateWrapper< StoryPage > Iterate(size_t from=0)
static StoryPage * Get(auto index)
static StoryPageElement * GetIfValid(auto index)
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.
void BuildStoryPageList()
(Re)Build story page list.
Definition story_gui.cpp:70
void OnPlaceObjectAbort() override
The user cancelled a tile highlight mode that has been set.
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:62
void OnPageElementClick(const StoryPageElement &pe)
Internal event handler for when a page element is clicked.
void OnResize() override
Called after the window got resized.
static bool PageElementOrderSorter(const StoryPageElement *const &a, const StoryPageElement *const &b)
Sort story page elements by order value.
uint GetAvailablePageContentWidth() const
Get the width available for displaying content on the page panel.
void OnPaint() override
The window must be repainted.
void SetSelectedPage(StoryPageID page_index)
Sets the selected page.
bool IsFirstPageSelected()
Check if the selected page is also the first available page.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
DropDownList BuildDropDownList() const
Builds the page selector drop down list.
int GetPageElementFloatWidth(const StoryPageElement &pe) const
Get the width a page element would use if it was floating left or right.
LayoutCache layout_cache
Cached element layout.
Definition story_gui.cpp:57
void UpdatePrevNextDisabledState()
Updates the disabled state of the prev/next buttons.
Scrollbar * vscroll
Scrollbar of the page text.
Definition story_gui.cpp:56
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
void SelectNextPage()
Selects the next available page after the currently selected page.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
bool IsPageAvailable(const StoryPage *page) const
Checks if a given page should be visible in the story book.
void SelectPrevPage()
Selects the previous available page before the currently selected page.
void RefreshSelectedPage()
Updates the content of selected page.
StoryPageID selected_page_id
Pool index of selected page.
Definition story_gui.cpp:61
bool IsLastPageSelected()
Check if the selected page is also the last available page.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
GUIStoryPageList story_pages
Sorted list of pages.
Definition story_gui.cpp:59
void InvalidateStoryPageElementLayout()
Invalidate the current page layout.
void DrawActionElement(int &y_offset, int width, int line_height, SpriteID action_sprite, const std::string &text) const
Draws a page element that is composed of a sprite to the left and a single line of text after that.
void BuildStoryPageElementList()
(Re)Build story page element list.
Definition story_gui.cpp:94
StoryPage * GetSelPage() const
Get instance of selected page.
static bool PageOrderSorter(const StoryPage *const &a, const StoryPage *const &b)
Sort story pages by order value.
Definition story_gui.cpp:88
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
StoryPageElementID active_button_id
Which button element the player is currently using.
Definition story_gui.cpp:64
int32_t GetContentHeight()
Get the total height of the content displayed in this window.
uint GetHeadHeight(int max_width) const
Counts how many pixels of height that are used by Date and Title (excluding marginal after Title,...
uint GetPageElementHeight(const StoryPageElement &pe, int max_width) const
Get the height in pixels used by a page element.
int GetSelPageNum() const
Get the page number of selected page.
void OnPlaceObject(Point pt, TileIndex tile) override
The user clicked some place on the map when a tile highlight mode has been set.
SpriteID GetPageElementSprite(const StoryPageElement &pe) const
Decides which sprite to display for a given page element.
void EnsureStoryPageElementLayout() const
Create the page layout if it is missing.
GUIStoryPageElementList story_page_elements
Sorted list of page elements that belong to the current page.
Definition story_gui.cpp:60
ElementFloat GetPageElementFloat(const StoryPageElement &pe) const
Get the float style of a page element.
Helper to construct packed "id" values for button-type StoryPageElement.
Definition story_base.h:121
Colours GetColour() const
Get the button background colour.
Definition story.cpp:173
VehicleType GetVehicleType() const
Get the type of vehicles that are accepted by the button.
Definition story.cpp:204
StoryPageButtonFlags GetFlags() const
Get the button flags.
Definition story.cpp:184
StoryPageButtonCursor GetCursor() const
Get the mouse cursor used while waiting for input for the button.
Definition story.cpp:193
Struct about story page elements.
Definition story_base.h:143
uint32_t referenced_id
Id of referenced object (location, goal etc.).
Definition story_base.h:148
EncodedString text
Static content text of page element.
Definition story_base.h:149
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:144
StoryPageElementType type
Type of page element.
Definition story_base.h:146
Struct about stories, current and completed.
Definition story_base.h:162
EncodedString title
Title of story page.
Definition story_base.h:167
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:163
CompanyID company
StoryPage is for a specific company; CompanyID::Invalid() if it is global.
Definition story_base.h:165
TimerGameCalendar::Date date
Date when the page was created.
Definition story_base.h:164
Vehicle data structure.
High level window description.
Definition window_gui.h:172
Number to differentiate different windows of the same class.
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:1814
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:786
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:562
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:510
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:1804
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:355
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1838
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:989
WindowFlags flags
Window flags.
Definition window_gui.h:300
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:319
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:381
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
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
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...
@ HT_RECT
rectangle (stations, depots, ...)
@ HT_VEHICLE
vehicle is accepted as target as well (bitmask)
Base class for all vehicles.
VehicleType
Available vehicle types.
@ Invalid
Non-existing type of vehicle.
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
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:309
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:74
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
EnumBitSet< FrameFlag, uint8_t > FrameFlags
Bitset of FrameFlag elements.
Definition window_gui.h:32
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed).
Definition window_gui.h:27
Twindow * AllocateWindowDescFront(WindowDesc &desc, WindowNumber window_number, Targs... extra_arguments)
Open a new window.
@ Automatic
Find a place automatically.
Definition window_gui.h:146
@ Center
Center the window.
Definition window_gui.h:147
int WidgetID
Widget ID.
Definition window_type.h:21