diff --git a/src/dialogxml/dialogs/dialog.cpp b/src/dialogxml/dialogs/dialog.cpp index 794dab05..728f3368 100644 --- a/src/dialogxml/dialogs/dialog.cpp +++ b/src/dialogxml/dialogs/dialog.cpp @@ -43,6 +43,8 @@ const short cDialog::BG_DARK = 5, cDialog::BG_LIGHT = 16; short cDialog::defaultBackground = cDialog::BG_DARK; cDialog* cDialog::topWindow = nullptr; void (*cDialog::redraw_everything)() = nullptr; +std::function cDialog::onOpen; +std::function cDialog::onClose; std::function cDialog::onLostFocus; std::function cDialog::onGainedFocus; std::function cDialog::onHandleEvents; @@ -502,7 +504,7 @@ bool cDialog::sendInput(cKey key) { void cDialog::run(std::function onopen){ cPict::resetAnim(); cDialog* formerTop = topWindow; - // TODO: The introduction of the static topWindow means I may be able to use this instead of parent->win; do I still need parent? + sf::RenderWindow* parentWin = &(parent ? parent->win : mainPtr()); auto parentPos = parentWin->getPosition(); auto parentSz = parentWin->getSize(); @@ -542,11 +544,16 @@ void cDialog::run(std::function onopen){ // but it does prevent editing other dialogs, and it also keeps this window on top // even when it loses focus. ModalSession dlog(win, *parentWin); - if(onopen) onopen(*this); animTimer.restart(); has_focus = true; topWindow = this; + + // Run the static onOpen event first + if(cDialog::onOpen) cDialog::onOpen(*this); + // Run this dialog's onOpen event + if(onopen) onopen(*this); + handle_events(); win.setVisible(false); @@ -555,6 +562,7 @@ void cDialog::run(std::function onopen){ set_cursor(former_curs); topWindow = formerTop; stackWindowsCorrectly(); + if(cDialog::onClose) cDialog::onClose(*this); } void cDialog::runWithHelp(short help1, short help2, bool help_forced) { diff --git a/src/dialogxml/dialogs/dialog.hpp b/src/dialogxml/dialogs/dialog.hpp index 443fff90..95380ab3 100644 --- a/src/dialogxml/dialogs/dialog.hpp +++ b/src/dialogxml/dialogs/dialog.hpp @@ -82,7 +82,11 @@ class cDialog : public iComponent, public iNameGiver { bool doAnimations; bool has_focus = false; public: + static bool anyOpen() { return topWindow != nullptr; } static void (*redraw_everything)(); + // Global onOpen and onClose events are for tracking things, not for modifying the dialogs. + static std::function onOpen; + static std::function onClose; static std::function onLostFocus; static std::function onGainedFocus; // Attach a handler here for any update/input logic that uses target-specific code diff --git a/src/scenedit/scen.main.cpp b/src/scenedit/scen.main.cpp index 0fff346e..a6b8c0ba 100644 --- a/src/scenedit/scen.main.cpp +++ b/src/scenedit/scen.main.cpp @@ -348,13 +348,22 @@ void init_scened(int argc, char* argv[]) { init_shaders(); init_tiling(); init_snd_tool(); + + // When a dialog is open, scenario action undo/redo needs to be disabled for 2 reasons: + // 1. it is very unsafe to pop from the stack while any edit operation is underway + // 2. the menu item must be disabled for cDialog to receive undo/redo keyboard shortcuts + // for text field editing + cDialog::onOpen = cDialog::onClose = [](const cDialog& dialog) { + update_edit_menu(); + }; #ifdef SFML_SYSTEM_MACOS init_menubar(); // This is called twice because Windows and Mac have different ordering requirements cDialog::onHandleEvents = [](sf::RenderWindow&) { - if (menuChoiceId>=0) { - handle_menu_choice(eMenu(menuChoiceId), true); + if(menuChoiceId>=0) { + short wasChoice = menuChoiceId; menuChoiceId=-1; + handle_menu_choice(eMenu(wasChoice)); } }; #endif @@ -399,8 +408,9 @@ void handle_events() { #ifdef __APPLE__ if (menuChoiceId>=0) { - handle_menu_choice(eMenu(menuChoiceId)); + short wasChoice = menuChoiceId; menuChoiceId=-1; + handle_menu_choice(eMenu(wasChoice)); } #endif while(pollEvent(mainPtr(), currentEvent)) handle_one_event(currentEvent); @@ -470,12 +480,29 @@ static void show_outdated_warning() { showWarning(outdated_help1, outdated_help2); } +// When any dialog is open, only allow menu items that work with text fields. +// NOTE: EDIT_UNDO and EDIT_REDO are included, even though currently, those menu items will be +// disabled (only the keyboard shortcuts work for undo/redo on text editing). +// Someday, update_edit_menu() could keep them enabled but change their text to show +// the undo action of the active text field--but that would be complicated to do, and require +// changing update_edit_menu() for all 3 platforms right now. +std::set dialog_allowed_menu_choices = { + eMenu::NONE, eMenu::EDIT_UNDO, eMenu::EDIT_REDO, eMenu::EDIT_CUT, eMenu::EDIT_COPY, + eMenu::EDIT_PASTE, eMenu::EDIT_DELETE, eMenu::EDIT_SELECT_ALL, +}; + void handle_menu_choice(eMenu item_hit) { extern cUndoList undo_list; bool isEdit = false, isHelp = false, isOutdated = false; std::string helpDlog; fs::path file_to_load; cKey editKey = {true}; + + if(cDialog::anyOpen() && !dialog_allowed_menu_choices.count(item_hit)){ + showWarning("You must confirm or cancel what you're currently doing first."); + return; + } + switch(item_hit) { case eMenu::NONE: return; case eMenu::FILE_OPEN: diff --git a/src/scenedit/scen.menu.cpp b/src/scenedit/scen.menu.cpp index 952085e2..2f1fd378 100644 --- a/src/scenedit/scen.menu.cpp +++ b/src/scenedit/scen.menu.cpp @@ -329,7 +329,7 @@ void OpenBoESceneditMenu::update_for_mode_4() { void OpenBoESceneditMenu::update_edit_menu(cUndoList const & undo_list) { auto menubar = this->get_menubar_ptr(); - menubar->setMenuItemEnabled({ "Edit", "Undo Ctrl-Z" }, !undo_list.noUndo()); - menubar->setMenuItemEnabled({ "Edit", "Redo Ctrl-Y" }, !undo_list.noRedo()); + menubar->setMenuItemEnabled({ "Edit", "Undo Ctrl-Z" }, !(undo_list.noUndo() || cDialog::anyOpen())); + menubar->setMenuItemEnabled({ "Edit", "Redo Ctrl-Y" }, !(undo_list.noRedo() || cDialog::anyOpen())); } diff --git a/src/scenedit/scen.menus.mac.mm b/src/scenedit/scen.menus.mac.mm index ee11c6bc..e60a3fe5 100644 --- a/src/scenedit/scen.menus.mac.mm +++ b/src/scenedit/scen.menus.mac.mm @@ -11,6 +11,7 @@ #include #include "tools/winutil.hpp" #include "tools/undo.hpp" +#include "dialogxml/dialogs/dialog.hpp" extern short menuChoiceId; @@ -154,7 +155,7 @@ void shut_down_menus(short mode) { void update_edit_menu() { NSMenuItem* mi_undo = [edit_menu itemAtIndex: 0]; - if(undo_list.noUndo()) { + if(undo_list.noUndo() || cDialog::anyOpen()) { [mi_undo setTitle: @"Can't Undo"]; [mi_undo setEnabled: NO]; } else { @@ -163,7 +164,7 @@ void update_edit_menu() { [mi_undo setEnabled: YES]; } NSMenuItem* mi_redo = [edit_menu itemAtIndex: 1]; - if(undo_list.noRedo()) { + if(undo_list.noRedo() || cDialog::anyOpen()) { [mi_redo setTitle: @"Can't Redo"]; [mi_redo setEnabled: NO]; } else { diff --git a/src/scenedit/scen.menus.win.cpp b/src/scenedit/scen.menus.win.cpp index cf1af96b..2699751e 100644 --- a/src/scenedit/scen.menus.win.cpp +++ b/src/scenedit/scen.menus.win.cpp @@ -190,7 +190,7 @@ void shut_down_menus(short mode) { void update_edit_menu() { if(menuHandle == NULL) return; HMENU edit_menu = GetSubMenu(menuHandle, EDIT_MENU_POS); - if(undo_list.noUndo()) { + if(undo_list.noUndo() || cDialog::anyOpen()) { ModifyMenuA(edit_menu, IDM_EDIT_UNDO, MF_BYCOMMAND, IDM_EDIT_UNDO, "Can't Undo\tCtrl+Z"); EnableMenuItem(edit_menu, IDM_EDIT_UNDO, MF_BYCOMMAND | MF_GRAYED); } else { @@ -198,7 +198,7 @@ void update_edit_menu() { ModifyMenuA(edit_menu, IDM_EDIT_UNDO, MF_BYCOMMAND, IDM_EDIT_UNDO, title.c_str()); EnableMenuItem(edit_menu, IDM_EDIT_UNDO, MF_BYCOMMAND | MF_ENABLED); } - if(undo_list.noRedo()) { + if(undo_list.noRedo() || cDialog::anyOpen()) { ModifyMenuA(edit_menu, IDM_EDIT_REDO, MF_BYCOMMAND, IDM_EDIT_REDO, "Can't Redo\tCtrl+Y"); EnableMenuItem(edit_menu, IDM_EDIT_REDO, MF_BYCOMMAND | MF_GRAYED); } else {