tibia-rme/source/lua/lua_dialog.h
Victor d05ba30ff8
feat: add Lua API modules and Script Manager UI (#161)
This commit adds the full Lua scripting API and integrates the Script Manager into the application.
It builds on top of the scripting engine introduced in #158, exposing all the API modules that Lua scripts can use and making everything accessible from the UI.  
  
Changes:
- Added all Lua API modules: map, tile, item, brush, image, position, selection, creature, color, app, dialog, noise, algo, geo, json, http.
- Added map overlay system for scripts to draw on the map view.
- Added event system, context menu registration, persistent storage, and transaction support with undo/redo.
- Added support for package scripts (directory with manifest.lua) and script metadata tags.
- Added Scripts menu to the menubar with per-script execution and reload.
- Connected the Script Manager window to the UI.
- Added missing build dependencies and fixed MSVC compilation errors.

Notes:
- Build dependencies were added for HTTP and noise generation support.  
- Some code was adjusted to compile correctly on MSVC.
2026-03-28 19:15:57 -03:00

225 lines
7 KiB
C++

//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
// Remere's Map Editor is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Remere's Map Editor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////
#ifndef RME_LUA_DIALOG_H
#define RME_LUA_DIALOG_H
#include <sol/sol.hpp>
#include <wx/dialog.h>
#include <wx/sizer.h>
#include <wx/textctrl.h>
#include <wx/spinctrl.h>
#include <wx/checkbox.h>
#include <wx/choice.h>
#include <wx/button.h>
#include <wx/stattext.h>
#include <wx/statline.h>
#include <wx/notebook.h>
#include <wx/slider.h>
#include <wx/clrpicker.h>
#include <wx/filepicker.h>
#include <wx/statbmp.h>
#include "lua_api_image.h"
#include <string>
#include <map>
#include <vector>
#include <variant>
#include <memory>
#include <stack>
// Forward declaration
class LuaDialog;
class LuaDialogListBox;
class LuaGridCtrl;
class ItemButton;
// Widget info stored for each widget added to the dialog
struct LuaDialogWidget {
std::string id;
std::string type;
wxWindow* widget = nullptr;
sol::function onchange;
sol::function onclick;
sol::function ondoubleclick;
sol::function oncontextmenu;
sol::function onleftclick;
sol::function onrightclick;
};
struct LuaDialogTab {
std::string id;
std::string text;
bool isButton = false;
sol::function onclick;
sol::function oncontextmenu;
};
// Lua Dialog wrapper class
// Provides Aseprite-style method chaining API for creating dialogs
class LuaDialog : public wxDialog {
public:
LuaDialog(const std::string &title, sol::this_state ts);
LuaDialog(sol::table options, sol::this_state ts);
virtual ~LuaDialog();
// Method chaining widget methods - all return pointer to self for chaining
LuaDialog* label(sol::table options);
LuaDialog* input(sol::table options);
LuaDialog* number(sol::table options);
LuaDialog* slider(sol::table options);
LuaDialog* check(sol::table options);
LuaDialog* radio(sol::table options);
LuaDialog* combobox(sol::table options);
LuaDialog* button(sol::table options);
LuaDialog* color(sol::table options);
LuaDialog* item(sol::table options);
LuaDialog* mapCanvas(sol::table options);
LuaDialog* list(sol::table options);
LuaDialog* grid(sol::table options);
LuaDialog* file(sol::table options);
LuaDialog* image(sol::table options);
LuaDialog* separator(sol::optional<sol::table> options);
LuaDialog* newrow();
LuaDialog* tab(sol::table options);
LuaDialog* endtabs();
// Nesting
LuaDialog* wrap(sol::table options); // Define explicit horizontal wrapper
LuaDialog* endwrap();
// Layout containers
LuaDialog* box(sol::table options); // Generic box sizer
LuaDialog* endbox();
LuaDialog* panel(sol::table options);
LuaDialog* endpanel();
// Dialog control
LuaDialog* show(sol::optional<sol::table> options);
void close();
LuaDialog* modify(sol::table options);
void repaint();
void clear();
void layout();
// Data access
sol::table getData();
void setData(sol::table data);
// Bounds access
sol::table getBounds();
void setBounds(sol::table bounds);
sol::object getActiveTab();
bool isDockable() const {
return dockPanel != nullptr;
}
private:
sol::state_view lua;
std::vector<LuaDialogWidget> widgets;
std::map<std::string, sol::object> values;
sol::function oncloseCallback;
std::stack<wxSizer*> sizerStack;
std::stack<wxWindow*> panelStack;
std::stack<wxBoxSizer*> rowSizerStack;
std::vector<wxSizer*> tabSizers;
// Parent window for widget creation (usually dialog, but can be other panels)
// Actually, keeping text/buttons parenting to dialog is easier for events.
// As long as sizer hierarchy is correct, parent hierarchy is less strict in wx, but static box sizer needs static box sibling.
// Let's rely on 'getParentForWidget()' which currently uses 'currentTabPanel'.
wxBoxSizer* mainSizer = nullptr;
wxBoxSizer* currentRowSizer = nullptr;
wxNotebook* currentNotebook = nullptr;
wxNotebook* activeNotebook = nullptr;
wxPanel* currentTabPanel = nullptr;
wxBoxSizer* currentTabSizer = nullptr;
int hotkeySuspendCount = 0;
bool hotkeysDisabledByDialog = false;
bool notebookEventsBound = false;
bool suppressTabButtonClick = false;
int suppressTabButtonIndex = -1;
bool isShowing = false;
bool waitMode = true;
std::vector<LuaDialogTab> tabInfos;
void createLayout();
void ensureRowSizer();
void finishCurrentRow();
wxWindow* getParentForWidget();
wxSizer* getSizerForWidget();
void applyInitialSizeAndPosition();
void setupDockablePanel(sol::table &options);
void updateValue(const std::string &id);
void collectAllValues();
void applyCommonOptions(wxWindow* widget, sol::table options);
int getSizerFlags(sol::table options, int defaultFlags = 0);
int getSizerBorder(sol::table options);
void suspendHotkeys();
void resumeHotkeys();
sol::table makeTabInfoTable(int index);
void handleItemButtonClick(const std::string &id, ItemButton* btn, bool readonly);
void handleTabButtonClick(int index);
void handleTabContextMenu(int index, const wxPoint &screenPos);
void popupContextMenu(const sol::function &callback, sol::table info, wxWindow* window, const wxPoint &screenPos);
// Tab helpers (extracted from tab() to reduce cognitive complexity)
void ensureNotebook();
void bindNotebookEvents();
void onNotebookPageChanging(wxNotebookEvent &event);
void onNotebookLeftDown(wxMouseEvent &event);
void onNotebookContextMenu(wxContextMenuEvent &event);
int insertTabPanel(const std::string &text, int insertIndex);
void onWidgetChange(const std::string &id);
void onButtonClick(const std::string &id);
void onWidgetDoubleClick(const std::string &id);
void handleListClick(const std::string &id, LuaDialogListBox* listbox, const sol::function &callback, wxMouseEvent &event);
void handleListContextMenu(const std::string &id, LuaDialogListBox* listbox, const sol::function &callback, wxContextMenuEvent &event);
void handleGridClick(const std::string &id, LuaGridCtrl* grid, const sol::function &callback, wxMouseEvent &event);
void handleGridContextMenu(const std::string &id, LuaGridCtrl* grid, const sol::function &callback, wxContextMenuEvent &event);
void applyFontOptions(wxWindow* widget, sol::table options);
// Event handlers
void OnClose(wxCloseEvent &event);
DECLARE_EVENT_TABLE()
int reqWidth = -1;
int reqHeight = -1;
int reqX = -1;
int reqY = -1;
public:
wxPanel* dockPanel = nullptr;
};
namespace LuaAPI {
// Register the Dialog class with Lua
void registerDialog(sol::state &lua);
}
#endif // RME_LUA_DIALOG_H