• Fixed the keybinding merge bug. The helper was restoring missing default commands

by overwriting an occupied default shortcut, which treated user bindings like mod+d=duplicate-line-or-selection as stale
  because that command is not in the default keybinding table. Now it only adds a default when the default shortcut is actually
  free.

  Added a regression test in src/tests/unit_tests/uicodeeditor_tests.cpp:108 covering the reported case: mod+d reassigned, then
  an extra mod+e=show-markdown-preview binding exists. This was reported in SpartanJ/ecode#914.

• Implemented the splitter-level focus state cache, it will remember closed documents cursor state when reopened and it will restore cursor state per-code editor and not per-document (SpartanJ/ecode#893).
This commit is contained in:
Martín Lucas Golini
2026-06-25 11:26:31 -03:00
parent 0693688f73
commit e720b70863
9 changed files with 172 additions and 53 deletions
@@ -2,14 +2,18 @@
#include <eepp/scene/node.hpp>
#include <eepp/scene/scenemanager.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/inifile.hpp>
#include <eepp/ui/doc/syntaxdefinitionmanager.hpp>
#include <eepp/ui/uiapplication.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include "../../tools/ecode/keybindingshelper.cpp"
using namespace EE;
using namespace EE::UI;
using namespace EE::UI::Doc;
using namespace EE::Scene;
using namespace EE::System;
static const std::string userCode = R"objcpp(#import "common.h"
#import <cmath>
@@ -101,6 +105,87 @@ OF_APPLICATION_DELEGATE(test)
} \
}
UTEST( KeybindingsHelper, PreservesUserShortcutWhenAddingBinding ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - KeybindingsHelper Test", WindowStyle::Default,
WindowBackend::Default, 32 ),
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
const std::string path = Sys::getTempPath() + "eepp_keybindingshelper.cfg";
const std::string statePath = Sys::getTempPath() + "eepp_keybindingshelper_state.cfg";
FileSystem::fileRemove( path );
FileSystem::fileRemove( statePath );
IniFile ini( path, false );
IniFile iniState( statePath, false );
const std::string group( "editor" );
const std::string modD( "mod+d" );
const std::string modX( "mod+x" );
const std::string modE( "mod+e" );
ini.setValue( group, modD, std::string( "duplicate-line-or-selection" ) );
ini.setValue( group, modX, std::string( "cut" ) );
ini.setValue( group, modE, std::string( "show-markdown-preview" ) );
std::unordered_map<std::string, std::string> keybindings;
std::unordered_map<std::string, std::string> invertedKeybindings;
const std::map<KeyBindings::Shortcut, std::string> defaultKeybindings{
{ { KEY_D, KeyMod::getDefaultModifier() }, "select-word" },
{ { KEY_X, KeyMod::getDefaultModifier() }, "cut" },
};
ecode::KeybindingsHelper::updateKeybindings( ini, group, app.getWindow()->getInput(),
keybindings, invertedKeybindings,
defaultKeybindings, false, {}, iniState );
ASSERT_TRUE( keybindings.find( modD ) != keybindings.end() );
const std::string savedModD = ini.getValue( group, modD, "" );
EXPECT_STREQ( keybindings[modD].c_str(), "duplicate-line-or-selection" );
EXPECT_STREQ( savedModD.c_str(), "duplicate-line-or-selection" );
EXPECT_STREQ( invertedKeybindings["duplicate-line-or-selection"].c_str(), modD.c_str() );
FileSystem::fileRemove( path );
FileSystem::fileRemove( statePath );
}
UTEST( KeybindingsHelper, RestoresMissingCommandWhenShortcutIsFree ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - KeybindingsHelper Test", WindowStyle::Default,
WindowBackend::Default, 32 ),
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
const std::string path = Sys::getTempPath() + "eepp_keybindingshelper_restore.cfg";
const std::string statePath = Sys::getTempPath() + "eepp_keybindingshelper_restore_state.cfg";
FileSystem::fileRemove( path );
FileSystem::fileRemove( statePath );
IniFile ini( path, false );
IniFile iniState( statePath, false );
const std::string group( "editor" );
const std::string modD( "mod+d" );
const std::string modX( "mod+x" );
ini.setValue( group, modX, std::string( "cut" ) );
std::unordered_map<std::string, std::string> keybindings;
std::unordered_map<std::string, std::string> invertedKeybindings;
const std::map<KeyBindings::Shortcut, std::string> defaultKeybindings{
{ { KEY_D, KeyMod::getDefaultModifier() }, "select-word" },
{ { KEY_X, KeyMod::getDefaultModifier() }, "cut" },
};
ecode::KeybindingsHelper::updateKeybindings( ini, group, app.getWindow()->getInput(),
keybindings, invertedKeybindings,
defaultKeybindings, false, {}, iniState );
ASSERT_TRUE( keybindings.find( modD ) != keybindings.end() );
const std::string savedModD = ini.getValue( group, modD, "" );
EXPECT_STREQ( keybindings[modD].c_str(), "select-word" );
EXPECT_STREQ( savedModD.c_str(), "select-word" );
EXPECT_STREQ( invertedKeybindings["select-word"].c_str(), modD.c_str() );
FileSystem::fileRemove( path );
FileSystem::fileRemove( statePath );
}
UTEST( UICodeEditor, DocumentViewStressTest ) {
UIApplication app(
WindowSettings( 800, 600, "eepp - Stress Test", WindowStyle::Default,