From 32466ba8b70e3ea6c1b8e9dced57e686cf611bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 9 Jun 2023 01:27:17 -0300 Subject: [PATCH] ecode: Fixed plugins not finding a running LSP for a language that it's not the default that made run the LSP when LSPs are shared between languages. Finished implementing the project build configuration (added clone and fixed several minor stuff). Fixed keybindings not loading the user configured keybidings in some components. Improved status bar. --- .ecode/project_build.json | 40 ++++ include/eepp/scene/event.hpp | 1 + .../eepp/ui/abstract/uiabstracttableview.hpp | 1 + src/eepp/ui/abstract/uiabstracttableview.cpp | 4 + src/eepp/ui/abstract/uiabstractview.cpp | 2 +- src/tools/ecode/ecode.cpp | 171 ++++++++++++----- src/tools/ecode/ecode.hpp | 20 +- .../ecode/plugins/lsp/lspclientplugin.cpp | 5 +- .../ecode/plugins/lsp/lspclientserver.cpp | 14 +- .../ecode/plugins/lsp/lspclientserver.hpp | 6 +- .../plugins/lsp/lspclientservermanager.cpp | 26 ++- .../plugins/lsp/lspclientservermanager.hpp | 3 +- src/tools/ecode/plugins/lsp/lspdefinition.hpp | 2 +- src/tools/ecode/projectbuild.cpp | 180 +++++++++++------- src/tools/ecode/projectbuild.hpp | 6 + .../ecode/statusbuildoutputcontroller.cpp | 8 +- src/tools/ecode/statusterminalcontroller.cpp | 11 +- src/tools/ecode/terminalmanager.cpp | 23 ++- src/tools/ecode/terminalmanager.hpp | 2 + src/tools/ecode/uibuildsettings.cpp | 37 +++- src/tools/ecode/uistatusbar.cpp | 45 +++-- src/tools/ecode/uistatusbar.hpp | 2 + src/tools/ecode/uiwelcomescreen.cpp | 1 - 23 files changed, 426 insertions(+), 184 deletions(-) diff --git a/.ecode/project_build.json b/.ecode/project_build.json index 071c66627..162193d2c 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -38,5 +38,45 @@ "var": { "build_dir": "${project_root}/make/${os}" } + }, + "eepp": { + "build": [ + { + "args": "--with-debug-symbols gmake", + "command": "premake4", + "working_dir": "${project_root}" + }, + { + "args": "-j${nproc} config=${build_type}", + "command": "make", + "working_dir": "${build_dir}" + } + ], + "build_types": [ + "debug", + "release" + ], + "clean": [ + { + "args": "config=${build_type} clean", + "command": "make", + "working_dir": "${build_dir}" + } + ], + "config": { + "clear_sys_env": false + }, + "os": [ + "linux" + ], + "output_parser": { + "config": { + "preset": "generic", + "relative_file_paths": true + } + }, + "var": { + "build_dir": "${project_root}/make/${os}" + } } } \ No newline at end of file diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index e00e02857..fc1f7a0c8 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -98,6 +98,7 @@ class EE_API Event { OnWindowAdded, OnWindowRemoved, OnItemValueChange, + OnCopy, NoEvent = eeINDEX_NOT_FOUND }; diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index a1c0425a5..24976124e 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -112,6 +112,7 @@ class EE_API UIAbstractTableView : public UIAbstractView { /** Tries to make all columns visible in the widget content. */ void setFitAllColumnsToWidget( bool fitAllColumnsToWidget ); + void recalculateColumnsWidth(); protected: friend class EE::UI::UITableHeaderColumn; diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 2e3cdfee1..076de6254 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -785,4 +785,8 @@ void UIAbstractTableView::setFitAllColumnsToWidget( bool fitAllColumnsToWidget ) mFitAllColumnsToWidget = fitAllColumnsToWidget; } +void UIAbstractTableView::recalculateColumnsWidth() { + createOrUpdateColumns( false ); +} + }}} // namespace EE::UI::Abstract diff --git a/src/eepp/ui/abstract/uiabstractview.cpp b/src/eepp/ui/abstract/uiabstractview.cpp index 0bad4957d..52e3ad346 100644 --- a/src/eepp/ui/abstract/uiabstractview.cpp +++ b/src/eepp/ui/abstract/uiabstractview.cpp @@ -153,7 +153,7 @@ void UIAbstractView::beginEditing( const ModelIndex& index, UIWidget* editedWidg mEditWidget->toFront(); mEditingDelegate->willBeginEditing(); mEditingDelegate->onCommit = [this]() { - if ( getModel() ) + if ( getModel() && mEditIndex.isValid() ) getModel()->setData( mEditIndex, mEditingDelegate->getValue() ); stopEditing(); }; diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 4999c40dd..1de1eba36 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -763,6 +763,18 @@ UITabWidget* App::getSidePanel() const { return mSidePanel; } +const std::map& App::getRealLocalKeybindings() const { + return mRealLocalKeybindings; +} + +const std::map& App::getRealSplitterKeybindings() const { + return mRealSplitterKeybindings; +} + +const std::map& App::getRealTerminalKeybindings() const { + return mRealTerminalKeybindings; +} + void App::switchSidePanel() { mConfig.ui.showSidePanel = !mConfig.ui.showSidePanel; mSettings->getWindowMenu() @@ -1387,41 +1399,91 @@ static void updateKeybindings( IniFile& ini, const std::string& group, Input* in } void App::loadKeybindings() { - if ( mKeybindings.empty() ) { - KeyBindings bindings( mWindow->getInput() ); - IniFile ini( mKeybindingsPath ); + if ( !mKeybindings.empty() ) + return; - std::string defMod = ini.getValue( "modifier", "mod", "" ); - if ( defMod.empty() ) { - defMod = KeyMod::getDefaultModifierString(); - ini.setValue( "modifier", "mod", defMod ); - ini.writeFile(); - } + KeyBindings bindings( mWindow->getInput() ); + IniFile ini( mKeybindingsPath ); - bool forceRebind = false; - auto version = ini.getValueU( "version", "version", 0 ); - if ( version != ecode::Version::getVersionNum() ) { - ini.setValueU( "version", "version", ecode::Version::getVersionNum() ); - ini.writeFile(); - forceRebind = true; - } - - Uint32 defModKeyCode = KeyMod::getKeyMod( defMod ); - if ( KEYMOD_NONE != defModKeyCode ) - KeyMod::setDefaultModifier( defModKeyCode ); - - updateKeybindings( ini, "editor", mWindow->getInput(), mKeybindings, mKeybindingsInvert, - getDefaultKeybindings(), forceRebind, getMigrateKeybindings(), - mConfig.iniState ); - - updateKeybindings( ini, "global_search", mWindow->getInput(), mGlobalSearchKeybindings, - GlobalSearchController::getDefaultKeybindings(), forceRebind, - getMigrateKeybindings(), mConfig.iniState ); - - updateKeybindings( ini, "document_search", mWindow->getInput(), mDocumentSearchKeybindings, - DocSearchController::getDefaultKeybindings(), forceRebind, - getMigrateKeybindings(), mConfig.iniState ); + std::string defMod = ini.getValue( "modifier", "mod", "" ); + if ( defMod.empty() ) { + defMod = KeyMod::getDefaultModifierString(); + ini.setValue( "modifier", "mod", defMod ); + ini.writeFile(); } + + bool forceRebind = false; + auto version = ini.getValueU( "version", "version", 0 ); + if ( version != ecode::Version::getVersionNum() ) { + ini.setValueU( "version", "version", ecode::Version::getVersionNum() ); + ini.writeFile(); + forceRebind = true; + } + + Uint32 defModKeyCode = KeyMod::getKeyMod( defMod ); + if ( KEYMOD_NONE != defModKeyCode ) + KeyMod::setDefaultModifier( defModKeyCode ); + + updateKeybindings( ini, "editor", mWindow->getInput(), mKeybindings, mKeybindingsInvert, + getDefaultKeybindings(), forceRebind, getMigrateKeybindings(), + mConfig.iniState ); + + updateKeybindings( ini, "global_search", mWindow->getInput(), mGlobalSearchKeybindings, + GlobalSearchController::getDefaultKeybindings(), forceRebind, + getMigrateKeybindings(), mConfig.iniState ); + + updateKeybindings( ini, "document_search", mWindow->getInput(), mDocumentSearchKeybindings, + DocSearchController::getDefaultKeybindings(), forceRebind, + getMigrateKeybindings(), mConfig.iniState ); + + auto localKeybindings = getLocalKeybindings(); + for ( const auto& kb : localKeybindings ) { + auto found = mKeybindingsInvert.find( kb.second ); + if ( found != mKeybindingsInvert.end() ) { + mRealLocalKeybindings[bindings.getShortcutFromString( found->second )] = kb.second; + } else { + mRealLocalKeybindings[kb.first] = kb.second; + } + } + + auto localSplitterKeybindings = UICodeEditorSplitter::getLocalDefaultKeybindings(); + for ( const auto& kb : localSplitterKeybindings ) { + auto found = mKeybindingsInvert.find( kb.second ); + if ( found != mKeybindingsInvert.end() ) { + mRealSplitterKeybindings[bindings.getShortcutFromString( found->second )] = kb.second; + } else { + mRealSplitterKeybindings[kb.first] = kb.second; + } + } + + auto localTerminalKeybindings = TerminalManager::getTerminalKeybindings(); + for ( const auto& kb : localTerminalKeybindings ) { + auto found = mKeybindingsInvert.find( kb.second ); + if ( found != mKeybindingsInvert.end() ) { + mRealTerminalKeybindings[bindings.getShortcutFromString( found->second )] = kb.second; + } else { + mRealTerminalKeybindings[kb.first] = kb.second; + } + } +} + +void App::reloadKeybindings() { + mKeybindings.clear(); + mKeybindingsInvert.clear(); + mRealLocalKeybindings.clear(); + mRealSplitterKeybindings.clear(); + mRealTerminalKeybindings.clear(); + mRealDefaultKeybindings.clear(); + loadKeybindings(); + mSplitter->forEachEditor( [&]( UICodeEditor* ed ) { + ed->getKeyBindings().reset(); + ed->getKeyBindings().addKeybindsStringUnordered( mKeybindings ); + } ); + mSplitter->forEachWidgetType( UI_TYPE_TERMINAL, [this]( UIWidget* widget ) { + mTerminalManager->setKeybindings( widget->asType() ); + } ); + mMainLayout->getKeyBindings().reset(); + mMainLayout->getKeyBindings().addKeybinds( getRealDefaultKeybindings() ); } void App::onDocumentStateChanged( UICodeEditor*, TextDocument& ) { @@ -1681,6 +1743,18 @@ AppConfig& App::getConfig() { return mConfig; } +const std::map& App::getRealDefaultKeybindings() { + if ( mRealDefaultKeybindings.empty() ) { + mRealDefaultKeybindings.insert( mRealLocalKeybindings.begin(), + mRealLocalKeybindings.end() ); + mRealDefaultKeybindings.insert( mRealSplitterKeybindings.begin(), + mRealSplitterKeybindings.end() ); + mRealDefaultKeybindings.insert( mRealTerminalKeybindings.begin(), + mRealTerminalKeybindings.end() ); + } + return mRealDefaultKeybindings; +} + std::map App::getDefaultKeybindings() { auto bindings = UICodeEditorSplitter::getDefaultKeybindings(); auto local = getLocalKeybindings(); @@ -1715,10 +1789,10 @@ std::map App::getLocalKeybindings() { { { KEY_K, KEYMOD_CTRL | KEYMOD_LALT | KEYMOD_SHIFT }, "terminal-split-bottom" }, { { KEY_S, KEYMOD_CTRL | KEYMOD_LALT | KEYMOD_SHIFT }, "terminal-split-swap" }, { { KEY_T, KEYMOD_CTRL | KEYMOD_LALT | KEYMOD_SHIFT }, "reopen-closed-tab" }, - { { KEY_1, KEYMOD_LALT }, "toggle-locatebar" }, - { { KEY_2, KEYMOD_LALT }, "toggle-global-search" }, - { { KEY_3, KEYMOD_LALT }, "toggle-status-build-output" }, - { { KEY_4, KEYMOD_LALT }, "toggle-status-terminal" }, + { { KEY_1, KEYMOD_LALT }, "toggle-status-locate-bar" }, + { { KEY_2, KEYMOD_LALT }, "toggle-status-global-search-bar" }, + { { KEY_3, KEYMOD_LALT }, "toggle-status-terminal" }, + { { KEY_4, KEYMOD_LALT }, "toggle-status-build-output" }, }; } @@ -1749,8 +1823,8 @@ std::vector App::getUnlockedCommands() { "open-global-search", "project-build-start", "project-build-cancel", - "toggle-locatebar", - "toggle-global-search", + "toggle-status-locate-bar", + "toggle-status-global-search-bar", "toggle-status-build-output", "toggle-status-terminal", "menu-toggle", @@ -2120,13 +2194,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { if ( mSplitter->curEditorExistsAndFocused() && mSplitter->getCurEditor() == editor ) editor->setFocus(); if ( editor->getDocument().getFilePath() == mKeybindingsPath ) { - mKeybindings.clear(); - mKeybindingsInvert.clear(); - loadKeybindings(); - mSplitter->forEachEditor( [&]( UICodeEditor* ed ) { - ed->getKeyBindings().reset(); - ed->getKeyBindings().addKeybindsStringUnordered( mKeybindings ); - } ); + reloadKeybindings(); } else if ( mFileSystemMatcher && mFileSystemMatcher->getIgnoreFilePath() == editor->getDocument().getFilePath() ) { loadFileSystemMatcher( mFileSystemMatcher ? mFileSystemMatcher->getPath() @@ -3532,10 +3600,10 @@ TableView#locate_bar_table > tableview::row:selected > tableview::cell:nth-child - - - - + + + + @@ -3821,7 +3889,6 @@ TableView#locate_bar_table > tableview::row:selected > tableview::cell:nth-child mMainSplitter->setSplitPartition( StyleSheetLength( mConfig.windowState.statusBarPartition ) ); mStatusBar = mUISceneNode->find( "status_bar" ); - mStatusBar->setApp( this ); #if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN mFileWatcher = new efsw::FileWatcher(); @@ -3856,6 +3923,8 @@ TableView#locate_bar_table > tableview::row:selected > tableview::cell:nth-child initImageView(); + mStatusBar->setApp( this ); + mSettings = std::make_unique(); mSettings->createSettingsMenu( this ); @@ -3867,7 +3936,7 @@ TableView#locate_bar_table > tableview::row:selected > tableview::cell:nth-child mConsole->setVisible( false ); registerUnlockedCommands( *mMainLayout ); - mMainLayout->getKeyBindings().addKeybinds( getDefaultKeybindings() ); + mMainLayout->getKeyBindings().addKeybinds( getRealDefaultKeybindings() ); Log::instance()->setKeepLog( false ); Log::info( "Complete UI took: %.2f ms", globalClock.getElapsedTime().asMilliseconds() ); diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index 22e5b252c..95bdf7c46 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -133,6 +133,8 @@ class App : public UICodeEditorSplitter::Client { Drawable* findIcon( const std::string& name, const size_t iconSize ); + const std::map& getRealDefaultKeybindings(); + std::map getDefaultKeybindings(); std::map getLocalKeybindings(); @@ -226,13 +228,13 @@ class App : public UICodeEditorSplitter::Client { t.setCommand( "console-toggle", [&] { consoleToggle(); } ); t.setCommand( "find-replace", [&] { showFindView(); } ); t.setCommand( "open-global-search", [&] { showGlobalSearch( false ); } ); - t.setCommand( "toggle-global-search", + t.setCommand( "toggle-status-global-search-bar", [&] { mGlobalSearchController->toggleGlobalSearchBar(); } ); t.setCommand( "toggle-status-build-output", [&] { mStatusBuildOutputController->toggle(); } ); t.setCommand( "toggle-status-terminal", [&] { mStatusTerminalController->toggle(); } ); t.setCommand( "open-locatebar", [&] { mUniversalLocator->showLocateBar(); } ); - t.setCommand( "toggle-locatebar", [&] { mUniversalLocator->toggleLocateBar(); } ); + t.setCommand( "toggle-status-locate-bar", [&] { mUniversalLocator->toggleLocateBar(); } ); t.setCommand( "open-command-palette", [&] { mUniversalLocator->showCommandPalette(); } ); t.setCommand( "project-build-start", [&] { if ( mProjectBuildManager && mStatusBuildOutputController ) { @@ -395,7 +397,13 @@ class App : public UICodeEditorSplitter::Client { UITabWidget* getSidePanel() const; - protected: + const std::map& getRealLocalKeybindings() const; + + const std::map& getRealSplitterKeybindings() const; + + const std::map& getRealTerminalKeybindings() const; + + protected: std::vector mArgs; EE::Window::Window* mWindow{ nullptr }; UISceneNode* mUISceneNode{ nullptr }; @@ -418,6 +426,10 @@ class App : public UICodeEditorSplitter::Client { std::unordered_map mKeybindingsInvert; std::unordered_map mGlobalSearchKeybindings; std::unordered_map mDocumentSearchKeybindings; + std::map mRealLocalKeybindings; + std::map mRealSplitterKeybindings; + std::map mRealTerminalKeybindings; + std::map mRealDefaultKeybindings; std::string mConfigPath; std::string mPluginsPath; std::string mColorSchemesPath; @@ -502,6 +514,8 @@ class App : public UICodeEditorSplitter::Client { void loadKeybindings(); + void reloadKeybindings(); + void onDocumentStateChanged( UICodeEditor*, TextDocument& ); void onDocumentModified( UICodeEditor* editor, TextDocument& ); diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index cff189707..fd6c245a1 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -845,7 +845,8 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std std::string name = obj.contains( "name" ) ? obj["name"] : obj["use"]; if ( lspR.name == name ) { lspOverwritten = true; - lspR.usesOtherDefinition = obj.contains( "use" ); + if ( obj.contains( "use" ) && obj["use"].is_string() ) + lspR.usesLSP = obj["use"].get(); if ( obj.contains( "share_process" ) && obj["share_process"].is_boolean() ) { lspR.shareProcessWithOtherDefinition = obj["share_process"].get(); } @@ -896,7 +897,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std lsp.host = tlsp.host; lsp.port = tlsp.port; lsp.initializationOptions = tlsp.initializationOptions; - lsp.usesOtherDefinition = true; + lsp.usesLSP = use; if ( obj.contains( "share_process" ) && obj["share_process"].is_boolean() ) { lsp.shareProcessWithOtherDefinition = obj["share_process"].get(); } diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 1f629429d..45e81f0a0 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -1114,8 +1114,13 @@ void LSPClientServer::initialize() { } LSPClientServer::LSPClientServer( LSPClientServerManager* manager, const String::HashType& id, - const LSPDefinition& lsp, const std::string& rootPath ) : - mManager( manager ), mId( id ), mLSP( lsp ), mRootPath( rootPath ) {} + const LSPDefinition& lsp, const std::string& rootPath, + const std::vector& languagesSupported ) : + mManager( manager ), + mId( id ), + mLSP( lsp ), + mRootPath( rootPath ), + mLanguagesSupported( languagesSupported ) {} LSPClientServer::~LSPClientServer() { shutdown(); @@ -2050,4 +2055,9 @@ void LSPClientServer::shutdown() { } } +bool LSPClientServer::supportsLanguage( const std::string& lang ) const { + return std::find( mLanguagesSupported.begin(), mLanguagesSupported.end(), lang ) != + mLanguagesSupported.end(); +} + } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index 55598deec..60e4d10b0 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -61,7 +61,8 @@ class LSPClientServer { }; LSPClientServer( LSPClientServerManager* manager, const String::HashType& id, - const LSPDefinition& lsp, const std::string& rootPath ); + const LSPDefinition& lsp, const std::string& rootPath, + const std::vector& languagesSupported ); ~LSPClientServer(); @@ -221,6 +222,8 @@ class LSPClientServer { void shutdown(); + bool supportsLanguage( const std::string& lang ) const; + protected: LSPClientServerManager* mManager{ nullptr }; String::HashType mId; @@ -247,6 +250,7 @@ class LSPClientServer { std::string mReceiveErr; LSPServerCapabilities mCapabilities; URI mWorkspaceFolder; + std::vector mLanguagesSupported; struct DidChangeQueue { URI uri; diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp index 5ea3b106a..892ece3e9 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp @@ -45,8 +45,9 @@ LSPClientServerManager::supportsLSP( const std::shared_ptr& doc ) std::unique_ptr LSPClientServerManager::runLSPServer( const String::HashType& id, const LSPDefinition& lsp, - const std::string& rootPath ) { - auto server = std::make_unique( this, id, lsp, rootPath ); + const std::string& rootPath, + std::vector languagesSupported ) { + auto server = std::make_unique( this, id, lsp, rootPath, languagesSupported ); server->start(); return server; } @@ -96,7 +97,22 @@ void LSPClientServerManager::tryRunServer( const std::shared_ptr& Lock l( mClientsMutex ); auto clientIt = mClients.find( id ); if ( clientIt == mClients.end() ) { - std::unique_ptr serverUP = runLSPServer( id, lsp, rootPath ); + auto rlsp = lsp; + if ( !lsp.usesLSP.empty() ) { + for ( const auto& flsp : mLSPs ) { + if ( flsp.name == lsp.usesLSP ) { + rlsp = flsp; + break; + } + } + } + std::vector languagesSupported; + languagesSupported.push_back( rlsp.language ); + for ( const auto& flsp : mLSPs ) + if ( flsp.name == lsp.usesLSP ) + languagesSupported.push_back( flsp.language ); + std::unique_ptr serverUP = + runLSPServer( id, rlsp, rootPath, languagesSupported ); if ( ( server = serverUP.get() ) ) { mClients[id] = std::move( serverUP ); if ( !mLSPWorkspaceFolder.uri.empty() ) @@ -471,7 +487,7 @@ LSPClientServer* LSPClientServerManager::getOneLSPClientServer( const URI& uri ) LSPClientServer* LSPClientServerManager::getOneLSPClientServer( const std::string& language ) { Lock l( mClientsMutex ); for ( auto& server : mClients ) { - if ( server.second->getDefinition().language == language ) + if ( server.second->supportsLanguage( language ) ) return server.second.get(); } return nullptr; @@ -482,7 +498,7 @@ LSPClientServerManager::getLSPClientServers( const std::string& language ) { std::vector servers; Lock l( mClientsMutex ); for ( auto& server : mClients ) { - if ( server.second->getDefinition().language == language ) + if ( server.second->supportsLanguage( language ) ) servers.push_back( server.second.get() ); } return servers; diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp index ea699c483..b3e7ed5b9 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp @@ -114,7 +114,8 @@ class LSPClientServerManager { std::unique_ptr runLSPServer( const String::HashType& id, const LSPDefinition& lsp, - const std::string& rootPath ); + const std::string& rootPath, + std::vector languagesSupported ); std::string findRootPath( const LSPDefinition& lsp, const std::shared_ptr& doc ); diff --git a/src/tools/ecode/plugins/lsp/lspdefinition.hpp b/src/tools/ecode/plugins/lsp/lspdefinition.hpp index 2224ab2be..7939e4657 100644 --- a/src/tools/ecode/plugins/lsp/lspdefinition.hpp +++ b/src/tools/ecode/plugins/lsp/lspdefinition.hpp @@ -24,7 +24,7 @@ struct LSPDefinition { int port{ 0 }; nlohmann::json initializationOptions; - bool usesOtherDefinition{ false }; + std::string usesLSP; bool shareProcessWithOtherDefinition{ false }; bool disabled{ false }; diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index bc8677777..9b3ef52b5 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -170,9 +170,113 @@ ProjectBuildManager::ProjectBuildManager( const std::string& projectRoot, void ProjectBuildManager::addNewBuild() { std::string name = mNewBuild.getName(); - ProjectBuild build = mNewBuild; + ProjectBuild newBuild = mNewBuild; + bool found; + do { + found = false; + for ( const auto& b : mBuilds ) { + if ( b.first == name ) { + name += " (" + mApp->i18n( "copy", "Copy" ) + ")"; + found = true; + } + } + } while ( found ); + newBuild.mName = name; mBuilds.insert( - std::make_pair( std::move( name ), std::move( build ) ) ); + std::make_pair( std::move( name ), std::move( newBuild ) ) ); +} + +bool ProjectBuildManager::cloneBuild( const std::string& build, std::string newBuildName ) { + auto oldBuild = mBuilds.find( build ); + if ( oldBuild == mBuilds.end() ) + return false; + ProjectBuild newBuild = oldBuild->second; + newBuild.mName = newBuildName; + mBuilds.insert( std::make_pair( std::move( newBuildName ), + std::move( newBuild ) ) ); + return true; +} + +void ProjectBuildManager::addBuild( UIWidget* buildTab ) { + mNewBuild = ProjectBuild( mApp->i18n( "new_name", "new_name" ), mProjectRoot ); + std::string hashName = String::toString( String::hash( "new_name" ) ); + UIWidget* widget = nullptr; + if ( ( widget = buildTab->getUISceneNode()->getRoot()->querySelector( "#build_settings_" + + hashName ) ) ) { + widget->asType()->select(); + return; + } + auto ret = + mApp->getSplitter()->createWidget( UIBuildSettings::New( mNewBuild, mConfig, true ), + mApp->i18n( "build_settings", "Build Settings" ) ); + auto bs = ret.second->asType(); + bs->setTab( ret.first ); + mCbs[bs].insert( bs->on( Event::OnConfirm, [this, bs]( const Event* event ) { + event->getNode()->removeEventListener( event->getCallbackId() ); + mCbs[bs].erase( event->getCallbackId() ); + addNewBuild(); + saveAsync(); + updateSidePanelTab(); + } ) ); + mCbs[bs].insert( bs->on( Event::OnClose, [this, bs]( auto ) { mCbs.erase( bs ); } ) ); + bs->on( Event::OnClear, [this]( const Event* event ) { + if ( mBuilds.erase( event->asTextEvent()->getText() ) > 0 ) { + if ( mConfig.buildName == event->asTextEvent()->getText() ) + mConfig.buildName = mBuilds.empty() ? "" : mBuilds.begin()->first; + updateSidePanelTab(); + } + } ); + ret.first->setIcon( mApp->findIcon( "hammer" ) ); +} + +void ProjectBuildManager::editBuild( std::string buildName, UIWidget* buildTab ) { + if ( buildName.empty() ) + return; + + auto build = mBuilds.find( buildName ); + if ( build == mBuilds.end() ) + return; + + std::string hashName = String::toString( String::hash( buildName ) ); + UIWidget* widget = nullptr; + if ( ( widget = buildTab->getUISceneNode()->getRoot()->querySelector( "#build_settings_" + + hashName ) ) ) { + widget->asType()->select(); + return; + } + auto ret = + mApp->getSplitter()->createWidget( UIBuildSettings::New( build->second, mConfig, false ), + mApp->i18n( "build_settings", "Build Settings" ) ); + auto bs = ret.second->asType(); + bs->setTab( ret.first ); + mCbs[bs].insert( bs->on( Event::OnConfirm, [this, bs]( const Event* event ) { + event->getNode()->removeEventListener( event->getCallbackId() ); + mCbs[bs].erase( event->getCallbackId() ); + saveAsync(); + } ) ); + mCbs[bs].insert( bs->on( Event::OnClose, [this, bs]( auto ) { mCbs.erase( bs ); } ) ); + bs->on( Event::OnClear, [this]( const Event* event ) { + if ( mBuilds.erase( event->asTextEvent()->getText() ) > 0 ) { + if ( mConfig.buildName == event->asTextEvent()->getText() ) + mConfig.buildName = mBuilds.empty() ? "" : mBuilds.begin()->first; + updateSidePanelTab(); + } + } ); + bs->on( Event::OnCopy, [this, buildName, buildTab]( const Event* event ) { + std::string clonedName( event->asTextEvent()->getText() ); + if ( mBuilds.find( clonedName ) != mBuilds.end() ) { + UIMessageBox::New( + UIMessageBox::OK, + mApp->i18n( "cloned_name_must_be_different", + "Cloned name must be different from any existing build name." ) ) + ->show(); + } else { + cloneBuild( buildName, clonedName ); + updateSidePanelTab(); + editBuild( clonedName, buildTab ); + } + } ); + ret.first->setIcon( mApp->findIcon( "hammer" ) ); } ProjectBuildManager::~ProjectBuildManager() { @@ -746,6 +850,8 @@ void ProjectBuildManager::updateSidePanelTab() { first = tbuild.first; } + std::sort( buildNamesItems.begin(), buildNamesItems.end() ); + buildList->getListBox()->addListBoxItems( buildNamesItems ); if ( !first.empty() && buildList->getListBox()->getItemIndex( first ) != eeINDEX_NOT_FOUND ) { @@ -792,80 +898,15 @@ void ProjectBuildManager::updateSidePanelTab() { } } ); - buildAdd->onClick( [this, buildTab]( auto ) { - mNewBuild = ProjectBuild( mApp->i18n( "new_name", "new_name" ), mProjectRoot ); - UIWidget* widget = nullptr; - if ( ( widget = buildTab->getUISceneNode()->getRoot()->querySelector( - "#build_settings_new_name" ) ) ) { - widget->asType()->select(); - return; - } - auto ret = - mApp->getSplitter()->createWidget( UIBuildSettings::New( mNewBuild, mConfig, true ), - mApp->i18n( "build_settings", "Build Settings" ) ); - auto bs = ret.second->asType(); - bs->setTab( ret.first ); - mCbs[bs].insert( bs->on( Event::OnConfirm, [this, bs]( const Event* event ) { - event->getNode()->removeEventListener( event->getCallbackId() ); - mCbs[bs].erase( event->getCallbackId() ); - std::string name = mNewBuild.getName(); - ProjectBuild build = mNewBuild; - mBuilds.insert( std::make_pair( std::move( name ), - std::move( build ) ) ); - saveAsync(); - updateSidePanelTab(); - } ) ); - mCbs[bs].insert( bs->on( Event::OnClose, [this, bs]( auto ) { mCbs.erase( bs ); } ) ); - bs->on( Event::OnClear, [this]( const Event* event ) { - if ( mBuilds.erase( event->asTextEvent()->getText() ) > 0 ) { - if ( mConfig.buildName == event->asTextEvent()->getText() ) - mConfig.buildName = mBuilds.empty() ? "" : mBuilds.begin()->first; - updateSidePanelTab(); - } - } ); - ret.first->setIcon( mApp->findIcon( "hammer" ) ); - } ); + buildAdd->onClick( [this, buildTab]( auto ) { addBuild( buildTab ); } ); - buildEdit->onClick( [this, buildTab]( auto ) { - if ( !mConfig.buildName.empty() ) { - auto build = mBuilds.find( mConfig.buildName ); - if ( build != mBuilds.end() ) { - UIWidget* widget = nullptr; - if ( ( widget = buildTab->getUISceneNode()->getRoot()->querySelector( - "#build_settings_" + mConfig.buildName ) ) ) { - widget->asType()->select(); - return; - } - auto ret = mApp->getSplitter()->createWidget( - UIBuildSettings::New( build->second, mConfig, false ), - mApp->i18n( "build_settings", "Build Settings" ) ); - auto bs = ret.second->asType(); - bs->setTab( ret.first ); - mCbs[bs].insert( bs->on( Event::OnConfirm, [this, bs]( const Event* event ) { - event->getNode()->removeEventListener( event->getCallbackId() ); - mCbs[bs].erase( event->getCallbackId() ); - saveAsync(); - } ) ); - mCbs[bs].insert( - bs->on( Event::OnClose, [this, bs]( auto ) { mCbs.erase( bs ); } ) ); - bs->on( Event::OnClear, [this]( const Event* event ) { - if ( mBuilds.erase( event->asTextEvent()->getText() ) > 0 ) { - if ( mConfig.buildName == event->asTextEvent()->getText() ) - mConfig.buildName = mBuilds.empty() ? "" : mBuilds.begin()->first; - updateSidePanelTab(); - } - } ); - ret.first->setIcon( mApp->findIcon( "hammer" ) ); - } - } - } ); + buildEdit->onClick( [this, buildTab]( auto ) { editBuild( mConfig.buildName, buildTab ); } ); } void ProjectBuildManager::updateBuildType() { UIWidget* buildTab = mTab->getOwnedWidget()->find( "build_tab" ); UIDropDownList* buildList = buildTab->find( "build_list" ); UIDropDownList* buildTypeList = buildTab->find( "build_type_list" ); - // UIPushButton* buildTypeAdd = buildTab->find( "build_type_add" ); buildTypeList->getListBox()->clear(); @@ -888,7 +929,6 @@ void ProjectBuildManager::updateBuildType() { } } buildTypeList->setEnabled( !buildTypeList->getListBox()->isEmpty() ); - // buildTypeAdd->setEnabled( !mConfig.buildName.empty() ); buildTypeList->removeEventsOfType( Event::OnItemSelected ); buildTypeList->addEventListener( Event::OnItemSelected, [this, buildTypeList]( const Event* ) { diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index cd8a510a2..abe52794d 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -320,6 +320,12 @@ class ProjectBuildManager { void updateBuildType(); void addNewBuild(); + + bool cloneBuild( const std::string& build, std::string newBuildName ); + + void addBuild( UIWidget* buildTab ); + + void editBuild( std::string buildName, UIWidget* buildTab ); }; } // namespace ecode diff --git a/src/tools/ecode/statusbuildoutputcontroller.cpp b/src/tools/ecode/statusbuildoutputcontroller.cpp index 97e236637..e590c0902 100644 --- a/src/tools/ecode/statusbuildoutputcontroller.cpp +++ b/src/tools/ecode/statusbuildoutputcontroller.cpp @@ -240,10 +240,10 @@ void StatusBuildOutputController::runClean( const std::string& buildName, if ( EXIT_SUCCESS == exitCode ) { buffer = Sys::getDateTimeStr() + ": " + - mApp->i18n( "build_successful", "Build run successfully\n" ); + mApp->i18n( "clean_successful", "Clean run successfully\n" ); } else { buffer = Sys::getDateTimeStr() + ": " + - mApp->i18n( "build_failed", "Build run with errors\n" ); + mApp->i18n( "clean_failed", "Clean run with errors\n" ); } mContainer->runOnMainThread( [this, buffer]() { @@ -278,6 +278,10 @@ UICodeEditor* StatusBuildOutputController::createContainer() { editor->setLocked( true ); editor->setLineBreakingColumn( 0 ); editor->setShowLineNumber( false ); + editor->getDocument().reset(); + editor->getDocument().textInput( + mApp->i18n( "no_build_has_been_run", "No build has been run" ) ); + editor->setScrollY( editor->getMaxScroll().y ); return editor; } diff --git a/src/tools/ecode/statusterminalcontroller.cpp b/src/tools/ecode/statusterminalcontroller.cpp index 635f9d665..fc40d0602 100644 --- a/src/tools/ecode/statusterminalcontroller.cpp +++ b/src/tools/ecode/statusterminalcontroller.cpp @@ -95,16 +95,7 @@ UITerminal* StatusTerminalController::createTerminal( const std::string& working term->setColorScheme( csIt != terminalColorSchemes.end() ? terminalColorSchemes.at( currentTerminalColorScheme ) : TerminalColorScheme::getDefault() ); - term->addKeyBinds( mApp->getLocalKeybindings() ); - term->addKeyBinds( UICodeEditorSplitter::getLocalDefaultKeybindings() ); - term->addKeyBinds( { { { KEY_E, KEYMOD_CTRL | KEYMOD_LALT | KEYMOD_SHIFT }, - UITerminal::getExclusiveModeToggleCommandName() } } ); - // Remove the keybinds that are problematic for a terminal - term->getKeyBindings().removeCommandsKeybind( - { "open-file", "download-file-web", "open-folder", "debug-draw-highlight-toggle", - "debug-draw-boxes-toggle", "debug-draw-debug-data", "debug-widget-tree-view", - "open-locatebar", "open-command-palette", "open-global-search", "menu-toggle", - "console-toggle", "go-to-line" } ); + mApp->getTerminalManager()->setKeybindings( term ); term->setCommand( "switch-to-previous-colorscheme", [&] { auto it = mApp->getTerminalManager()->getTerminalColorSchemes().find( mApp->getTerminalManager()->getTerminalCurrentColorScheme() ); diff --git a/src/tools/ecode/terminalmanager.cpp b/src/tools/ecode/terminalmanager.cpp index 521235381..fd4b85227 100644 --- a/src/tools/ecode/terminalmanager.cpp +++ b/src/tools/ecode/terminalmanager.cpp @@ -287,15 +287,7 @@ UITerminal* TerminalManager::createNewTerminal( const std::string& title, UITabW return; mApp->setAppTitle( event->getNode()->asType()->getTitle() ); } ); - term->addKeyBinds( mApp->getLocalKeybindings() ); - term->addKeyBinds( UICodeEditorSplitter::getLocalDefaultKeybindings() ); - term->addKeyBinds( getTerminalKeybindings() ); - // Remove the keybinds that are problematic for a terminal - term->getKeyBindings().removeCommandsKeybind( - { "open-file", "download-file-web", "open-folder", "debug-draw-highlight-toggle", - "debug-draw-boxes-toggle", "debug-draw-debug-data", "debug-widget-tree-view", - "open-locatebar", "open-command-palette", "open-global-search", "menu-toggle", - "console-toggle", "go-to-line" } ); + setKeybindings( term ); term->setCommand( "terminal-rename", [&, term] { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, mApp->i18n( "new_terminal_name", "New terminal name:" ) ); @@ -336,4 +328,17 @@ UITerminal* TerminalManager::createNewTerminal( const std::string& title, UITabW #endif } +void TerminalManager::setKeybindings( UITerminal* term ) { + term->getKeyBindings().reset(); + term->addKeyBinds( mApp->getRealLocalKeybindings() ); + term->addKeyBinds( mApp->getRealSplitterKeybindings() ); + term->addKeyBinds( mApp->getRealTerminalKeybindings() ); + // Remove the keybinds that are problematic for a terminal + term->getKeyBindings().removeCommandsKeybind( + { "open-file", "download-file-web", "open-folder", "debug-draw-highlight-toggle", + "debug-draw-boxes-toggle", "debug-draw-debug-data", "debug-widget-tree-view", + "open-locatebar", "open-command-palette", "open-global-search", "menu-toggle", + "console-toggle", "go-to-line" } ); +} + } // namespace ecode diff --git a/src/tools/ecode/terminalmanager.hpp b/src/tools/ecode/terminalmanager.hpp index f039de8ed..4135badcb 100644 --- a/src/tools/ecode/terminalmanager.hpp +++ b/src/tools/ecode/terminalmanager.hpp @@ -50,6 +50,8 @@ class TerminalManager { const std::string& getTerminalCurrentColorScheme() { return mTerminalCurrentColorScheme; } + void setKeybindings( UITerminal* term ); + protected: App* mApp; std::string mTerminalColorSchemesPath; diff --git a/src/tools/ecode/uibuildsettings.cpp b/src/tools/ecode/uibuildsettings.cpp index 788082a7b..1bc049c43 100644 --- a/src/tools/ecode/uibuildsettings.cpp +++ b/src/tools/ecode/uibuildsettings.cpp @@ -306,6 +306,7 @@ static const auto SETTINGS_PANEL_XML = R"xml( + @@ -543,6 +544,23 @@ UIBuildSettings::UIBuildSettings( ProjectBuild& build, ProjectBuildConfiguration } ); } ); + find( "build_clone" )->onClick( [this]( auto ) { + UIMessageBox* msgBox = UIMessageBox::New( + UIMessageBox::INPUT, i18n( "build_cloned_name", "New Cloned Build Name:" ) ); + msgBox->setTitle( i18n( "build_settings", "Build Settings" ) ); + msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); + msgBox->showWhenReady(); + msgBox->on( Event::OnWindowReady, [this, msgBox]( auto ) { + msgBox->getTextInput()->setText( mBuild.getName() ); + msgBox->getTextInput()->getDocument().selectAll(); + } ); + msgBox->addEventListener( Event::OnConfirm, [msgBox, this]( const Event* ) { + const auto& newBuildName = msgBox->getTextInput()->getText(); + sendTextEvent( Event::OnCopy, newBuildName ); + msgBox->closeWindow(); + } ); + } ); + find( "build_type_add" )->onClick( [this, buildTypeDropDown, panelBuildTypeDDL]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, i18n( "build_type_name", "Build Type Name:" ) ); @@ -663,23 +681,26 @@ void UIBuildSettings::refreshTab() { mTab->setText( String::format( ( i18n( "build_seetings", "Build Settings" ) + ": %s" ).toUtf8().c_str(), mBuild.mName.c_str() ) ); - mTab->setId( "build_settings_" + mBuild.mName ); + std::string hashName = String::toString( String::hash( mIsNew ? "new_name" : mBuild.mName ) ); + mTab->setId( "build_settings_" + hashName ); } void UIBuildSettings::bindTable( const std::string& name, const std::string& key, ProjectBuildKeyVal& data ) { - const auto createInputDelegate = []( const ModelIndex& ) -> ModelEditingDelegate* { + UITableView* table = find( name ); + auto model = ItemPairListModel::create( data ); + const auto createInputDelegate = [table]( const ModelIndex& ) -> ModelEditingDelegate* { auto delegate = StringModelEditingDelegate::New(); - delegate->onWillBeginEditing = [delegate]() { - delegate->getWidget()->asType()->on( - Event::OnFocusLoss, [delegate]( auto ) { delegate->onCommit(); } ); + delegate->onWillBeginEditing = [delegate, table]() { + delegate->getWidget()->asType()->on( Event::OnFocusLoss, + [delegate, table]( auto ) { + delegate->onCommit(); + table->recalculateColumnsWidth(); + } ); }; return delegate; }; - - UITableView* table = find( name ); - auto model = ItemPairListModel::create( data ); model->setColumnName( 0, getTranslatorString( key + "_name", "Name" ) ); model->setColumnName( 1, getTranslatorString( key + "_value", "Value" ) ); model->setIsEditable( true ); diff --git a/src/tools/ecode/uistatusbar.cpp b/src/tools/ecode/uistatusbar.cpp index 3ed839ea9..c8456d97a 100644 --- a/src/tools/ecode/uistatusbar.cpp +++ b/src/tools/ecode/uistatusbar.cpp @@ -14,6 +14,20 @@ UIStatusBar::UIStatusBar() : WidgetCommandExecuter( getUISceneNode()->getWindow()->getInput() ) {} void UIStatusBar::updateState() { + forEachChild( [this]( Node* node ) { + if ( node->isWidget() && String::startsWith( node->getId(), "status_" ) ) { + auto widget = node->asType(); + widget->removeClass( "selected" ); + if ( nullptr == widget->getTooltip() ) { + std::string name( widget->getId() ); + String::replaceAll( name, "_", "-" ); + auto kb = mApp->getKeybind( "toggle-" + name ); + if ( !kb.empty() ) + widget->setTooltipText( kb ); + } + } + } ); + getParent()->forEachChild( [this]( Node* node ) { UIWidget* but = find( "status_" + node->getId() ); if ( but && but != this ) { @@ -25,23 +39,14 @@ void UIStatusBar::updateState() { } } ); - UIWidget* termBut = find( "status_terminal" ); - if ( termBut ) - termBut->removeClass( "selected" ); - UIWidget* boBut = find( "status_build_output" ); - if ( boBut ) - boBut->removeClass( "selected" ); - - if ( mApp->getMainSplitter() ) { - if ( mApp->getMainSplitter()->getLastWidget() ) { - UIWidget* widget = mApp->getMainSplitter()->getLastWidget(); - UIWidget* but = find( "status_" + widget->getId() ); - if ( but && but != this ) { - if ( widget->isVisible() ) { - but->addClass( "selected" ); - } else { - but->removeClass( "selected" ); - } + if ( mApp->getMainSplitter() && mApp->getMainSplitter()->getLastWidget() ) { + UIWidget* widget = mApp->getMainSplitter()->getLastWidget(); + UIWidget* but = find( "status_" + widget->getId() ); + if ( but && but != this ) { + if ( widget->isVisible() ) { + but->addClass( "selected" ); + } else { + but->removeClass( "selected" ); } } } @@ -80,6 +85,7 @@ Uint32 UIStatusBar::onMessage( const NodeMessage* msg ) { void UIStatusBar::setApp( App* app ) { mApp = app; + updateState(); } void UIStatusBar::onVisibilityChange() { @@ -87,4 +93,9 @@ void UIStatusBar::onVisibilityChange() { updateState(); } +void UIStatusBar::onChildCountChange( Node*, const bool& ) { + if ( mApp ) + updateState(); +} + } // namespace ecode diff --git a/src/tools/ecode/uistatusbar.hpp b/src/tools/ecode/uistatusbar.hpp index b867a14dc..fecb72dc8 100644 --- a/src/tools/ecode/uistatusbar.hpp +++ b/src/tools/ecode/uistatusbar.hpp @@ -28,6 +28,8 @@ class UIStatusBar : public UILinearLayout, public WidgetCommandExecuter { App* mApp{ nullptr }; virtual void onVisibilityChange(); + + virtual void onChildCountChange( Node* child, const bool& removed ); }; } // namespace ecode diff --git a/src/tools/ecode/uiwelcomescreen.cpp b/src/tools/ecode/uiwelcomescreen.cpp index 16ac506f9..20adff8c0 100644 --- a/src/tools/ecode/uiwelcomescreen.cpp +++ b/src/tools/ecode/uiwelcomescreen.cpp @@ -178,7 +178,6 @@ UIWelcomeScreen::UIWelcomeScreen( App* app ) : setId( "welcome_ecode" ); setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); app->registerUnlockedCommands( *this ); - getKeyBindings().addKeybinds( app->getDefaultKeybindings() ); getUISceneNode()->loadLayoutFromString( LAYOUT, this, String::hash( "UIWelcomeScreen" ) ); auto bindBtn = [this]( const std::string& id ) {