From c2183989a5682edcf6b458f403f56b2a5c540654 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 4 Feb 2021 00:25:34 +0000 Subject: [PATCH] Allow spinners to be clicked and held --- distribution/openrct2.d.ts | 8 +++++++ src/openrct2-ui/interface/Widget.cpp | 17 ++++++++++++++ src/openrct2-ui/scripting/CustomWindow.cpp | 19 ++++++++++++++-- src/openrct2-ui/scripting/ScWidget.hpp | 21 +++++++++++++++++ src/openrct2/interface/Widget.h | 2 ++ src/openrct2/interface/Window.h | 1 + src/openrct2/scripting/ScriptEngine.cpp | 26 +++++++++++++--------- 7 files changed, 82 insertions(+), 12 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 5a707d838a..421322eb84 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -2125,8 +2125,16 @@ declare global { interface SpinnerWidget extends WidgetBase { type: 'spinner'; text?: string; + + /** + * If true, the user can click and hold the decrement and increment + * button for repeated calls. + */ + isHoldable?: boolean; + onDecrement?: () => void; onIncrement?: () => void; + onClick?: () => void; } interface TextBoxWidget extends WidgetBase { diff --git a/src/openrct2-ui/interface/Widget.cpp b/src/openrct2-ui/interface/Widget.cpp index 5e04864630..9d30152d64 100644 --- a/src/openrct2-ui/interface/Widget.cpp +++ b/src/openrct2-ui/interface/Widget.cpp @@ -856,6 +856,11 @@ bool WidgetIsDisabled(rct_window* w, rct_widgetindex widgetIndex) return (w->disabled_widgets & (1LL << widgetIndex)) != 0; } +bool WidgetIsHoldable(rct_window* w, rct_widgetindex widgetIndex) +{ + return (w->hold_down_widgets & (1LL << widgetIndex)) != 0; +} + bool WidgetIsVisible(rct_window* w, rct_widgetindex widgetIndex) { return w->widgets[widgetIndex].IsVisible(); @@ -1050,6 +1055,18 @@ void WidgetSetDisabled(rct_window* w, rct_widgetindex widgetIndex, bool value) } } +void WidgetSetHoldable(rct_window* w, rct_widgetindex widgetIndex, bool value) +{ + if (value) + { + w->hold_down_widgets |= (1ULL << widgetIndex); + } + else + { + w->hold_down_widgets &= ~(1ULL << widgetIndex); + } +} + void WidgetSetVisible(rct_window* w, rct_widgetindex widgetIndex, bool value) { if (value) diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp index c6306abc3c..1c47819df1 100644 --- a/src/openrct2-ui/scripting/CustomWindow.cpp +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -77,6 +77,7 @@ namespace OpenRCT2::Ui::Windows bool IsDisabled{}; bool IsVisible{}; bool IsPressed{}; + bool IsHoldable{}; bool HasBorder{}; bool ShowColumnHeaders{}; bool IsStriped{}; @@ -180,9 +181,11 @@ namespace OpenRCT2::Ui::Windows } else if (result.Type == "spinner") { + result.IsHoldable = AsOrDefault(desc["isHoldable"], false); result.Text = ProcessString(desc["text"]); result.OnIncrement = desc["onIncrement"]; result.OnDecrement = desc["onDecrement"]; + result.OnClick = desc["onClick"]; } else if (result.Type == "textbox") { @@ -564,8 +567,6 @@ namespace OpenRCT2::Ui::Windows auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.ExecutePluginCall(info.Owner, widgetDesc->OnDraw, dukWidget, { dukG }, false); } - // auto widgetDpi = dpi.Crop( - // { windowPos.x + widget.left, windowPos.y + widget.top }, { widget.width(), widget.height() }); } } else @@ -612,6 +613,14 @@ namespace OpenRCT2::Ui::Windows args.push_back(DukValue::take_from_stack(ctx)); InvokeEventHandler(info.Owner, widgetDesc->OnChange, args); } + else if (widgetDesc->Type == "spinner") + { + auto& widget = widgets[widgetIndex]; + if (widget.text != STR_NUMERIC_DOWN && widget.text != STR_NUMERIC_UP) + { + InvokeEventHandler(info.Owner, widgetDesc->OnClick); + } + } } break; } @@ -960,6 +969,10 @@ namespace OpenRCT2::Ui::Windows { disabled_widgets |= mask; } + if (widgetFlags & WIDGET_FLAGS::IS_HOLDABLE) + { + hold_down_widgets |= mask; + } } widgetList.push_back({ WIDGETS_END }); @@ -1110,6 +1123,8 @@ namespace OpenRCT2::Ui::Windows widget.flags |= WIDGET_FLAGS::IS_ENABLED; if (desc.IsDisabled) widget.flags |= WIDGET_FLAGS::IS_DISABLED; + if (desc.IsHoldable) + widget.flags |= WIDGET_FLAGS::IS_HOLDABLE; widgetList.push_back(widget); // Add the increment button diff --git a/src/openrct2-ui/scripting/ScWidget.hpp b/src/openrct2-ui/scripting/ScWidget.hpp index 6a85ceec98..48ff3ef17f 100644 --- a/src/openrct2-ui/scripting/ScWidget.hpp +++ b/src/openrct2-ui/scripting/ScWidget.hpp @@ -887,8 +887,29 @@ namespace OpenRCT2::Scripting static void Register(duk_context* ctx) { dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScSpinnerWidget::isHoldable_get, &ScSpinnerWidget::isHoldable_set, "isHoldable"); dukglue_register_property(ctx, &ScSpinnerWidget::text_get, &ScSpinnerWidget::text_set, "text"); } + + private: + int32_t isHoldable_get() const + { + auto w = GetWindow(); + if (w != nullptr) + { + return WidgetIsHoldable(w, _widgetIndex); + } + return false; + } + + void isHoldable_set(int32_t value) + { + auto w = GetWindow(); + if (w != nullptr) + { + WidgetSetHoldable(w, _widgetIndex, value); + } + } }; class ScTextBoxWidget : public ScWidget diff --git a/src/openrct2/interface/Widget.h b/src/openrct2/interface/Widget.h index c4b7a7b333..3f8735f90b 100644 --- a/src/openrct2/interface/Widget.h +++ b/src/openrct2/interface/Widget.h @@ -138,6 +138,7 @@ void WidgetDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetInd bool WidgetIsEnabled(rct_window* w, rct_widgetindex widgetIndex); bool WidgetIsDisabled(rct_window* w, rct_widgetindex widgetIndex); +bool WidgetIsHoldable(rct_window* w, rct_widgetindex widgetIndex); bool WidgetIsVisible(rct_window* w, rct_widgetindex widgetIndex); bool WidgetIsPressed(rct_window* w, rct_widgetindex widgetIndex); bool WidgetIsHighlighted(rct_window* w, rct_widgetindex widgetIndex); @@ -148,6 +149,7 @@ void WidgetScrollGetPart( void WidgetSetEnabled(rct_window* w, rct_widgetindex widgetIndex, bool enabled); void WidgetSetDisabled(rct_window* w, rct_widgetindex widgetIndex, bool value); +void WidgetSetHoldable(rct_window* w, rct_widgetindex widgetIndex, bool value); void WidgetSetVisible(rct_window* w, rct_widgetindex widgetIndex, bool value); void WidgetSetCheckboxValue(rct_window* w, rct_widgetindex widgetIndex, int32_t value); diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 0aa1795afa..a83aadc053 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -77,6 +77,7 @@ namespace WIDGET_FLAGS const WidgetFlags IS_DISABLED = 1 << 3; const WidgetFlags TOOLTIP_IS_STRING = 1 << 4; const WidgetFlags IS_HIDDEN = 1 << 5; + const WidgetFlags IS_HOLDABLE = 1 << 6; } // namespace WIDGET_FLAGS enum class WindowWidgetType : uint8_t; diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 9f639dcbe6..2547160040 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -73,8 +73,14 @@ private: _ss << "\n" << std::string(_indent, ' '); } - void Stringify(const DukValue& val, bool canStartWithNewLine) + void Stringify(const DukValue& val, bool canStartWithNewLine, int32_t nestLevel) { + if (nestLevel >= 8) + { + _ss << "[...]"; + return; + } + switch (val.type()) { case DukValue::Type::UNDEFINED: @@ -99,11 +105,11 @@ private: } else if (val.is_array()) { - StringifyArray(val, canStartWithNewLine); + StringifyArray(val, canStartWithNewLine, nestLevel); } else { - StringifyObject(val, canStartWithNewLine); + StringifyObject(val, canStartWithNewLine, nestLevel); } break; case DukValue::Type::BUFFER: @@ -118,7 +124,7 @@ private: } } - void StringifyArray(const DukValue& val, bool canStartWithNewLine) + void StringifyArray(const DukValue& val, bool canStartWithNewLine, int32_t nestLevel) { constexpr auto maxItemsToShow = 4; @@ -139,7 +145,7 @@ private: { _ss << ", "; } - Stringify(DukValue::take_from_stack(_context), false); + Stringify(DukValue::take_from_stack(_context), false, nestLevel + 1); } } _ss << " ]"; @@ -177,7 +183,7 @@ private: { if (duk_get_prop_index(_context, -1, i)) { - Stringify(DukValue::take_from_stack(_context), false); + Stringify(DukValue::take_from_stack(_context), false, nestLevel + 1); } } } @@ -191,7 +197,7 @@ private: duk_pop(_context); } - void StringifyObject(const DukValue& val, bool canStartWithNewLine) + void StringifyObject(const DukValue& val, bool canStartWithNewLine, int32_t nestLevel) { auto numEnumerables = GetNumEnumerablesOnObject(val); if (numEnumerables == 0) @@ -222,7 +228,7 @@ private: // For some reason the key was not a string _ss << "?: "; } - Stringify(value, true); + Stringify(value, true, nestLevel + 1); index++; } duk_pop_2(_context); @@ -261,7 +267,7 @@ private: // For some reason the key was not a string _ss << "?: "; } - Stringify(value, true); + Stringify(value, true, nestLevel + 1); index++; } duk_pop_2(_context); @@ -343,7 +349,7 @@ public: static std::string StringifyExpression(const DukValue& val) { ExpressionStringifier instance(val.context()); - instance.Stringify(val, false); + instance.Stringify(val, false, 0); return instance._ss.str(); } };