From 918022da3ef59a3f25882404ff2b94dc49d44dda Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 25 Mar 2018 22:04:36 +0100 Subject: [PATCH] Get custom buttons working --- distribution/openrct2.d.ts | 24 +++++ src/openrct2-ui/interface/Widget.cpp | 18 +++- src/openrct2-ui/scripting/CustomWindow.cpp | 120 +++++++++++++++++++-- src/openrct2/interface/Window.h | 9 ++ 4 files changed, 158 insertions(+), 13 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index c9974c0dad..349bcffa70 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -122,6 +122,7 @@ export interface Park { bankLoan: number; maxBankLoan: number; + postMessage(message: string): void; postMessage(message: ParkMessage): void; } @@ -138,6 +139,28 @@ export interface Window { close(): void; } +export type WidgetType = + "button" | "dropdown"; + +export interface Widget { + type: WidgetType; + x: number; + y: number; + width: number; + height: number; +} + +export interface ButtonWidget extends Widget { + text: string; + onClick: () => void; +} + +export interface DropdownWidget extends Widget { + items: string[]; + selectedIndex: number; + onChanged: (index: number) => void; +} + export interface WindowDesc { classification: string; x?: number; @@ -148,6 +171,7 @@ export interface WindowDesc { id?: number; minWidth?: number; minHeight?: number; + widgets?: Widget[]; } export interface Ui { diff --git a/src/openrct2-ui/interface/Widget.cpp b/src/openrct2-ui/interface/Widget.cpp index 8e013ea215..b5cc9cfd16 100644 --- a/src/openrct2-ui/interface/Widget.cpp +++ b/src/openrct2-ui/interface/Widget.cpp @@ -361,8 +361,15 @@ static void widget_text_centred(rct_drawpixelinfo* dpi, rct_window* w, rct_widge else t = w->windowPos.y + widget->top; + auto stringId = widget->text; + void * formatArgs = gCommonFormatArgs; + if (widget->flags & WIDGET_FLAGS::TEXT_IS_STRING) + { + stringId = STR_STRING; + formatArgs = &widget->string; + } gfx_draw_string_centred_clipped( - dpi, widget->text, gCommonFormatArgs, colour, (l + r + 1) / 2 - 1, t, widget->right - widget->left - 2); + dpi, stringId, formatArgs, colour, (l + r + 1) / 2 - 1, t, widget->right - widget->left - 2); } /** @@ -399,7 +406,14 @@ static void widget_text(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex w else t = w->windowPos.y + widget->top; - gfx_draw_string_left_clipped(dpi, widget->text, gCommonFormatArgs, colour, l + 1, t, r - l); + auto stringId = widget->text; + void * formatArgs = gCommonFormatArgs; + if (widget->flags & WIDGET_FLAGS::TEXT_IS_STRING) + { + stringId = STR_STRING; + formatArgs = &widget->string; + } + gfx_draw_string_left_clipped(dpi, stringId, formatArgs, colour, l + 1, t, r - l); } /** diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp index 14d85bb9e3..3d4a67f6a1 100644 --- a/src/openrct2-ui/scripting/CustomWindow.cpp +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -29,14 +31,14 @@ namespace OpenRCT2::Ui::Windows WIDX_TITLE, WIDX_CLOSE, WIDX_CONTENT_PANEL, + WIDX_CUSTOM_BEGIN, }; - static rct_widget window_custom_widgets[] = { + static rct_widget CustomDefaultWidgets[] = { { WWT_FRAME, 0, 0, 0, 0, 0, 0xFFFFFFFF, STR_NONE }, // panel / background { WWT_CAPTION, 0, 1, 0, 1, 14, STR_STRING, STR_WINDOW_TITLE_TIP }, // title bar { WWT_CLOSEBOX, 0, 0, 0, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button { WWT_RESIZE, 1, 0, 0, 14, 0, 0xFFFFFFFF, STR_NONE }, // content panel - { WIDGETS_END }, }; static void window_custom_close(rct_window *w); @@ -79,6 +81,33 @@ namespace OpenRCT2::Ui::Windows nullptr }; + struct CustomWidgetDesc + { + // Properties + std::string Type; + sint32 X; + sint32 Y; + sint32 Width; + sint32 Height; + std::string Text; + + // Event handlers + DukValue OnClick; + + static CustomWidgetDesc FromDukValue(DukValue desc) + { + CustomWidgetDesc result; + result.Type = desc["type"].as_string(); + result.X = desc["x"].as_int(); + result.Y = desc["y"].as_int(); + result.Width = desc["width"].as_int(); + result.Height = desc["height"].as_int(); + result.Text = desc["text"].as_string(); + result.OnClick = desc["onClick"]; + return result; + } + }; + struct CustomWindowDesc { std::string Classification; @@ -92,6 +121,7 @@ namespace OpenRCT2::Ui::Windows std::optional MaxHeight; std::string Title; std::optional Id; + std::vector Widgets; CustomWindowDesc() = default; @@ -114,6 +144,14 @@ namespace OpenRCT2::Ui::Windows result.MaxHeight = GetOptionalInt(desc["maxHeight"]); result.Title = desc["title"].as_string(); result.Id = GetOptionalInt(desc["id"]); + + if (desc["widgets"].is_array()) + { + auto dukWidgets = desc["widgets"].as_array(); + std::transform(dukWidgets.begin(), dukWidgets.end(), std::back_inserter(result.Widgets), + [](const DukValue& w) { return CustomWidgetDesc::FromDukValue(w); }); + } + return std::move(result); } @@ -134,6 +172,7 @@ namespace OpenRCT2::Ui::Windows public: std::shared_ptr Owner; CustomWindowDesc Desc; + std::vector Widgets; CustomWindowInfo( rct_windowclass cls, @@ -154,6 +193,8 @@ namespace OpenRCT2::Ui::Windows static CustomWindowInfo& GetInfo(rct_window * w); static rct_windownumber GetNewWindowNumber(); + static void RefreshWidgets(rct_window * w); + static void InvokeEventHandler(std::shared_ptr owner, const DukValue& dukHandler); rct_window * window_custom_open(std::shared_ptr owner, DukValue dukDesc) { @@ -189,7 +230,6 @@ namespace OpenRCT2::Ui::Windows window->number = GetNewWindowNumber(); window->custom_info = new CustomWindowInfo(window->classification, window->number, owner, desc); - window->widgets = window_custom_widgets; window->enabled_widgets = (1 << WIDX_CLOSE); window->colours[0] = COLOUR_GREY; window->colours[1] = COLOUR_GREY; @@ -201,7 +241,7 @@ namespace OpenRCT2::Ui::Windows window->max_width = desc.MaxWidth.value_or(std::numeric_limits::max()); window->max_height = desc.MaxHeight.value_or(std::numeric_limits::max()); } - + RefreshWidgets(window); window_init_scroll_widgets(window); return window; } @@ -218,6 +258,13 @@ namespace OpenRCT2::Ui::Windows case WIDX_CLOSE: window_close(w); break; + default: + { + const auto& info = GetInfo(w); + const auto& widgetDesc = info.Desc.Widgets[widgetIndex - WIDX_CUSTOM_BEGIN]; + InvokeEventHandler(info.Owner, widgetDesc.OnClick); + break; + } } } @@ -241,13 +288,13 @@ namespace OpenRCT2::Ui::Windows static void window_custom_invalidate(rct_window * w) { - window_custom_widgets[WIDX_BACKGROUND].right = w->width - 1; - window_custom_widgets[WIDX_BACKGROUND].bottom = w->height - 1; - window_custom_widgets[WIDX_TITLE].right = w->width - 2; - window_custom_widgets[WIDX_CLOSE].left = w->width - 13; - window_custom_widgets[WIDX_CLOSE].right = w->width - 3; - window_custom_widgets[WIDX_CONTENT_PANEL].right = w->width - 1; - window_custom_widgets[WIDX_CONTENT_PANEL].bottom = w->height - 1; + w->widgets[WIDX_BACKGROUND].right = w->width - 1; + w->widgets[WIDX_BACKGROUND].bottom = w->height - 1; + w->widgets[WIDX_TITLE].right = w->width - 2; + w->widgets[WIDX_CLOSE].left = w->width - 13; + w->widgets[WIDX_CLOSE].right = w->width - 3; + w->widgets[WIDX_CONTENT_PANEL].right = w->width - 1; + w->widgets[WIDX_CONTENT_PANEL].bottom = w->height - 1; const auto& desc = GetInfo(w).Desc; set_format_arg(0, void *, desc.Title.c_str()); @@ -272,4 +319,55 @@ namespace OpenRCT2::Ui::Windows } return result; } + + static void RefreshWidgets(rct_window * w) + { + auto& info = GetInfo(w); + auto& widgets = info.Widgets; + + widgets.clear(); + + // Add default widgets (window shim) + widgets.insert(widgets.begin(), std::begin(CustomDefaultWidgets), std::end(CustomDefaultWidgets)); + w->enabled_widgets = 1ULL << WIDX_CLOSE; + + // Add custom widgets + for (const auto& widgetDesc : info.Desc.Widgets) + { + rct_widget widget{}; + widget.type = WWT_BUTTON; + widget.left = widgetDesc.X; + widget.top = widgetDesc.Y; + widget.right = widgetDesc.X + widgetDesc.Width; + widget.bottom = widgetDesc.Y + widgetDesc.Height; + widget.string = (utf8*)widgetDesc.Text.c_str(); + widget.tooltip = STR_NONE; + widget.flags = WIDGET_FLAGS::TEXT_IS_STRING; + widgets.push_back(widget); + + auto widgetIndex = widgets.size() - 1; + if (widgetIndex < 64) + { + w->enabled_widgets |= 1ULL << widgetIndex; + } + } + + widgets.push_back({ WIDGETS_END }); + w->widgets = widgets.data(); + } + + static void InvokeEventHandler(std::shared_ptr owner, const DukValue& dukHandler) + { + if (dukHandler.is_function()) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto& execInfo = scriptEngine.GetExecInfo(); + { + ScriptExecutionInfo::PluginScope scope(execInfo, owner); + dukHandler.push(); + duk_pcall(dukHandler.context(), 0); + duk_pop(dukHandler.context()); + } + } + } } diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 4fd71c4207..63a6ec4912 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -64,6 +64,12 @@ struct widget_identifier extern widget_identifier gCurrentTextBox; +using WidgetFlags = uint32; +namespace WIDGET_FLAGS +{ + const uint32 TEXT_IS_STRING = 1 << 0; +} + /** * Widget structure * size: 0x10 @@ -84,6 +90,9 @@ struct rct_widget utf8* string; }; rct_string_id tooltip; // 0x0E + + // New properties + WidgetFlags flags; }; /**