ecode: add customizable debugger panel layouts

- add a reusable right-side panel managed at the application level
  - migrate debugger tabs to UITabWidgetSplitter instances
  - support moving and splitting debugger tabs across bottom and right panels
  - restrict debugger tab drops to debugger-owned panel targets
  - serialize debugger tab layouts, panel state, and table column widths
  - add pixel and percentage column-width modes to UIAbstractTableView
  - support XML/CSS configuration and an optional width-mode context menu
  - preserve percentage totals across resizing, fit-to-content, minimum widths,
    scrollbar changes, and pixel-density rounding
  - add splitter edge hiding while preserving splitter-always-show precedence
  - use EventConnection ownership for safe widget and controller lifetimes
  - validate serialized splitter layouts with structural limits
  - display project-relative source paths in debugger stack frames
  - add ecode localization strings, CSS documentation, tests, and agent rules
This commit is contained in:
Martín Lucas Golini
2026-08-14 00:17:29 -03:00
parent a2c9adca9a
commit bf750761ae
37 changed files with 1579 additions and 131 deletions
+21
View File
@@ -6,10 +6,12 @@
#include "fontpickercontroller.hpp"
#include "keybindingshelper.hpp"
#include "pathhelper.hpp"
#include "plugins/debugger/statusdebuggercontroller.hpp"
#include "settingsactions.hpp"
#include "settingsmenu.hpp"
#include "uibuildsettings.hpp"
#include "uidownloadwindow.hpp"
#include "uirightpanel.hpp"
#include "uitreeviewfs.hpp"
#include "uiwelcomescreen.hpp"
#include "version.hpp"
@@ -860,6 +862,8 @@ bool App::loadConfig( const LogLevel& logLevel, const Sizeu& displaySize, bool s
void App::saveConfig() {
if ( !mCurrentProject.empty() )
saveSidePanelTabsOrder();
if ( mRightPanel )
mRightPanel->saveState();
mConfig.save(
mRecentFiles, mRecentFolders,
@@ -3810,6 +3814,17 @@ UISplitter* App::getMainSplitter() const {
return mMainSplitter;
}
UIRightPanel* App::getRightPanel() const {
return mRightPanel.get();
}
StatusDebuggerController* App::getStatusDebuggerController() const {
if ( !mStatusBar )
return nullptr;
auto element = mStatusBar->getStatusBarElement( "status_app_debugger" );
return static_cast<StatusDebuggerController*>( element.get() );
}
StatusTerminalController* App::getStatusTerminalController() const {
return mStatusTerminalController.get();
}
@@ -4979,6 +4994,8 @@ void App::init( InitParameters& params ) {
mSplitter->setOpenDocumentsInMainSplit( mConfig.editor.openDocumentsInMainSplit );
mSplitter->setRestoreEditorSelectionOnFocus( mConfig.editor.restoreEditorSelectionOnFocus );
mSplitter->setOnTabWidgetCreateCb( [this]( UITabWidget* tabWidget ) {
tabWidget->setAcceptsDropOfWidgetFn(
[]( const UIWidget* widget ) { return !widget->hasClass( "debugger-tab" ); } );
tabWidget->getTabBar()->onDoubleClick(
[this]( const MouseEvent* ) { mSplitter->createEditorInNewTab(); } );
} );
@@ -5032,6 +5049,10 @@ void App::init( InitParameters& params ) {
mMainSplitter = mUISceneNode->find<UISplitter>( "main_splitter" );
mMainSplitter->setSplitPartition(
StyleSheetLength( mConfig.windowState.statusBarPartition ) );
auto rightPanelSplitter = mUISceneNode->find<UISplitter>( "right_panel_splitter" );
auto rightPanelContainer = mUISceneNode->find<UILayout>( "right_panel_container" );
mRightPanel =
std::make_unique<UIRightPanel>( rightPanelSplitter, rightPanelContainer, &mConfig );
mStatusBar = mUISceneNode->find<UIStatusBar>( "status_bar" );
mPluginManager->setMainSplitter( mMainSplitter );