From 82b13329fd928768eb3ff4d95cbea189b8a19738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 7 Jan 2024 21:22:16 -0300 Subject: [PATCH] Improve context menu in tab widget. --- .../eepp/ui/tools/uicodeeditorsplitter.hpp | 39 ++++- src/eepp/ui/tools/uicodeeditorsplitter.cpp | 153 ++++++++++++++++-- src/tools/ecode/ecode.cpp | 93 +++++++---- 3 files changed, 240 insertions(+), 45 deletions(-) diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index f45d04fc5..f52d41325 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -53,7 +53,21 @@ class EE_API UICodeEditorSplitter { virtual ~UICodeEditorSplitter(); - virtual bool tryTabClose( UIWidget* widget, UITabWidget::FocusTabBehavior focusTabBehavior ); + virtual bool tryTabClose( UIWidget* widget, UITabWidget::FocusTabBehavior focusTabBehavior, + std::function onMsgBoxCloseCb = {} ); + + virtual bool tryCloseAllTabs( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior ); + + virtual bool tryCloseOtherTabs( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior ); + + virtual bool tryCloseCleanTabs( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior ); + + virtual bool tryCloseTabsToDirection( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior, + bool toTheRight ); void closeTab( UIWidget* widget, UITabWidget::FocusTabBehavior focusTabBehavior ); @@ -223,6 +237,21 @@ class EE_API UICodeEditorSplitter { t.setCommand( "close-tab", [this] { tryTabClose( mCurWidget, UITabWidget::FocusTabBehavior::Default ); } ); + t.setCommand( "close-other-tabs", [this] { + tryCloseOtherTabs( mCurWidget, UITabWidget::FocusTabBehavior::Default ); + } ); + t.setCommand( "close-all-tabs", [this] { + tryCloseAllTabs( mCurWidget, UITabWidget::FocusTabBehavior::Default ); + } ); + t.setCommand( "close-clean-tabs", [this] { + tryCloseCleanTabs( mCurWidget, UITabWidget::FocusTabBehavior::Default ); + } ); + t.setCommand( "close-tabs-to-the-left", [this] { + tryCloseTabsToDirection( mCurWidget, UITabWidget::FocusTabBehavior::Default, false ); + } ); + t.setCommand( "close-tabs-to-the-right", [this] { + tryCloseTabsToDirection( mCurWidget, UITabWidget::FocusTabBehavior::Default, true ); + } ); t.setCommand( "create-new", [this] { auto d = createCodeEditorInTabWidget( tabWidgetFromWidget( mCurWidget ) ); if ( d.first != nullptr && d.second != nullptr ) { @@ -308,6 +337,10 @@ class EE_API UICodeEditorSplitter { void setThreadPool( const std::shared_ptr& threadPool ); + bool checkEditorExists( UICodeEditor* ) const; + + bool checkWidgetExists( UIWidget* ) const; + protected: UISceneNode* mUISceneNode{ nullptr }; std::shared_ptr mThreadPool; @@ -336,9 +369,9 @@ class EE_API UICodeEditorSplitter { const std::vector& colorSchemes, const std::string& initColorScheme ); - bool checkEditorExists( UICodeEditor* ) const; - virtual void onTabClosed( const TabEvent* tabEvent ); + + void closeAllTabs( std::vector tabs, UITabWidget::FocusTabBehavior focusTabBehavior ); }; }}} // namespace EE::UI::Tools diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index 8098a6ef4..380ce8119 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -63,7 +63,12 @@ std::vector UICodeEditorSplitter::getUnlockedCommands() { "switch-to-previous-split", "switch-to-next-split", "switch-to-previous-colorscheme", - "switch-to-next-colorscheme" }; + "switch-to-next-colorscheme", + "close-other-tabs", + "close-all-tabs", + "close-clean-tabs", + "close-tabs-to-the-left", + "close-tabs-to-the-right" }; return unlockedCmds; } @@ -920,7 +925,8 @@ void UICodeEditorSplitter::zoomReset() { } bool UICodeEditorSplitter::tryTabClose( UIWidget* widget, - UITabWidget::FocusTabBehavior focusTabBehavior ) { + UITabWidget::FocusTabBehavior focusTabBehavior, + std::function onMsgBoxCloseCb ) { if ( !widget ) return false; @@ -932,17 +938,24 @@ bool UICodeEditorSplitter::tryTabClose( UIWidget* widget, return false; mTryCloseMsgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, - widget->getUISceneNode()->getTranslatorString( - "@string/confirm_close_tab", - "Do you really want to close this tab?\nAll changes will be lost." ) ); - mTryCloseMsgBox->addEventListener( Event::OnConfirm, [&, editor]( const Event* ) { - closeTab( editor, focusTabBehavior ); - } ); - mTryCloseMsgBox->addEventListener( Event::OnClose, [this]( const Event* ) { - mTryCloseMsgBox = nullptr; - if ( mCurEditor ) - mCurEditor->setFocus(); - } ); + String::format( widget->getUISceneNode() + ->i18n( "@string/confirm_close_tab", + "Do you really want to close this tab?\nAll changes in " + "\"%s\" will be lost." ) + .toUtf8(), + editor->getDocument().getFilename() ) ); + mTryCloseMsgBox->addEventListener( Event::OnConfirm, + [this, editor, focusTabBehavior]( const Event* ) { + closeTab( editor, focusTabBehavior ); + } ); + mTryCloseMsgBox->addEventListener( Event::OnClose, + [this, onMsgBoxCloseCb]( const Event* ) { + mTryCloseMsgBox = nullptr; + if ( mCurEditor ) + mCurEditor->setFocus(); + if ( onMsgBoxCloseCb ) + onMsgBoxCloseCb(); + } ); mTryCloseMsgBox->setTitle( widget->getUISceneNode()->getTranslatorString( "@string/ask_close_tab", "Close Tab?" ) ); mTryCloseMsgBox->center(); @@ -958,6 +971,108 @@ bool UICodeEditorSplitter::tryTabClose( UIWidget* widget, } } +void UICodeEditorSplitter::closeAllTabs( std::vector tabs, + UITabWidget::FocusTabBehavior focusTabBehavior ) { + while ( !tabs.empty() ) { + UITab* tab = tabs.back(); + if ( tab && !tryTabClose( tab->getOwnedWidget()->asType(), focusTabBehavior, + [this, tabs, focusTabBehavior]() mutable { + tabs.pop_back(); + closeAllTabs( tabs, focusTabBehavior ); + } ) ) { + return; + } else { + tabs.pop_back(); + } + } +} + +bool UICodeEditorSplitter::tryCloseAllTabs( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior ) { + UITabWidget* tabW = tabWidgetFromWidget( widget ); + if ( !tabW ) + return false; + + size_t tabCount = tabW->getTabCount(); + std::vector tabs; + for ( size_t i = 0; i < tabCount; i++ ) + tabs.push_back( tabW->getTab( i ) ); + + closeAllTabs( tabs, focusTabBehavior ); + + return true; +} + +bool UICodeEditorSplitter::tryCloseOtherTabs( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior ) { + UITabWidget* tabW = tabWidgetFromWidget( widget ); + if ( !tabW ) + return false; + + size_t tabCount = tabW->getTabCount(); + std::vector tabs; + for ( size_t i = 0; i < tabCount; i++ ) { + if ( tabW->getTab( i )->getOwnedWidget() != widget ) + tabs.push_back( tabW->getTab( i ) ); + } + + closeAllTabs( tabs, focusTabBehavior ); + + return true; +} + +bool UICodeEditorSplitter::tryCloseCleanTabs( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior ) { + UITabWidget* tabW = tabWidgetFromWidget( widget ); + if ( !tabW ) + return false; + + size_t tabCount = tabW->getTabCount(); + std::vector tabs; + for ( size_t i = 0; i < tabCount; i++ ) { + UITab* tab = tabW->getTab( i ); + UIWidget* widget = + tab->getOwnedWidget()->isWidget() ? tab->getOwnedWidget()->asType() : nullptr; + if ( widget && widget->isType( UI_TYPE_CODEEDITOR ) && + !widget->asType()->isDirty() ) + tabs.push_back( tabW->getTab( i ) ); + } + + closeAllTabs( tabs, focusTabBehavior ); + + return true; +} + +bool UICodeEditorSplitter::tryCloseTabsToDirection( UIWidget* widget, + UITabWidget::FocusTabBehavior focusTabBehavior, + bool toTheRight ) { + UITabWidget* tabW = tabWidgetFromWidget( widget ); + if ( !tabW ) + return false; + + UITab* tab = tabW->getTabFromOwnedWidget( widget ); + if ( !tab ) + return false; + size_t tabIndex = tabW->getTabIndex( tab ); + if ( tabIndex == eeINDEX_NOT_FOUND ) + return false; + + size_t tabCount = tabW->getTabCount(); + std::vector tabs; + + if ( toTheRight ) { + for ( size_t i = tabIndex + 1; i < tabCount; i++ ) + tabs.push_back( tabW->getTab( i ) ); + } else { + for ( size_t i = 0; i < tabIndex; i++ ) + tabs.push_back( tabW->getTab( i ) ); + } + + closeAllTabs( tabs, focusTabBehavior ); + + return true; +} + void UICodeEditorSplitter::closeTab( UIWidget* widget, UITabWidget::FocusTabBehavior focusTabBehavior ) { if ( widget ) { @@ -1158,6 +1273,18 @@ void UICodeEditorSplitter::closeTabWidgets( UISplitter* splitter ) { } } +bool UICodeEditorSplitter::checkWidgetExists( UIWidget* checkWidget ) const { + bool found = false; + forEachWidgetStoppable( [&]( UIWidget* widget ) { + if ( widget == checkWidget ) { + found = true; + return true; + } + return false; + } ); + return found || checkWidget == nullptr; +} + bool UICodeEditorSplitter::checkEditorExists( UICodeEditor* checkEditor ) const { bool found = false; forEachEditorStoppable( [&found, checkEditor]( UICodeEditor* editor ) { diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 28bbd2bb3..290fba803 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -1183,6 +1183,8 @@ Drawable* App::findIcon( const std::string& name ) { } Drawable* App::findIcon( const std::string& name, const size_t iconSize ) { + if ( name.empty() ) + return nullptr; UIIcon* icon = mUISceneNode->findIcon( name ); if ( icon ) return icon->getSize( iconSize ); @@ -1659,39 +1661,72 @@ void App::onTabCreated( UITab* tab, UIWidget* ) { UITab* tab = event->getNode()->asType(); if ( !tab->getTabWidget() ) return; - menu->add( i18n( "editor_tab_menu_close_tab", "Close Tab" ) )->setId( "close-tab" ); - menu->add( i18n( "editor_tab_menu_close_other_tabs", "Close Other Tabs" ) ) - ->setId( "close-other-tabs" ); - menu->add( i18n( "editor_tab_menu_close_all_tabs", "Close All Tabs" ) ) - ->setId( "close-all-tabs" ); + const auto menuAdd = [menu, this]( const std::string& translateKey, + const String& translateString, const std::string& icon, + const std::string& cmd ) { + UIMenuItem* menuItem = menu->add( i18n( translateKey, translateString ), + findIcon( icon ), getKeybind( cmd ) ); + menuItem->setId( cmd ); + return menuItem; + }; + + menuAdd( "editor_tab_menu_close_tab", "Close Tab", "document-close", "close-tab" ); + menuAdd( "editor_tab_menu_close_other_tabs", "Close Other Tabs", "", "close-other-tabs" ); + menuAdd( "editor_tab_menu_close_clean_tabs", "Close Clean Tabs", "", "close-clean-tabs" ); + menuAdd( "editor_tab_menu_close_all_tabs", "Close All Tabs", "", "close-all-tabs" ); + menuAdd( "editor_tab_menu_close_tabs_to_the_left", "Close Tabs To The Left", "", + "close-tabs-to-the-left" ); + menuAdd( "editor_tab_menu_close_tabs_to_the_right", "Close Tabs To The Right", "", + "close-tabs-to-the-right" ); + + if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + menu->addSeparator(); + + menuAdd( "split_left", "Split Left", "split-horizontal", "split-left" ); + menuAdd( "split_right", "Split Right", "split-horizontal", "split-right" ); + menuAdd( "split_top", "Split Top", "split-vertical", "split-top" ); + menuAdd( "split_bottom", "Split Bottom", "split-vertical", "split-bottom" ); + + menu->addSeparator(); + + menuAdd( "open_containing_folder", "Open Containing Folder...", "folder-open", + "open-containing-folder" ); + + menuAdd( "copy_containing_folder_path", "Copy Containing Folder Path...", "copy", + "copy-containing-folder-path" ); + + menuAdd( "copy_file_path", "Copy File Path", "copy", "copy-file-path" ); + + menuAdd( "copy_file_path_and_position", "Copy File Path and Position", "copy", + "copy-file-path-and-position" ); + } + menu->addEventListener( Event::OnItemClicked, [tab, this]( const Event* event ) { if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) return; UIMenuItem* item = event->getNode()->asType(); - UITabWidget* tabW = tab->getTabWidget(); - if ( "close-tab" == item->getId() ) { - mSplitter->closeTab( tab->getOwnedWidget()->asType(), - UITabWidget::FocusTabBehavior::Closest ); - } else if ( "close-other-tabs" == item->getId() ) { - size_t tabCount = tabW->getTabCount(); - std::vector tabs; - for ( size_t i = 0; i < tabCount; i++ ) { - if ( tabW->getTab( i ) != tab ) - tabs.push_back( tabW->getTab( i ) ); - } - for ( auto* tab : tabs ) { - mSplitter->closeTab( tab->getOwnedWidget()->asType(), - UITabWidget::FocusTabBehavior::Closest ); - } - } else if ( "close-all-tabs" == item->getId() ) { - size_t tabCount = tabW->getTabCount(); - std::vector tabs; - for ( size_t i = 0; i < tabCount; i++ ) - tabs.push_back( tabW->getTab( i ) ); - for ( auto* tab : tabs ) { - mSplitter->closeTab( tab->getOwnedWidget()->asType(), - UITabWidget::FocusTabBehavior::Closest ); - } + UICodeEditor* nce = nullptr; + UICodeEditor* ce = nullptr; + UIWidget* ncw = nullptr; + UIWidget* cw = nullptr; + if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + nce = tab->getOwnedWidget()->asType(); + ce = mSplitter->getCurEditor(); + if ( nce != ce ) + mSplitter->setCurrentEditor( nce ); + } else { + ncw = tab->getOwnedWidget()->asType(); + cw = mSplitter->getCurWidget(); + if ( ncw != cw ) + mSplitter->setCurrentWidget( ncw ); + } + runCommand( item->getId() ); + if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + if ( nce != ce && mSplitter->checkEditorExists( ce ) ) + mSplitter->setCurrentEditor( ce ); + } else { + if ( ncw != cw && mSplitter->checkWidgetExists( cw ) ) + mSplitter->setCurrentWidget( cw ); } } ); } );