mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-22 23:33:04 +01:00
Implement custom menu items
This commit is contained in:
1
distribution/openrct2.d.ts
vendored
1
distribution/openrct2.d.ts
vendored
@@ -439,6 +439,7 @@ export interface Ui {
|
||||
closeAllWindows(): void;
|
||||
|
||||
activateTool(options: ToolDesc): IDisposable;
|
||||
registerMenuItem(text: string, callback: () => void): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
36
src/openrct2-ui/scripting/CustomMenu.cpp
Normal file
36
src/openrct2-ui/scripting/CustomMenu.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2020 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 "CustomMenu.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
std::vector<CustomToolbarMenuItem> CustomMenuItems;
|
||||
|
||||
static void RemoveMenuItems(std::shared_ptr<Plugin> owner)
|
||||
{
|
||||
auto& menuItems = CustomMenuItems;
|
||||
for (auto it = menuItems.begin(); it != menuItems.end();)
|
||||
{
|
||||
if (it->Owner == owner)
|
||||
{
|
||||
it = menuItems.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitialiseCustomMenuItems(ScriptEngine& scriptEngine)
|
||||
{
|
||||
scriptEngine.SubscribeToPluginStoppedEvent([](std::shared_ptr<Plugin> plugin) -> void { RemoveMenuItems(plugin); });
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
52
src/openrct2-ui/scripting/CustomMenu.h
Normal file
52
src/openrct2-ui/scripting/CustomMenu.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2020 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class CustomToolbarMenuItem
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<Plugin> Owner;
|
||||
std::string Text;
|
||||
DukValue Callback;
|
||||
|
||||
CustomToolbarMenuItem(std::shared_ptr<Plugin> owner, std::string text, DukValue callback)
|
||||
: Owner(owner)
|
||||
, Text(text)
|
||||
, Callback(callback)
|
||||
{
|
||||
}
|
||||
|
||||
void Invoke() const
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto& execInfo = scriptEngine.GetExecInfo();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
|
||||
ScriptExecutionInfo::PluginScope scope(execInfo, Owner);
|
||||
Callback.push();
|
||||
duk_pcall(ctx, 0);
|
||||
duk_pop(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
extern std::vector<CustomToolbarMenuItem> CustomMenuItems;
|
||||
|
||||
void InitialiseCustomMenuItems(ScriptEngine& scriptEngine);
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CustomMenu.h"
|
||||
#include "ScWindow.hpp"
|
||||
|
||||
#include <memory>
|
||||
@@ -105,6 +106,13 @@ namespace OpenRCT2::Scripting
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void registerMenuItem(std::string text, DukValue callback)
|
||||
{
|
||||
auto& execInfo = _scriptEngine.GetExecInfo();
|
||||
auto owner = execInfo.GetCurrentPlugin();
|
||||
CustomMenuItems.emplace_back(owner, text, callback);
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScUi::height_get, nullptr, "height");
|
||||
@@ -114,6 +122,7 @@ namespace OpenRCT2::Scripting
|
||||
dukglue_register_method(ctx, &ScUi::closeWindows, "closeWindows");
|
||||
dukglue_register_method(ctx, &ScUi::closeAllWindows, "closeAllWindows");
|
||||
dukglue_register_method(ctx, &ScUi::getWindow, "getWindow");
|
||||
dukglue_register_method(ctx, &ScUi::registerMenuItem, "registerMenuItem");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "UiExtensions.h"
|
||||
|
||||
#include "CustomMenu.h"
|
||||
#include "ScUi.hpp"
|
||||
#include "ScWindow.hpp"
|
||||
|
||||
@@ -24,4 +25,6 @@ void UiScriptExtensions::Extend(ScriptEngine& scriptEngine)
|
||||
|
||||
ScUi::Register(ctx);
|
||||
ScWindow::Register(ctx);
|
||||
|
||||
InitialiseCustomMenuItems(scriptEngine);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "../UiContext.h"
|
||||
#include "../interface/InGameConsole.h"
|
||||
#include "../scripting/CustomMenu.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
@@ -305,6 +306,8 @@ static rct_window_event_list window_top_toolbar_events = {
|
||||
|
||||
static void top_toolbar_init_view_menu(rct_window* window, rct_widget* widget);
|
||||
static void top_toolbar_view_menu_dropdown(int16_t dropdownIndex);
|
||||
static void top_toolbar_init_map_menu(rct_window* window, rct_widget* widget);
|
||||
static void top_toolbar_map_menu_dropdown(int16_t dropdownIndex);
|
||||
static void top_toolbar_init_fastforward_menu(rct_window* window, rct_widget* widget);
|
||||
static void top_toolbar_fastforward_menu_dropdown(int16_t dropdownIndex);
|
||||
static void top_toolbar_init_rotate_menu(rct_window* window, rct_widget* widget);
|
||||
@@ -521,20 +524,7 @@ static void window_top_toolbar_mousedown(rct_window* w, rct_widgetindex widgetIn
|
||||
top_toolbar_init_view_menu(w, widget);
|
||||
break;
|
||||
case WIDX_MAP:
|
||||
gDropdownItemsFormat[0] = STR_SHORTCUT_SHOW_MAP;
|
||||
gDropdownItemsFormat[1] = STR_EXTRA_VIEWPORT;
|
||||
numItems = 2;
|
||||
|
||||
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && gS6Info.editor_step == EDITOR_STEP_LANDSCAPE_EDITOR)
|
||||
{
|
||||
gDropdownItemsFormat[2] = STR_MAPGEN_WINDOW_TITLE;
|
||||
numItems++;
|
||||
}
|
||||
|
||||
window_dropdown_show_text(
|
||||
w->windowPos.x + widget->left, w->windowPos.y + widget->top, widget->bottom - widget->top + 1,
|
||||
w->colours[1] | 0x80, 0, numItems);
|
||||
gDropdownDefaultIndex = DDIDX_SHOW_MAP;
|
||||
top_toolbar_init_map_menu(w, widget);
|
||||
break;
|
||||
case WIDX_FASTFORWARD:
|
||||
top_toolbar_init_fastforward_menu(w, widget);
|
||||
@@ -651,18 +641,7 @@ static void window_top_toolbar_dropdown(rct_window* w, rct_widgetindex widgetInd
|
||||
top_toolbar_view_menu_dropdown(dropdownIndex);
|
||||
break;
|
||||
case WIDX_MAP:
|
||||
switch (dropdownIndex)
|
||||
{
|
||||
case 0:
|
||||
context_open_window(WC_MAP);
|
||||
break;
|
||||
case 1:
|
||||
context_open_window(WC_VIEWPORT);
|
||||
break;
|
||||
case 2:
|
||||
context_open_window(WC_MAPGEN);
|
||||
break;
|
||||
}
|
||||
top_toolbar_map_menu_dropdown(dropdownIndex);
|
||||
break;
|
||||
case WIDX_FASTFORWARD:
|
||||
top_toolbar_fastforward_menu_dropdown(dropdownIndex);
|
||||
@@ -3293,6 +3272,68 @@ static void window_top_toolbar_tool_abort(rct_window* w, rct_widgetindex widgetI
|
||||
}
|
||||
}
|
||||
|
||||
static void top_toolbar_init_map_menu(rct_window* w, rct_widget* widget)
|
||||
{
|
||||
auto i = 0;
|
||||
gDropdownItemsFormat[i++] = STR_SHORTCUT_SHOW_MAP;
|
||||
gDropdownItemsFormat[i++] = STR_EXTRA_VIEWPORT;
|
||||
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && gS6Info.editor_step == EDITOR_STEP_LANDSCAPE_EDITOR)
|
||||
{
|
||||
gDropdownItemsFormat[i++] = STR_MAPGEN_WINDOW_TITLE;
|
||||
}
|
||||
|
||||
const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems;
|
||||
if (!customMenuItems.empty())
|
||||
{
|
||||
gDropdownItemsFormat[i++] = STR_EMPTY;
|
||||
for (const auto& item : customMenuItems)
|
||||
{
|
||||
gDropdownItemsFormat[i] = STR_STRING;
|
||||
set_format_arg_on((uint8_t*)&gDropdownItemsArgs[i], 0, const char*, item.Text.c_str());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
window_dropdown_show_text(
|
||||
w->windowPos.x + widget->left, w->windowPos.y + widget->top, widget->bottom - widget->top + 1, w->colours[1] | 0x80, 0,
|
||||
i);
|
||||
gDropdownDefaultIndex = DDIDX_SHOW_MAP;
|
||||
}
|
||||
|
||||
static void top_toolbar_map_menu_dropdown(int16_t dropdownIndex)
|
||||
{
|
||||
int32_t customStartIndex = 3;
|
||||
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && gS6Info.editor_step == EDITOR_STEP_LANDSCAPE_EDITOR)
|
||||
{
|
||||
customStartIndex++;
|
||||
}
|
||||
|
||||
if (dropdownIndex < customStartIndex)
|
||||
{
|
||||
switch (dropdownIndex)
|
||||
{
|
||||
case 0:
|
||||
context_open_window(WC_MAP);
|
||||
break;
|
||||
case 1:
|
||||
context_open_window(WC_VIEWPORT);
|
||||
break;
|
||||
case 2:
|
||||
context_open_window(WC_MAPGEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems;
|
||||
auto customIndex = dropdownIndex - customStartIndex;
|
||||
if (customMenuItems.size() > customIndex)
|
||||
{
|
||||
customMenuItems[customIndex].Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void top_toolbar_init_fastforward_menu(rct_window* w, rct_widget* widget)
|
||||
{
|
||||
int32_t num_items = 4;
|
||||
|
||||
@@ -136,6 +136,28 @@ void ScriptEngine::LoadPlugin(const std::string& path)
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::StopPlugin(std::shared_ptr<Plugin> plugin)
|
||||
{
|
||||
if (plugin->HasStarted())
|
||||
{
|
||||
_hookEngine.UnsubscribeAll(plugin);
|
||||
for (auto callback : _pluginStoppedSubscriptions)
|
||||
{
|
||||
callback(plugin);
|
||||
}
|
||||
|
||||
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
|
||||
try
|
||||
{
|
||||
plugin->Stop();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
_console.WriteLineError(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptEngine::ShouldLoadScript(const std::string& path)
|
||||
{
|
||||
// A lot of JavaScript is often found in a node_modules directory tree and is most likely unwanted, so ignore it
|
||||
@@ -174,7 +196,7 @@ void ScriptEngine::AutoReloadPlugins()
|
||||
auto& plugin = *findResult;
|
||||
try
|
||||
{
|
||||
_hookEngine.UnsubscribeAll(plugin);
|
||||
StopPlugin(plugin);
|
||||
|
||||
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
|
||||
plugin->Load();
|
||||
@@ -225,21 +247,9 @@ void ScriptEngine::StartPlugins()
|
||||
|
||||
void ScriptEngine::StopPlugins()
|
||||
{
|
||||
_hookEngine.UnsubscribeAll();
|
||||
for (auto& plugin : _plugins)
|
||||
{
|
||||
if (plugin->HasStarted())
|
||||
{
|
||||
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
|
||||
try
|
||||
{
|
||||
plugin->Stop();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
_console.WriteLineError(e.what());
|
||||
}
|
||||
}
|
||||
StopPlugin(plugin);
|
||||
}
|
||||
_pluginsStarted = false;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ namespace OpenRCT2::Scripting
|
||||
std::unique_ptr<FileWatcher> _pluginFileWatcher;
|
||||
std::unordered_set<std::string> _changedPluginFiles;
|
||||
std::mutex _changedPluginFilesMutex;
|
||||
std::vector<std::function<void(std::shared_ptr<Plugin>)>> _pluginStoppedSubscriptions;
|
||||
|
||||
public:
|
||||
ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env);
|
||||
@@ -130,11 +131,17 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
void LogPluginInfo(const std::shared_ptr<Plugin>& plugin, const std::string_view& message);
|
||||
|
||||
void SubscribeToPluginStoppedEvent(std::function<void(std::shared_ptr<Plugin>)> callback)
|
||||
{
|
||||
_pluginStoppedSubscriptions.push_back(callback);
|
||||
}
|
||||
|
||||
private:
|
||||
void Initialise();
|
||||
void StartPlugins();
|
||||
void StopPlugins();
|
||||
void LoadPlugin(const std::string& path);
|
||||
void StopPlugin(std::shared_ptr<Plugin> plugin);
|
||||
bool ShouldLoadScript(const std::string& path);
|
||||
void SetupHotReloading();
|
||||
void AutoReloadPlugins();
|
||||
|
||||
Reference in New Issue
Block a user