mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-18 06:55:48 +03:00
446 lines
15 KiB
C++
446 lines
15 KiB
C++
#ifndef EE_UI_TOOLS_UICODEEDITORSPLITTER_HPP
|
|
#define EE_UI_TOOLS_UICODEEDITORSPLITTER_HPP
|
|
|
|
#include <eepp/ui/splitdirection.hpp>
|
|
#include <eepp/ui/uicodeeditor.hpp>
|
|
#include <eepp/ui/uimessagebox.hpp>
|
|
#include <eepp/ui/uiscenenode.hpp>
|
|
#include <eepp/ui/uisplitter.hpp>
|
|
#include <eepp/ui/uitabwidget.hpp>
|
|
|
|
#include <eepp/system/log.hpp>
|
|
|
|
using namespace EE::UI::Doc;
|
|
|
|
namespace EE { namespace UI { namespace Tools {
|
|
|
|
class EE_API UICodeEditorSplitter {
|
|
public:
|
|
static const std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings();
|
|
|
|
static const std::map<KeyBindings::Shortcut, std::string> getLocalDefaultKeybindings();
|
|
|
|
class EE_API Client {
|
|
public:
|
|
virtual ~Client() {};
|
|
|
|
virtual void onTabCreated( UITab* tab, UIWidget* widget ) = 0;
|
|
|
|
virtual void onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) = 0;
|
|
|
|
virtual void onCodeEditorFocusChange( UICodeEditor* editor ) = 0;
|
|
|
|
virtual void onWidgetFocusChange( UIWidget* widget ) = 0;
|
|
|
|
virtual void onDocumentStateChanged( UICodeEditor* editor, TextDocument& doc ) = 0;
|
|
|
|
virtual void onDocumentModified( UICodeEditor* editor, TextDocument& doc ) = 0;
|
|
|
|
virtual void onDocumentSelectionChange( UICodeEditor* editor, TextDocument& doc ) = 0;
|
|
|
|
virtual void onDocumentCursorPosChange( UICodeEditor* editor, TextDocument& doc ) = 0;
|
|
|
|
virtual void onDocumentUndoRedo( UICodeEditor* editor, TextDocument& doc ) = 0;
|
|
|
|
virtual void onColorSchemeChanged( const std::string& currentColorScheme ) = 0;
|
|
|
|
virtual void onDocumentLoaded( UICodeEditor* codeEditor, const std::string& path ) = 0;
|
|
};
|
|
|
|
static std::vector<std::string> getUnlockedCommands();
|
|
|
|
static UICodeEditorSplitter* New( UICodeEditorSplitter::Client* client, UISceneNode* sceneNode,
|
|
std::shared_ptr<ThreadPool> = nullptr,
|
|
const std::vector<SyntaxColorScheme>& colorSchemes = {},
|
|
const std::string& initColorScheme = "" );
|
|
|
|
virtual ~UICodeEditorSplitter();
|
|
|
|
virtual bool tryTabClose( UIWidget* widget, UITabWidget::FocusTabBehavior focusTabBehavior,
|
|
std::function<void()> onMsgBoxCloseCb = {} );
|
|
|
|
virtual bool tryCodeEditorClose( UICodeEditor* editor,
|
|
UITabWidget::FocusTabBehavior focusTabBehavior,
|
|
std::function<void()> 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 );
|
|
|
|
bool curEditorExistsAndFocused() const;
|
|
|
|
UISplitter* split( const SplitDirection& direction, UIWidget* editor,
|
|
bool openCurEditor = true );
|
|
|
|
void switchToTab( Int32 index );
|
|
|
|
UITabWidget* findPreviousSplit( UIWidget* widget );
|
|
|
|
void switchPreviousSplit( UIWidget* widget );
|
|
|
|
UITabWidget* findNextSplit( UIWidget* widget );
|
|
|
|
void switchNextSplit( UIWidget* widget );
|
|
|
|
void setCurrentEditor( UICodeEditor* editor );
|
|
|
|
UITabWidget* tabWidgetFromEditor( UICodeEditor* editor ) const;
|
|
|
|
UITab* tabFromEditor( UICodeEditor* editor ) const;
|
|
|
|
UITabWidget* tabWidgetFromWidget( UIWidget* widget ) const;
|
|
|
|
UISplitter* splitterFromEditor( UICodeEditor* editor ) const;
|
|
|
|
UISplitter* splitterFromWidget( UIWidget* widget ) const;
|
|
|
|
std::pair<UITab*, UICodeEditor*> createCodeEditorInTabWidget( UITabWidget* tabWidget );
|
|
|
|
std::pair<UITab*, UIWidget*> createWidgetInTabWidget( UITabWidget* tabWidget, UIWidget* widget,
|
|
const std::string& tabName,
|
|
bool focus = true );
|
|
|
|
std::pair<UITab*, UIWidget*> createWidget( UIWidget* widget, const std::string& tabName,
|
|
bool focus = true );
|
|
|
|
std::vector<std::pair<UITab*, UITabWidget*>> getTabFromOwnedWidgetId( const std::string& id );
|
|
|
|
bool ownedWidgetExists( UIWidget* widget );
|
|
|
|
bool removeTabWithOwnedWidgetId( const std::string& id, bool destroyOwnedNode = true,
|
|
bool immediateClose = false );
|
|
|
|
UICodeEditor* createCodeEditor();
|
|
|
|
void focusSomeEditor( Node* searchFrom = nullptr );
|
|
|
|
bool loadDocument( std::shared_ptr<TextDocument> doc, UICodeEditor* codeEditor = nullptr );
|
|
|
|
std::pair<UITab*, UICodeEditor*> createEditorInNewTab();
|
|
|
|
std::pair<UITab*, UICodeEditor*> loadDocumentInNewTab( std::shared_ptr<TextDocument> doc );
|
|
|
|
bool loadFileFromPath( const std::string& path, UICodeEditor* codeEditor = nullptr );
|
|
|
|
void loadAsyncFileFromPath( const std::string& path, UICodeEditor* codeEditor = nullptr,
|
|
std::function<void( UICodeEditor*, const std::string& )> onLoaded =
|
|
std::function<void( UICodeEditor*, const std::string& )>() );
|
|
|
|
std::pair<UITab*, UICodeEditor*> loadFileFromPathInNewTab( const std::string& path );
|
|
|
|
void loadAsyncFileFromPathInNewTab(
|
|
const std::string& path, std::function<void( UICodeEditor*, const std::string& )> onLoaded =
|
|
std::function<void( UICodeEditor*, const std::string& )>() );
|
|
|
|
void loadAsyncFileFromPathInNewTab(
|
|
const std::string& path, std::function<void( UICodeEditor*, const std::string& )> onLoaded,
|
|
UITabWidget* tabWidget );
|
|
|
|
void removeUnusedTab( UITabWidget* tabWidget, bool destroyOwnedNode = true,
|
|
bool immediateClose = true );
|
|
|
|
UITabWidget* createEditorWithTabWidget( Node* parent, bool openCurEditor = true );
|
|
|
|
UITab* isDocumentOpen( const std::string& path, bool checkOnlyInCurrentTabWidget = false,
|
|
bool checkOpeningDocuments = false ) const;
|
|
|
|
UITab* isDocumentOpen( const URI& uri, bool checkOnlyInCurrentTabWidget = false,
|
|
bool checkOpeningDocuments = false ) const;
|
|
|
|
UICodeEditor* editorFromTab( UITab* tab ) const;
|
|
|
|
UICodeEditor* findEditorFromPath( const std::string& path );
|
|
|
|
void applyColorScheme( const SyntaxColorScheme& colorScheme );
|
|
|
|
void forEachWidgetClass( const std::string& className,
|
|
std::function<void( UIWidget* )> run ) const;
|
|
|
|
void forEachWidgetClassStoppable( const std::string& className,
|
|
std::function<bool( UIWidget* )> run ) const;
|
|
|
|
void forEachWidgetType( const UINodeType& nodeType,
|
|
std::function<void( UIWidget* )> run ) const;
|
|
|
|
void forEachWidgetTypeStoppable( const UINodeType& nodeType,
|
|
std::function<bool( UIWidget* )> run ) const;
|
|
|
|
void forEachWidgetStoppable( std::function<bool( UIWidget* )> run ) const;
|
|
|
|
void forEachWidget( std::function<void( UIWidget* )> run ) const;
|
|
|
|
void forEachEditor( std::function<void( UICodeEditor* )> run ) const;
|
|
|
|
void forEachDoc( std::function<void( TextDocument& doc )> run ) const;
|
|
|
|
void forEachDocSharedPtr( std::function<void( std::shared_ptr<TextDocument> doc )> run ) const;
|
|
|
|
void forEachTabWidget( std::function<void( UITabWidget* )> run ) const;
|
|
|
|
void forEachTabWidgetStoppable( std::function<bool( UITabWidget* )> run ) const;
|
|
|
|
void forEachTab( std::function<void( UITab* )> run ) const;
|
|
|
|
void zoomIn();
|
|
|
|
void zoomOut();
|
|
|
|
void zoomReset();
|
|
|
|
void closeSplitter( UISplitter* splitter );
|
|
|
|
void addRemainingTabWidgets( Node* widget );
|
|
|
|
void closeTabWidgets( UISplitter* splitter );
|
|
|
|
UICodeEditor* getCurEditor() const;
|
|
|
|
bool curEditorIsNotNull() const;
|
|
|
|
const SyntaxColorScheme& getCurrentColorScheme() const;
|
|
|
|
const std::string& getCurrentColorSchemeName() const;
|
|
|
|
void setColorScheme( const std::string& name );
|
|
|
|
const std::map<std::string, SyntaxColorScheme>& getColorSchemes() const;
|
|
|
|
bool editorExists( UICodeEditor* editor );
|
|
|
|
bool isAnyEditorDirty();
|
|
|
|
bool allEditorsEmpty();
|
|
|
|
void forEachEditorStoppable( std::function<bool( UICodeEditor* )> run ) const;
|
|
|
|
std::vector<UICodeEditor*> getAllEditors();
|
|
|
|
void forEachDocStoppable( std::function<bool( TextDocument& )> run ) const;
|
|
|
|
std::shared_ptr<TextDocument> findDocFromPath( const std::string& path );
|
|
|
|
std::shared_ptr<TextDocument> findDocFromURI( const URI& uri );
|
|
|
|
bool getHideTabBarOnSingleTab() const;
|
|
|
|
void setHideTabBarOnSingleTab( bool hideTabBarOnSingleTab );
|
|
|
|
void setHideTabBar( bool hideTabBar );
|
|
|
|
const std::vector<UITabWidget*>& getTabWidgets() const;
|
|
|
|
Node* getBaseLayout() const;
|
|
|
|
UIWidget* getCurWidget() const;
|
|
|
|
void setCurrentWidget( UIWidget* curWidget );
|
|
|
|
bool curWidgetExists() const;
|
|
|
|
bool isCurEditor( UICodeEditor* editor );
|
|
|
|
UICodeEditor* getSomeEditor();
|
|
|
|
size_t countEditorsOpeningDoc( const TextDocument& doc ) const;
|
|
|
|
// T must implement setCommand( const std::string& command, const std::function<void()>& func )
|
|
template <typename T> void registerSplitterCommands( T& t ) {
|
|
t.setCommand( "switch-to-previous-split", [this] { switchPreviousSplit( mCurWidget ); } );
|
|
t.setCommand( "switch-to-next-split", [this] { switchNextSplit( mCurWidget ); } );
|
|
t.setCommand( "close-tab", [this] {
|
|
if ( tryTabClose( mCurWidget, UITabWidget::FocusTabBehavior::Default ) )
|
|
closeTab( 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( getPreferredTabWidget() );
|
|
if ( d.first != nullptr && d.second != nullptr ) {
|
|
d.first->getTabWidget()->setTabSelected( d.first );
|
|
} else if ( !mTabWidgets.empty() ) {
|
|
d = createCodeEditorInTabWidget( mTabWidgets[0] );
|
|
}
|
|
if ( d.first == nullptr || d.second == nullptr )
|
|
Log::error( "Couldn't createCodeEditorInTabWidget in create-new command" );
|
|
} );
|
|
t.setCommand( "next-tab", [this] {
|
|
UITabWidget* tabWidget = tabWidgetFromWidget( mCurWidget );
|
|
if ( tabWidget )
|
|
tabWidget->focusNextTab();
|
|
} );
|
|
t.setCommand( "previous-tab", [this] {
|
|
UITabWidget* tabWidget = tabWidgetFromWidget( mCurWidget );
|
|
if ( tabWidget )
|
|
tabWidget->focusPreviousTab();
|
|
} );
|
|
for ( int i = 1; i <= 10; i++ )
|
|
t.setCommand( String::format( "switch-to-tab-%d", i ),
|
|
[this, i] { switchToTab( i - 1 ); } );
|
|
t.setCommand( "switch-to-first-tab", [this] {
|
|
UITabWidget* tabWidget = tabWidgetFromWidget( mCurWidget );
|
|
if ( tabWidget && tabWidget->getTabCount() ) {
|
|
switchToTab( 0 );
|
|
}
|
|
} );
|
|
t.setCommand( "switch-to-last-tab", [this] {
|
|
UITabWidget* tabWidget = tabWidgetFromWidget( mCurWidget );
|
|
if ( tabWidget && tabWidget->getTabCount() ) {
|
|
switchToTab( tabWidget->getTabCount() - 1 );
|
|
}
|
|
} );
|
|
t.setCommand( "split-right", [this] {
|
|
split( SplitDirection::Right, mCurWidget, curEditorExistsAndFocused() );
|
|
} );
|
|
t.setCommand( "split-bottom", [this] {
|
|
split( SplitDirection::Bottom, mCurWidget, curEditorExistsAndFocused() );
|
|
} );
|
|
t.setCommand( "split-left", [this] {
|
|
split( SplitDirection::Left, mCurWidget, curEditorExistsAndFocused() );
|
|
} );
|
|
t.setCommand( "split-top", [this] {
|
|
split( SplitDirection::Top, mCurWidget, curEditorExistsAndFocused() );
|
|
} );
|
|
t.setCommand( "split-swap", [this] {
|
|
if ( UISplitter* splitter = splitterFromWidget( mCurWidget ) )
|
|
splitter->swap();
|
|
} );
|
|
}
|
|
|
|
UISceneNode* getUISceneNode() const;
|
|
|
|
bool curEditorExists() const;
|
|
|
|
bool hasSplit() const;
|
|
|
|
UIOrientation getMainSplitOrientation() const;
|
|
|
|
void addCurrentPositionToNavigationHistory();
|
|
|
|
void addEditorPositionToNavigationHistory( UICodeEditor* editor );
|
|
|
|
void updateCurrentPositionInNavigationHistory();
|
|
|
|
void goBackInNavigationHistory();
|
|
|
|
void goForwardInNavigationHistory();
|
|
|
|
void clearNavigationHistory();
|
|
|
|
std::shared_ptr<ThreadPool> getThreadPool() const;
|
|
|
|
void setThreadPool( const std::shared_ptr<ThreadPool>& threadPool );
|
|
|
|
bool checkEditorExists( UICodeEditor* ) const;
|
|
|
|
bool checkWidgetExists( UIWidget* ) const;
|
|
|
|
void setOnTabWidgetCreateCb( std::function<void( UITabWidget* )> cb );
|
|
|
|
bool getVisualSplitting() const;
|
|
|
|
void setVisualSplitting( bool visualSplitting );
|
|
|
|
Float getVisualSplitEdgePercent() const;
|
|
|
|
void setVisualSplitEdgePercent( Float visualSplitEdgePercent );
|
|
|
|
UITabWidget* splitTabWidget( SplitDirection, UITabWidget* );
|
|
|
|
std::shared_ptr<TextDocument> getTextDocumentRef( TextDocument* doc );
|
|
|
|
// @return True if can be removed
|
|
typedef std::function<bool( UIWidget*, UITabWidget::FocusTabBehavior,
|
|
std::function<void()> onMsgBoxCloseCb )>
|
|
TabTryCloseCallback;
|
|
|
|
void setTabTryCloseCallback( TabTryCloseCallback cb );
|
|
|
|
bool isWidgetInAnyWidget( UIWidget* ) const;
|
|
|
|
void setOpenDocumentsInMainSplit( bool open ) { mOpenDocumentsInMainSplit = open; }
|
|
|
|
bool openDocumentsInMainSplit() const { return mOpenDocumentsInMainSplit; }
|
|
|
|
UITabWidget* getFirstTabWidget() const;
|
|
|
|
UITabWidget* getPreferredTabWidget() const;
|
|
|
|
UITabWidget* getCurTabWidget() const;
|
|
|
|
UITab* getTabFromWidget( UIWidget* ) const;
|
|
|
|
void
|
|
setCanCreateSplitFn( std::function<bool( SplitDirection direction, UIWidget* widget )> fn );
|
|
|
|
protected:
|
|
UISceneNode* mUISceneNode{ nullptr };
|
|
std::shared_ptr<ThreadPool> mThreadPool;
|
|
UICodeEditor* mCurEditor{ nullptr };
|
|
UIWidget* mCurWidget{ nullptr };
|
|
std::map<std::string, SyntaxColorScheme> mColorSchemes;
|
|
std::string mCurrentColorScheme;
|
|
std::vector<UITabWidget*> mTabWidgets;
|
|
Node* mBaseLayout{ nullptr };
|
|
Client* mClient;
|
|
bool mHideTabBar{ false };
|
|
bool mHideTabBarOnSingleTab{ true };
|
|
bool mFirstCodeEditor{ true };
|
|
bool mVisualSplitting{ true };
|
|
bool mOpenDocumentsInMainSplit{ false };
|
|
UICodeEditor* mAboutToAddEditor{ nullptr };
|
|
UIMessageBox* mTryCloseMsgBox{ nullptr };
|
|
Mutex mTabWidgetMutex;
|
|
struct NavigationRecord {
|
|
std::string path;
|
|
TextPosition pos;
|
|
};
|
|
size_t mNavigationHistoryMaxSize{ 100 };
|
|
std::vector<NavigationRecord> mNavigationHistory;
|
|
size_t mNavigationHistoryPos{ std::numeric_limits<size_t>::max() };
|
|
std::function<void( UITabWidget* )> mOnTabWidgetCreateCb;
|
|
Float mVisualSplitEdgePercent{ 0.1 };
|
|
TabTryCloseCallback mTabTryCloseCb;
|
|
std::function<bool( SplitDirection direction, UIWidget* widget )> mCanCreateSplitFn;
|
|
|
|
UICodeEditorSplitter( UICodeEditorSplitter::Client* client, UISceneNode* sceneNode,
|
|
std::shared_ptr<ThreadPool> threadPool,
|
|
const std::vector<SyntaxColorScheme>& colorSchemes,
|
|
const std::string& initColorScheme );
|
|
|
|
virtual void onTabClosed( const TabEvent* tabEvent );
|
|
|
|
void closeAllTabs( std::vector<UITab*> tabs, UITabWidget::FocusTabBehavior focusTabBehavior );
|
|
|
|
UITabWidget* createTabWidget( Node* parent );
|
|
|
|
void updateTabWidgetVisualSplitting();
|
|
};
|
|
|
|
}}} // namespace EE::UI::Tools
|
|
|
|
#endif // EE_UI_TOOLS_UICODEEDITORSPLITTER_HPP
|