ecode: add centralized settings panel

Introduce modal user and project settings panels with searchable tree navigation and live configuration updates.

  - centralize settings registration and presentation
  - add user and project settings commands and shortcuts
  - preserve operational actions in the existing settings menu
  - support boolean, choice, numeric, and action-based settings
  - restore renderer notifications and choice descriptions
  - persist settings when closing the panel
  - add translated settings strings for all supported locales
  - fix live color preview updates
This commit is contained in:
Martín Lucas Golini
2026-08-24 02:34:14 -03:00
parent 6b27ea2dc8
commit 288aa931fe
9 changed files with 2532 additions and 3 deletions
+19
View File
@@ -9,6 +9,7 @@
#include "plugins/debugger/statusdebuggercontroller.hpp"
#include "settingsactions.hpp"
#include "settingsmenu.hpp"
#include "settingspanel.hpp"
#include "uibuildsettings.hpp"
#include "uidownloadwindow.hpp"
#include "uirightpanel.hpp"
@@ -2123,6 +2124,8 @@ std::map<KeyBindings::Shortcut, std::string> App::getLocalKeybindings() {
{ { KEY_F11, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "debug-widget-tree-view" },
{ { KEY_K, KeyMod::getDefaultModifier() }, "open-locatebar" },
{ { KEY_P, KeyMod::getDefaultModifier() }, "open-command-palette" },
{ { KEY_COMMA, KeyMod::getDefaultModifier() }, "open-settings" },
{ { KEY_COMMA, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-project-settings" },
{ { KEY_F, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-global-search" },
{ { KEY_L, KeyMod::getDefaultModifier() }, "go-to-line" },
#if EE_PLATFORM == EE_PLATFORM_MACOS
@@ -2206,6 +2209,8 @@ std::vector<std::string> App::getUnlockedCommands() {
"toggle-status-terminal",
"toggle-status-app-output",
"menu-toggle",
"open-settings",
"open-project-settings",
"switch-side-panel",
"toggle-status-bar",
"download-file-web",
@@ -3593,6 +3598,20 @@ void App::toggleSettingsMenu() {
mSettings->toggleSettingsMenu();
}
void App::openSettings() {
if ( !mSettingsPanel )
mSettingsPanel = std::make_unique<SettingsPanel>( this );
mSettingsPanel->show( SettingsPanel::Scope::User );
}
void App::openProjectSettings() {
if ( !projectIsOpen() )
return;
if ( !mSettingsPanel )
mSettingsPanel = std::make_unique<SettingsPanel>( this );
mSettingsPanel->show( SettingsPanel::Scope::Project );
}
void App::showFolderTreeViewTab() {
UITab* tab = mSidePanel->find<UITab>( "treeview_tab" );
if ( tab )
+9 -2
View File
@@ -34,6 +34,7 @@ class FormatterPlugin;
class DateTimeController;
class FontPickerController;
class SettingsMenu;
class SettingsPanel;
class UITreeViewFS;
class StatusDebuggerController;
class UIRightPanel;
@@ -215,6 +216,10 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
void toggleSettingsMenu();
void openSettings();
void openProjectSettings();
UIStatusBar* getStatusBar() const { return mStatusBar; }
void showFolderTreeViewTab();
@@ -243,6 +248,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
t.setCommand( "debug-draw-debug-data", [this] { debugDrawData(); } );
t.setCommand( "debug-widget-tree-view", [this] { createWidgetInspector(); } );
t.setCommand( "menu-toggle", [this] { toggleSettingsMenu(); } );
t.setCommand( "open-settings", [this] { openSettings(); } );
t.setCommand( "open-project-settings", [this] { openProjectSettings(); } );
t.setCommand( "switch-side-panel", [this] { switchSidePanel(); } );
t.setCommand( "download-file-web", [this] { downloadFileWebDialog(); } );
t.setCommand( "move-panel-left", [this] { panelPosition( PanelPosition::Left ); } );
@@ -364,8 +371,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
t.setCommand( "ui-panel-font-size", [this] { mSettingsActions->setUIPanelFontSize(); } );
t.setCommand( "sans-serif-font",
[this] { openFontDialog( mConfig.ui.sansSerifFont, false ); } );
t.setCommand( "editor-font",
[this] { openFontDialog( mConfig.ui.monospaceFont, true ); } );
t.setCommand( "editor-font", [this] { openFontDialog( mConfig.ui.monospaceFont, true ); } );
t.setCommand( "terminal-font",
[this] { openFontDialog( mConfig.ui.terminalFont, true, true ); } );
t.setCommand( "fallback-font", [this] {
@@ -777,6 +783,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
std::unique_ptr<TerminalManager> mTerminalManager;
std::unique_ptr<PluginManager> mPluginManager;
std::unique_ptr<SettingsMenu> mSettings;
std::unique_ptr<SettingsPanel> mSettingsPanel;
std::unique_ptr<DateTimeController> mDateTimeController;
std::unique_ptr<FontPickerController> mFontPickerController;
std::string mFileToOpen;
+12 -1
View File
@@ -73,6 +73,16 @@ void SettingsMenu::createSettingsMenu( App* app, UIMenuBar* menuBar ) {
getKeybind( "save-all" ) )
->setId( "save-all" );
mSettingsMenu->addSeparator()->setId( "settings-submenus-sep" );
mSettingsMenu
->add( i18n( "open_settings_ellipsis", "Open Settings..." ), findIcon( "settings" ),
getKeybind( "open-settings" ) )
->setId( "open-settings" );
mSettingsMenu
->add( i18n( "open_project_settings_ellipsis", "Open Project Settings..." ),
findIcon( "folder-settings" ), getKeybind( "open-project-settings" ) )
->setId( "open-project-settings" )
->setEnabled( mApp->projectIsOpen() );
mSettingsMenu->addSeparator();
mProjectMenu = UIPopUpMenu::New();
mDocMenu = UIPopUpMenu::New();
@@ -2353,7 +2363,7 @@ UIMenu* SettingsMenu::createViewMenu() {
} else if ( item->getId() == "enable-color-preview" ) {
mApp->getConfig().editor.colorPreview = item->asType<UIMenuCheckBox>()->isActive();
mSplitter->forEachEditor( [this]( UICodeEditor* editor ) {
editor->setEnableColorPickerOnSelection( mApp->getConfig().editor.colorPreview );
editor->setColorPreview( mApp->getConfig().editor.colorPreview );
} );
} else if ( item->getId() == "enable-color-picker" ) {
mApp->getConfig().editor.colorPickerSelection =
@@ -2587,6 +2597,7 @@ void SettingsMenu::toggleSettingsMenu() {
}
void SettingsMenu::updateProjectSettingsMenu() {
mSettingsMenu->getItemId( "open-project-settings" )->setEnabled( mApp->projectIsOpen() );
mSettingsMenu->getItemId( "project_settings" )
->setEnabled( !mApp->getCurrentProject().empty() );
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
#ifndef ECODE_SETTINGSPANEL_HPP
#define ECODE_SETTINGSPANEL_HPP
#include <eepp/ee.hpp>
#include <functional>
#include <string>
#include <vector>
namespace ecode {
class App;
class SettingsPanel {
public:
enum class Scope : Uint8 { User, Project };
explicit SettingsPanel( App* app );
void show( Scope scope );
protected:
struct SettingBinding {
std::string id;
std::string category;
String name;
String description;
UIWidget* row{ nullptr };
};
struct PanelState {
UIWindow* window{ nullptr };
EventConnectionList connections;
UITextInput* search{ nullptr };
UITreeView* categories{ nullptr };
UIScrollView* scroll{ nullptr };
UILinearLayout* settings{ nullptr };
UITextView* pageTitle{ nullptr };
std::shared_ptr<Model> categoryModel;
UIBindingGroup bindingGroup;
std::vector<std::pair<std::string, std::vector<std::string>>> categoryItems;
UnorderedMap<std::string, std::string> categoryIds;
UnorderedMap<std::string, String> categorySearchText;
UnorderedMap<std::string, String> categoryTitles;
UnorderedMap<std::string, UITextView*> categoryHeadings;
std::vector<SettingBinding> bindings;
std::string selectedCategory;
std::string categoryFilter;
void reset();
};
App* mApp{ nullptr };
PanelState mUser;
PanelState mProject;
PanelState& state( Scope scope );
void create( Scope scope );
void addUserSettings( PanelState& state );
void addProjectSettings( PanelState& state );
void addCategory( PanelState& state, const std::string& id, const String& parent,
const String& name );
void setupCategories( PanelState& state );
void addBool( PanelState& state, SettingBinding binding, bool* value,
std::function<void( bool )> apply = {} );
void addBool( PanelState& state, SettingBinding binding, std::function<bool()> get,
std::function<void( bool )> set );
void addChoice( PanelState& state, SettingBinding binding, const std::vector<String>& choices,
std::function<size_t()> get, std::function<void( size_t )> set,
std::vector<String> choiceDescriptions = {} );
void addInteger( PanelState& state, SettingBinding binding, int min, int max, int* value,
std::function<void( int )> apply = {} );
void addAction( PanelState& state, SettingBinding binding, const String& buttonText,
std::function<void()> action );
UIWidget* createRow( PanelState& state, SettingBinding& binding );
UICheckBox* createBoolControl( PanelState& state, SettingBinding& binding );
void filter( PanelState& state );
};
} // namespace ecode
#endif