From 291143c3cd298342e950473cb3944d783d826762 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 25 Mar 2018 18:23:24 +0100 Subject: [PATCH] Add opening of a custom window --- distribution/openrct2.d.ts | 2 + src/openrct2-ui/scripting/CustomWindow.cpp | 275 +++++++++++++++++++++ src/openrct2-ui/scripting/ScUi.hpp | 35 ++- src/openrct2-ui/scripting/ScWindow.hpp | 5 + src/openrct2-ui/scripting/UiExtensions.cpp | 2 +- src/openrct2/interface/Window.h | 1 + src/openrct2/interface/Window_internal.h | 1 + src/openrct2/scripting/ScriptEngine.h | 1 + 8 files changed, 318 insertions(+), 4 deletions(-) create mode 100644 src/openrct2-ui/scripting/CustomWindow.cpp diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 610c9c4bfa..c9974c0dad 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -146,6 +146,8 @@ export interface WindowDesc { height: number; title: string; id?: number; + minWidth?: number; + minHeight?: number; } export interface Ui { diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp new file mode 100644 index 0000000000..14d85bb9e3 --- /dev/null +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -0,0 +1,275 @@ +/***************************************************************************** + * Copyright (c) 2014-2018 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ScUi.hpp" +#include "ScWindow.hpp" + +using namespace OpenRCT2; +using namespace OpenRCT2::Scripting; + +namespace OpenRCT2::Ui::Windows +{ + enum + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_CONTENT_PANEL, + }; + + static rct_widget window_custom_widgets[] = { + { 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); + static void window_custom_mouseup(rct_window *w, rct_widgetindex widgetIndex); + static void window_custom_resize(rct_window *w); + static void window_custom_scrollgetsize(rct_window *w, sint32 scrollIndex, sint32 *width, sint32 *height); + static void window_custom_invalidate(rct_window *w); + static void window_custom_paint(rct_window *w, rct_drawpixelinfo *dpi); + static void window_custom_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, sint32 scrollIndex); + + static rct_window_event_list window_custom_events = + { + window_custom_close, + window_custom_mouseup, + window_custom_resize, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + window_custom_invalidate, + window_custom_paint, + nullptr + }; + + struct CustomWindowDesc + { + std::string Classification; + std::optional X; + std::optional Y; + sint32 Width; + sint32 Height; + std::optional MinWidth; + std::optional MinHeight; + std::optional MaxWidth; + std::optional MaxHeight; + std::string Title; + std::optional Id; + + CustomWindowDesc() = default; + + bool IsResizable() const + { + return MinWidth || MinHeight || MaxWidth || MaxHeight; + } + + static CustomWindowDesc FromDukValue(DukValue desc) + { + CustomWindowDesc result; + result.Classification = desc["classification"].as_string(); + result.X = GetOptionalInt(desc["x"]); + result.Y = GetOptionalInt(desc["y"]); + result.Width = desc["width"].as_int(); + result.Height = desc["height"].as_int(); + result.MinWidth = GetOptionalInt(desc["minWidth"]); + result.MaxWidth = GetOptionalInt(desc["maxWidth"]); + result.MinHeight = GetOptionalInt(desc["minHeight"]); + result.MaxHeight = GetOptionalInt(desc["maxHeight"]); + result.Title = desc["title"].as_string(); + result.Id = GetOptionalInt(desc["id"]); + return std::move(result); + } + + static std::optional GetOptionalInt(DukValue input) + { + return input.type() == DukValue::Type::NUMBER ? + std::make_optional(input.as_int()) : + std::nullopt; + } + }; + + class CustomWindowInfo + { + private: + rct_windowclass _class; + rct_windownumber _number; + + public: + std::shared_ptr Owner; + CustomWindowDesc Desc; + + CustomWindowInfo( + rct_windowclass cls, + rct_windownumber number, + std::shared_ptr owner, + const CustomWindowDesc& desc) + : _class(cls), + _number(number), + Owner(owner), + Desc(desc) + { + } + + CustomWindowInfo(const CustomWindowInfo&) = delete; + }; + + static rct_windownumber _nextWindowNumber; + + static CustomWindowInfo& GetInfo(rct_window * w); + static rct_windownumber GetNewWindowNumber(); + + rct_window * window_custom_open(std::shared_ptr owner, DukValue dukDesc) + { + auto desc = CustomWindowDesc::FromDukValue(dukDesc); + + uint16 windowFlags = 0; + if (desc.IsResizable()) + { + windowFlags |= WF_RESIZABLE; + } + + rct_window * window; + if (desc.X && desc.Y) + { + window = window_create( + desc.X.value(), + desc.Y.value(), + desc.Width, + desc.Height, + &window_custom_events, + WC_CUSTOM, + windowFlags); + } + else + { + window = window_create_auto_pos( + desc.Width, + desc.Height, + &window_custom_events, + WC_CUSTOM, + windowFlags); + } + + 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; + window->colours[2] = COLOUR_GREY; + if (desc.IsResizable()) + { + window->min_width = desc.MinWidth.value_or(0); + window->min_height = desc.MinHeight.value_or(0); + window->max_width = desc.MaxWidth.value_or(std::numeric_limits::max()); + window->max_height = desc.MaxHeight.value_or(std::numeric_limits::max()); + } + + window_init_scroll_widgets(window); + return window; + } + + static void window_custom_close(rct_window * w) + { + delete static_cast(w->custom_info); + w->custom_info = nullptr; + } + + static void window_custom_mouseup(rct_window * w, rct_widgetindex widgetIndex) + { + switch (widgetIndex) { + case WIDX_CLOSE: + window_close(w); + break; + } + } + + static void window_custom_resize(rct_window * w) + { + const auto& desc = GetInfo(w).Desc; + if (desc.IsResizable()) + { + if (w->width < w->min_width) + { + window_invalidate(w); + w->width = w->min_width; + } + if (w->height < w->min_height) + { + window_invalidate(w); + w->height = w->min_height; + } + } + } + + 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; + + const auto& desc = GetInfo(w).Desc; + set_format_arg(0, void *, desc.Title.c_str()); + } + + static void window_custom_paint(rct_window * w, rct_drawpixelinfo * dpi) + { + window_draw_widgets(w, dpi); + } + + static CustomWindowInfo& GetInfo(rct_window * w) + { + return *(static_cast(w->custom_info)); + } + + static rct_windownumber GetNewWindowNumber() + { + auto result = _nextWindowNumber++; + while (window_find_by_number(WC_CUSTOM, result) != nullptr) + { + result++; + } + return result; + } +} diff --git a/src/openrct2-ui/scripting/ScUi.hpp b/src/openrct2-ui/scripting/ScUi.hpp index f3dea76ee1..04862be3f5 100644 --- a/src/openrct2-ui/scripting/ScUi.hpp +++ b/src/openrct2-ui/scripting/ScUi.hpp @@ -1,17 +1,35 @@ #pragma once -#include #include +#include #include +#include #include "../common.h" #include "../Context.h" #include "ScWindow.hpp" +namespace OpenRCT2::Scripting +{ + class Plugin; +} + +namespace OpenRCT2::Ui::Windows +{ + rct_window * window_custom_open(std::shared_ptr owner, DukValue dukDesc); +} + namespace OpenRCT2::Scripting { class ScUi { + private: + ScriptEngine& _scriptEngine; public: + ScUi(ScriptEngine& scriptEngine) + : _scriptEngine(scriptEngine) + { + } + sint32 width_get() { return context_get_width(); } sint32 height_get() { return context_get_height(); } sint32 windows_get() @@ -21,7 +39,18 @@ namespace OpenRCT2::Scripting std::shared_ptr openWindow(DukValue desc) { - return nullptr; + using namespace OpenRCT2::Ui::Windows; + + auto& execInfo = _scriptEngine.GetExecInfo(); + auto owner = execInfo.GetCurrentPlugin(); + + std::shared_ptr scWindow = nullptr; + auto w = window_custom_open(owner, desc); + if (w != nullptr) + { + scWindow = std::make_shared(w); + } + return scWindow; } void closeWindows(std::string classification, DukValue id) @@ -53,7 +82,7 @@ namespace OpenRCT2::Scripting { if (i == index) { - return std::make_shared(w->classification, w->number); + return std::make_shared(w); } i++; } diff --git a/src/openrct2-ui/scripting/ScWindow.hpp b/src/openrct2-ui/scripting/ScWindow.hpp index 40493029a0..a38b01eaba 100644 --- a/src/openrct2-ui/scripting/ScWindow.hpp +++ b/src/openrct2-ui/scripting/ScWindow.hpp @@ -14,6 +14,11 @@ namespace OpenRCT2::Scripting rct_windownumber _number; public: + ScWindow(rct_window * w) + { + ScWindow(w->classification, w->number); + } + ScWindow(rct_windowclass c, rct_windownumber n) : _class(c), _number(n) diff --git a/src/openrct2-ui/scripting/UiExtensions.cpp b/src/openrct2-ui/scripting/UiExtensions.cpp index 78e8ee318f..e7ea265d9a 100644 --- a/src/openrct2-ui/scripting/UiExtensions.cpp +++ b/src/openrct2-ui/scripting/UiExtensions.cpp @@ -9,7 +9,7 @@ void UiScriptExtensions::Extend(ScriptEngine& scriptEngine) { auto ctx = scriptEngine.GetContext(); - dukglue_register_global(ctx, std::make_shared(), "ui"); + dukglue_register_global(ctx, std::make_shared(scriptEngine), "ui"); ScUi::Register(ctx); ScWindow::Register(ctx); diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 07c84854fa..4fd71c4207 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -443,6 +443,7 @@ enum WC_EDITOR_SCENARIO_BOTTOM_TOOLBAR = 222, WC_CHAT = 223, WC_CONSOLE = 224, + WC_CUSTOM = 225, WC_NULL = 255, }; diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index 7fdcb62c39..1ba55eab3c 100644 --- a/src/openrct2/interface/Window_internal.h +++ b/src/openrct2/interface/Window_internal.h @@ -55,6 +55,7 @@ struct rct_window scenery_variables scenery; track_list_variables track_list; error_variables error; + void * custom_info; }; int16_t page; union diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 59457515d1..5a11d519e5 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -94,6 +94,7 @@ namespace OpenRCT2::Scripting duk_context * GetContext() { return _context; } HookEngine& GetHookEngine() { return _hookEngine; } + ScriptExecutionInfo& GetExecInfo() { return _execInfo; } void Update(); std::future Eval(const std::string &s);