1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-24 00:03:11 +01:00

Add plugin API for shortcuts

This commit is contained in:
Ted John
2020-12-16 00:15:20 +00:00
parent 9964df5335
commit afc548c5a7
8 changed files with 171 additions and 3 deletions

View File

@@ -1792,6 +1792,8 @@ declare global {
activateTool(tool: ToolDesc): void;
registerMenuItem(text: string, callback: () => void): void;
registerShortcut(desc: ShortcutDesc): void;
}
/**
@@ -1960,6 +1962,31 @@ declare global {
"label" |
"banner";
interface ShortcutDesc {
/**
* The unique identifier for the shortcut.
* If the identifier already exists, the shortcut will not be registered.
* Use full stops to group shortcuts together, e.g. `yourplugin.somewindow.apply`.
*/
id: string;
/**
* The display text for the shortcut.
*/
text: string;
/**
* Default bindings for the shortcut.
* E.g. `["CTRL+SHIFT+L", "MOUSE 3"]`
*/
bindings?: string[];
/**
* Function to call when the shortcut is invoked.
*/
callback: () => void;
}
/**
* Represents the type of a widget, e.g. button or label.
*/

View File

@@ -424,7 +424,10 @@ ShortcutManager::ShortcutManager()
void ShortcutManager::RegisterShortcut(RegisteredShortcut&& shortcut)
{
Shortcuts.push_back(shortcut);
if (!shortcut.Id.empty() && GetShortcut(shortcut.Id) == nullptr)
{
Shortcuts.push_back(shortcut);
}
}
RegisteredShortcut* ShortcutManager::GetShortcut(std::string_view id)
@@ -433,6 +436,12 @@ RegisteredShortcut* ShortcutManager::GetShortcut(std::string_view id)
return result == Shortcuts.end() ? nullptr : &(*result);
}
void ShortcutManager::RemoveShortcut(std::string_view id)
{
Shortcuts.erase(std::remove_if(
Shortcuts.begin(), Shortcuts.end(), [id](const RegisteredShortcut& shortcut) { return shortcut.Id == id; }));
}
void ShortcutManager::SetPendingShortcutChange(std::string_view id)
{
_pendingShortcutChange = id;

View File

@@ -46,11 +46,19 @@ namespace OpenRCT2::Ui
public:
std::string Id;
rct_string_id LocalisedName = STR_NONE;
std::string CustomName;
std::vector<ShortcutInput> Default;
std::vector<ShortcutInput> Current;
std::function<void()> Action;
RegisteredShortcut() = default;
RegisteredShortcut(const std::string_view& id, std::string_view name, const std::function<void()>& action)
: Id(id)
, CustomName(name)
, Action(action)
{
}
RegisteredShortcut(const std::string_view& id, rct_string_id localisedName, const std::function<void()>& action)
: Id(id)
, LocalisedName(localisedName)
@@ -105,6 +113,7 @@ namespace OpenRCT2::Ui
}
void RegisterDefaultShortcuts();
RegisteredShortcut* GetShortcut(std::string_view id);
void RemoveShortcut(std::string_view id);
void SetPendingShortcutChange(std::string_view id);
void ProcessEvent(const InputEvent& e);
bool ProcessEventForSpecificShortcut(const InputEvent& e, std::string_view id);

View File

@@ -11,14 +11,50 @@
# include "CustomMenu.h"
# include <openrct2-ui/input/ShortcutManager.h>
# include <openrct2/Input.h>
# include <openrct2/world/Map.h>
# include <openrct2/world/Sprite.h>
using namespace OpenRCT2;
using namespace OpenRCT2::Ui;
namespace OpenRCT2::Scripting
{
std::optional<CustomTool> ActiveCustomTool;
std::vector<CustomToolbarMenuItem> CustomMenuItems;
std::vector<CustomShortcut> CustomShortcuts;
CustomShortcut::CustomShortcut(
std::shared_ptr<Plugin> owner, std::string_view id, std::string_view text, const std::vector<std::string>& bindings,
DukValue callback)
: Owner(owner)
, Id(id)
, Text(text)
, Bindings(bindings)
, Callback(callback)
{
auto& shortcutManager = GetShortcutManager();
RegisteredShortcut registeredShortcut(Id, Text, [this]() { Invoke(); });
for (const auto& binding : bindings)
{
registeredShortcut.Default.emplace_back(binding);
}
shortcutManager.RegisterShortcut(std::move(registeredShortcut));
}
CustomShortcut::~CustomShortcut()
{
auto& shortcutManager = GetShortcutManager();
shortcutManager.RemoveShortcut(Id);
}
void CustomShortcut::Invoke() const
{
auto& scriptEngine = GetContext()->GetScriptEngine();
scriptEngine.ExecutePluginCall(Owner, Callback, {}, false);
}
static constexpr std::array<std::string_view, EnumValue(CursorID::Count)> CursorNames = {
"arrow", "blank", "up_arrow", "up_down_arrow", "hand_point", "zzz", "diagonal_arrows",
@@ -89,6 +125,19 @@ namespace OpenRCT2::Scripting
it++;
}
}
auto& shortcuts = CustomShortcuts;
for (auto it = shortcuts.begin(); it != shortcuts.end();)
{
if (it->Owner == owner)
{
it = shortcuts.erase(it);
}
else
{
it++;
}
}
}
void InitialiseCustomMenuItems(ScriptEngine& scriptEngine)

View File

@@ -44,6 +44,28 @@ namespace OpenRCT2::Scripting
}
};
class CustomShortcut
{
public:
std::shared_ptr<Plugin> Owner;
std::string Id;
std::string Text;
std::vector<std::string> Bindings;
DukValue Callback;
CustomShortcut(
std::shared_ptr<Plugin> owner, std::string_view id, std::string_view text, const std::vector<std::string>& bindings,
DukValue callback);
CustomShortcut(CustomShortcut&&) = default;
CustomShortcut(const CustomShortcut&) = delete;
~CustomShortcut();
CustomShortcut& operator=(const CustomShortcut&) = delete;
CustomShortcut& operator=(CustomShortcut&& other) = default;
void Invoke() const;
};
struct CustomTool
{
std::shared_ptr<Plugin> Owner;
@@ -72,6 +94,7 @@ namespace OpenRCT2::Scripting
extern std::optional<CustomTool> ActiveCustomTool;
extern std::vector<CustomToolbarMenuItem> CustomMenuItems;
extern std::vector<CustomShortcut> CustomShortcuts;
void InitialiseCustomMenuItems(ScriptEngine& scriptEngine);
void InitialiseCustomTool(ScriptEngine& scriptEngine, const DukValue& dukValue);

View File

@@ -307,6 +307,34 @@ namespace OpenRCT2::Scripting
CustomMenuItems.emplace_back(owner, text, callback);
}
void registerShortcut(DukValue desc)
{
try
{
auto& execInfo = _scriptEngine.GetExecInfo();
auto owner = execInfo.GetCurrentPlugin();
auto id = desc["id"].as_string();
auto text = desc["text"].as_string();
std::vector<std::string> bindings;
auto dukBindings = desc["bindings"];
if (dukBindings.is_array())
{
for (auto binding : dukBindings.as_array())
{
bindings.push_back(binding.as_string());
}
}
auto callback = desc["callback"];
CustomShortcuts.emplace_back(owner, id, text, bindings, callback);
}
catch (const DukException&)
{
duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters.");
}
}
public:
static void Register(duk_context* ctx)
{
@@ -326,6 +354,7 @@ namespace OpenRCT2::Scripting
dukglue_register_method(ctx, &ScUi::showScenarioSelect, "showScenarioSelect");
dukglue_register_method(ctx, &ScUi::activateTool, "activateTool");
dukglue_register_method(ctx, &ScUi::registerMenuItem, "registerMenuItem");
dukglue_register_method(ctx, &ScUi::registerShortcut, "registerShortcut");
}
private:

View File

@@ -49,6 +49,7 @@ static rct_window_event_list window_shortcut_change_events([](auto& events)
// clang-format on
static rct_string_id _shortcutLocalisedName{};
static std::string _shortcutCustomName{};
rct_window* window_shortcut_change_open(const std::string_view& shortcutId)
{
@@ -58,6 +59,7 @@ rct_window* window_shortcut_change_open(const std::string_view& shortcutId)
if (registeredShortcut != nullptr)
{
_shortcutLocalisedName = registeredShortcut->LocalisedName;
_shortcutCustomName = registeredShortcut->CustomName;
window_close_by_class(WC_CHANGE_KEYBOARD_SHORTCUT);
auto w = WindowCreateCentred(WW, WH, &window_shortcut_change_events, WC_CHANGE_KEYBOARD_SHORTCUT, 0);
@@ -105,6 +107,14 @@ static void window_shortcut_change_paint(rct_window* w, rct_drawpixelinfo* dpi)
ScreenCoordsXY stringCoords(w->windowPos.x + 125, w->windowPos.y + 30);
auto ft = Formatter();
ft.Add<rct_string_id>(_shortcutLocalisedName);
if (_shortcutCustomName.empty())
{
ft.Add<rct_string_id>(_shortcutLocalisedName);
}
else
{
ft.Add<rct_string_id>(STR_STRING);
ft.Add<const char*>(_shortcutCustomName.c_str());
}
gfx_draw_string_centred_wrapped(dpi, ft.Data(), stringCoords, 242, STR_SHORTCUT_CHANGE_PROMPT, COLOUR_BLACK);
}

View File

@@ -71,6 +71,7 @@ struct ShortcutStringPair
size_t ShortcutIndex;
std::string ShortcutId;
rct_string_id StringId = STR_NONE;
std::string CustomString;
};
static std::vector<ShortcutStringPair> _shortcutList;
@@ -103,6 +104,7 @@ static void InitialiseShortcutList()
ssp.ShortcutIndex = index;
ssp.ShortcutId = shortcut.Id;
ssp.StringId = shortcut.LocalisedName;
ssp.CustomString = shortcut.CustomName;
_shortcutList.push_back(std::move(ssp));
index++;
}
@@ -288,10 +290,20 @@ static void window_shortcut_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, i
gfx_filter_rect(dpi, 0, y - 1, scrollWidth, y + (SCROLLABLE_ROW_HEIGHT - 2), FilterPaletteID::PaletteDarken1);
}
const auto& shortcut = _shortcutList[i];
const int32_t bindingOffset = scrollWidth - 150;
auto ft = Formatter();
ft.Add<rct_string_id>(STR_SHORTCUT_ENTRY_FORMAT);
ft.Add<rct_string_id>(_shortcutList[i].StringId);
if (shortcut.CustomString.empty())
{
ft.Add<rct_string_id>(shortcut.StringId);
}
else
{
ft.Add<rct_string_id>(STR_STRING);
ft.Add<const char*>(shortcut.CustomString.c_str());
}
DrawTextEllipsised(dpi, { 0, y - 1 }, bindingOffset, format, ft, COLOUR_BLACK);
char keybinding[128];