scenario editor disable undo/redo when dialog is open.

fix #758
This commit is contained in:
2025-06-13 09:13:46 -05:00
parent ac5a8eeb6a
commit 0e157cf434
6 changed files with 51 additions and 11 deletions

View File

@@ -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<void(const cDialog&)> cDialog::onOpen;
std::function<void(const cDialog&)> cDialog::onClose;
std::function<void(sf::RenderWindow& win)> cDialog::onLostFocus;
std::function<void(sf::RenderWindow& win)> cDialog::onGainedFocus;
std::function<void(sf::RenderWindow& win)> cDialog::onHandleEvents;
@@ -502,7 +504,7 @@ bool cDialog::sendInput(cKey key) {
void cDialog::run(std::function<void(cDialog&)> 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<void(cDialog&)> 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<void(cDialog&)> onopen){
set_cursor(former_curs);
topWindow = formerTop;
stackWindowsCorrectly();
if(cDialog::onClose) cDialog::onClose(*this);
}
void cDialog::runWithHelp(short help1, short help2, bool help_forced) {

View File

@@ -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<void(const cDialog&)> onOpen;
static std::function<void(const cDialog&)> onClose;
static std::function<void(sf::RenderWindow& win)> onLostFocus;
static std::function<void(sf::RenderWindow& win)> onGainedFocus;
// Attach a handler here for any update/input logic that uses target-specific code

View File

@@ -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<eMenu> 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:

View File

@@ -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()));
}

View File

@@ -11,6 +11,7 @@
#include <Cocoa/Cocoa.h>
#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 {

View File

@@ -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 {