diff --git a/include/eepp/ui.hpp b/include/eepp/ui.hpp index b07e494af..83ae4830b 100644 --- a/include/eepp/ui.hpp +++ b/include/eepp/ui.hpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -71,4 +72,8 @@ #include #include +#include +#include +#include + #endif diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 819890220..aa8681dde 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -20,7 +20,7 @@ class EE_API UIAbstractTableView : public UIAbstractView { bool isType( const Uint32& type ) const; - virtual Float getRowHeight() const { return getHeaderHeight(); } + virtual Float getRowHeight() const; virtual Float getHeaderHeight() const; @@ -34,6 +34,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { void setColumnHidden( const size_t& column, bool hidden ); + void setColumnsHidden( const std::vector columns, bool hidden ); + virtual void selectAll(); const Float& getDragBorderDistance() const; @@ -42,6 +44,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { Vector2f getColumnPosition( const size_t& index ); + int visibleColumnCount() const; + protected: friend class EE::UI::UITableHeaderColumn; @@ -71,8 +75,14 @@ class EE_API UIAbstractTableView : public UIAbstractView { virtual void onColumnResizeToContent( const size_t& colIndex ); + virtual void updateColumnsWidth(); + + virtual void updateScroll(); + void updateHeaderSize(); + int visibleColumn(); + UILinearLayout* mHeader; Float mDragBorderDistance{8}; }; diff --git a/include/eepp/ui/abstract/uiabstractview.hpp b/include/eepp/ui/abstract/uiabstractview.hpp index f65d1bcb4..4d98b9fa1 100644 --- a/include/eepp/ui/abstract/uiabstractview.hpp +++ b/include/eepp/ui/abstract/uiabstractview.hpp @@ -11,18 +11,27 @@ using namespace EE::UI::Models; namespace EE { namespace UI { namespace Abstract { +enum class ModelEventType { Open }; + class EE_API ModelEvent : public Event { public: - ModelEvent( Model* model, const ModelIndex& index, Node* node ) : - Event( node, Event::OnModelEvent ), model( model ), index( index ) {} + ModelEvent( Model* model, const ModelIndex& index, Node* node, + const ModelEventType& modelEventType = ModelEventType::Open ) : + Event( node, Event::OnModelEvent ), + model( model ), + index( index ), + modelEventType( modelEventType ) {} const Model* getModel() const { return model; } const ModelIndex& getModelIndex() const { return index; } + const ModelEventType& getModelEventType() const { return modelEventType; } + protected: const Model* model; ModelIndex index; + ModelEventType modelEventType; }; class EE_API UIAbstractView : public UIScrollableWidget { diff --git a/include/eepp/ui/models/filesystemmodel.hpp b/include/eepp/ui/models/filesystemmodel.hpp index f3f7de06c..9ccb38993 100644 --- a/include/eepp/ui/models/filesystemmodel.hpp +++ b/include/eepp/ui/models/filesystemmodel.hpp @@ -45,6 +45,7 @@ class EE_API FileSystemModel : public Model { FileInfo mInfo; std::vector mChildren; bool mHasTraversed{false}; + bool mInfoDirty{true}; bool mSelected{false}; ModelIndex index( const FileSystemModel& model, int column ) const; void traverseIfNeeded( const FileSystemModel& ); diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index 289e7e390..a5539c864 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -81,6 +81,8 @@ class EE_API UICodeEditorSplitter { UITabWidget* createEditorWithTabWidget( Node* parent ); + UITab* isDocumentOpen( const std::string& path ) const; + void applyColorScheme( const SyntaxColorScheme& colorScheme ); void forEachEditor( std::function run ); diff --git a/src/eepp/system/fileinfo.cpp b/src/eepp/system/fileinfo.cpp index 89235f23c..3c5969716 100644 --- a/src/eepp/system/fileinfo.cpp +++ b/src/eepp/system/fileinfo.cpp @@ -116,11 +116,13 @@ void FileInfo::getInfo() { mInode = st.st_ino; } - FileSystem::dirAddSlashAtEnd( mFilepath ); + if ( isDirectory() ) + FileSystem::dirAddSlashAtEnd( mFilepath ); } void FileInfo::getRealInfo() { - FileSystem::dirRemoveSlashAtEnd( mFilepath ); + if ( isDirectory() ) + FileSystem::dirRemoveSlashAtEnd( mFilepath ); #if EE_PLATFORM != EE_PLATFORM_WIN struct stat st; @@ -139,7 +141,8 @@ void FileInfo::getRealInfo() { mInode = st.st_ino; } - FileSystem::dirAddSlashAtEnd( mFilepath ); + if ( isDirectory() ) + FileSystem::dirAddSlashAtEnd( mFilepath ); } const std::string& FileInfo::getFilepath() const { @@ -219,7 +222,8 @@ std::string FileInfo::linksTo() const { } bool FileInfo::exists() const { - FileSystem::dirRemoveSlashAtEnd( mFilepath ); + if ( isDirectory() ) + FileSystem::dirRemoveSlashAtEnd( mFilepath ); #if EE_PLATFORM != EE_PLATFORM_WIN struct stat st; @@ -229,7 +233,8 @@ bool FileInfo::exists() const { int res = _wstat( String::fromUtf8( mFilepath ).toWideString().c_str(), &st ); #endif - FileSystem::dirAddSlashAtEnd( mFilepath ); + if ( isDirectory() ) + FileSystem::dirAddSlashAtEnd( mFilepath ); return 0 == res; } diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index bf31207d9..8e0fd71aa 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace EE { namespace UI { namespace Abstract { @@ -24,6 +25,11 @@ bool UIAbstractTableView::isType( const Uint32& type ) const { return UIAbstractTableView::getType() == type ? true : UIAbstractView::isType( type ); } +Float UIAbstractTableView::getRowHeight() const { + return eeceil( columnData( 0 ).widget ? columnData( 0 ).widget->getPixelsSize().getHeight() + : 16 ); +} + void UIAbstractTableView::selectAll() { getSelection().clear(); for ( size_t itemIndex = 0; itemIndex < getItemCount(); ++itemIndex ) { @@ -86,6 +92,8 @@ void UIAbstractTableView::createOrUpdateColumns() { mHeader->setPixelsSize( totalWidth, getHeaderHeight() ); mHeader->updateLayout(); + + updateColumnsWidth(); } Float UIAbstractTableView::getHeaderHeight() const { @@ -133,6 +141,32 @@ void UIAbstractTableView::updateHeaderSize() { mHeader->setPixelsSize( totalWidth, getHeaderHeight() ); } +int UIAbstractTableView::visibleColumn() { + for ( size_t i = 0; i < getModel()->columnCount(); i++ ) { + if ( columnData( i ).visible ) + return i; + } + return -1; +} + +void UIAbstractTableView::updateColumnsWidth() { + int col = 0; + Float width = + eefloor( getPixelsSize().getWidth() - getPixelsPadding().Left - getPixelsPadding().Right - + ( mVScroll->isVisible() ? mVScroll->getPixelsSize().getWidth() : 0 ) ); + + if ( visibleColumnCount() == 1 && ( col = visibleColumn() ) != -1 ) { + columnData( col ).width = width; + updateHeaderSize(); + onColumnSizeChange( col ); + } +} + +void UIAbstractTableView::updateScroll() { + UIAbstractView::updateScroll(); + updateColumnsWidth(); +} + const Float& UIAbstractTableView::getDragBorderDistance() const { return mDragBorderDistance; } @@ -145,6 +179,17 @@ Vector2f UIAbstractTableView::getColumnPosition( const size_t& index ) { return columnData( index ).widget->getPixelsPosition(); } +int UIAbstractTableView::visibleColumnCount() const { + if ( !getModel() ) + return 0; + int count = 0; + for ( size_t i = 0; i < getModel()->columnCount(); i++ ) { + if ( columnData( i ).visible ) + count++; + } + return count; +} + UIAbstractTableView::ColumnData& UIAbstractTableView::columnData( const size_t& column ) const { if ( column >= mColumn.size() ) mColumn.resize( column + 1 ); @@ -162,4 +207,10 @@ void UIAbstractTableView::setColumnHidden( const size_t& column, bool hidden ) { } } +void UIAbstractTableView::setColumnsHidden( const std::vector columns, bool hidden ) { + for ( auto col : columns ) + columnData( col ).visible = !hidden; + createOrUpdateColumns(); +} + }}} // namespace EE::UI::Abstract diff --git a/src/eepp/ui/models/filesystemmodel.cpp b/src/eepp/ui/models/filesystemmodel.cpp index e567389d5..7e703fa29 100644 --- a/src/eepp/ui/models/filesystemmodel.cpp +++ b/src/eepp/ui/models/filesystemmodel.cpp @@ -13,6 +13,7 @@ namespace EE { namespace UI { namespace Models { FileSystemModel::Node::Node( const std::string& rootPath, const FileSystemModel& model ) : mInfo( FileSystem::getRealPath( rootPath ) ) { + mInfoDirty = false; mName = FileSystem::fileNameFromPath( mInfo.getFilepath() ); mMimeType = ""; traverseIfNeeded( model ); @@ -20,6 +21,7 @@ FileSystemModel::Node::Node( const std::string& rootPath, const FileSystemModel& FileSystemModel::Node::Node( FileInfo&& info, FileSystemModel::Node* parent ) : mParent( parent ), mInfo( info ) { + mInfoDirty = false; mName = FileSystem::fileNameFromPath( mInfo.getFilepath() ); if ( !mInfo.isDirectory() ) { mMimeType = "filetype-" + FileSystem::fileExtension( mName ); @@ -59,11 +61,15 @@ void FileSystemModel::Node::traverseIfNeeded( const FileSystemModel& model ) { void FileSystemModel::Node::reifyIfNeeded( const FileSystemModel& model ) { traverseIfNeeded( model ); - fetchData( fullPath() ); + if ( mInfoDirty ) + fetchData( fullPath() ); } bool FileSystemModel::Node::fetchData( const String& fullPath ) { - mInfo = FileInfo( fullPath, mParent == nullptr ); + if ( mInfoDirty ) { + mInfo = FileInfo( fullPath, mParent == nullptr ); + mInfoDirty = false; + } return true; } diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index d91ceb344..a11267367 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -370,6 +370,20 @@ UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent ) { return tabWidget; } +UITab* UICodeEditorSplitter::isDocumentOpen( const std::string& path ) const { + for ( auto tabWidget : mTabWidgets ) { + for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { + UITab* tab = tabWidget->getTab( i ); + UICodeEditor* editor = tab->getOwnedWidget()->asType(); + + if ( editor->getDocument().getFilePath() == path ) { + return tab; + } + } + } + return nullptr; +} + void UICodeEditorSplitter::applyColorScheme( const SyntaxColorScheme& colorScheme ) { forEachEditor( [colorScheme]( UICodeEditor* editor ) { editor->setColorScheme( colorScheme ); } ); diff --git a/src/eepp/ui/uiscrollablewidget.cpp b/src/eepp/ui/uiscrollablewidget.cpp index 0a16aef38..9c1e57f94 100644 --- a/src/eepp/ui/uiscrollablewidget.cpp +++ b/src/eepp/ui/uiscrollablewidget.cpp @@ -101,9 +101,10 @@ void UIScrollableWidget::onContentSizeChange() { mHScroll->setVisible( false ); mHScroll->setEnabled( false ); } else { - bool visible = contentSize.getWidth() > - getPixelsSize().getWidth() - getPixelsPadding().Left - - getPixelsPadding().Right - mVScroll->getPixelsSize().getWidth(); + Float totW = getPixelsSize().getWidth() - getPixelsPadding().Left - + getPixelsPadding().Right - mVScroll->getPixelsSize().getWidth(); + + bool visible = contentSize.getWidth() > totW; mHScroll->setVisible( visible ); mHScroll->setEnabled( visible ); @@ -116,14 +117,26 @@ void UIScrollableWidget::onContentSizeChange() { mVScroll->setVisible( false ); mVScroll->setEnabled( false ); } else { - bool visible = contentSize.getHeight() > - getPixelsSize().getHeight() - getPixelsPadding().Top - - getPixelsPadding().Bottom - mHScroll->getPixelsSize().getHeight(); + Float totH = getPixelsSize().getHeight() - getPixelsPadding().Top - + getPixelsPadding().Bottom - mHScroll->getPixelsSize().getHeight(); + + bool visible = contentSize.getHeight() > totH; mVScroll->setVisible( visible ); mVScroll->setEnabled( visible ); } + if ( ScrollBarMode::Auto == mHScrollMode ) { + Float totW = getPixelsSize().getWidth() - getPixelsPadding().Left - + getPixelsPadding().Right - + ( mVScroll->isVisible() ? mVScroll->getPixelsSize().getWidth() : 0 ); + + bool visible = contentSize.getWidth() > totW; + + mHScroll->setVisible( visible ); + mHScroll->setEnabled( visible ); + } + Sizef size = getPixelsSize() - mRealPadding; if ( Exclusive == mViewType ) { diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 938b28885..8a83185d9 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -35,7 +35,8 @@ UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& in } template void UITreeView::traverseTree( Callback callback ) const { - eeASSERT( getModel() ); + if ( !getModel() ) + return; auto& model = *getModel(); int indentLevel = 1; Float yOffset = getHeaderHeight(); @@ -267,8 +268,10 @@ void UITreeView::drawChilds() { return IterationDecision::Stop; if ( yOffset - mScrollOffset.y + getRowHeight() < 0 ) return IterationDecision::Continue; - for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) - updateCell( rowIndex, index, colIndex, indentLevel, yOffset ); + for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) { + if ( columnData( colIndex ).visible ) + updateCell( rowIndex, index, colIndex, indentLevel, yOffset ); + } updateRow( rowIndex, index, yOffset )->nodeDraw(); return IterationDecision::Continue; } ); diff --git a/src/tools/codeeditor/autocompletemodule.cpp b/src/tools/codeeditor/autocompletemodule.cpp index 23fb289d0..a73151a00 100644 --- a/src/tools/codeeditor/autocompletemodule.cpp +++ b/src/tools/codeeditor/autocompletemodule.cpp @@ -403,7 +403,7 @@ void AutoCompleteModule::resetSuggestions( UICodeEditor* editor ) { mSuggestionIndex = 0; mSuggestionsEditor = nullptr; mSuggestions.clear(); - if ( editor ) + if ( editor && editor->hasFocus() ) editor->getUISceneNode()->setCursor( !editor->isLocked() ? Cursor::IBeam : Cursor::Arrow ); } diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 164777c51..157fd932a 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -1435,51 +1435,49 @@ void App::init( const std::string& file, const Float& pidelDensity ) { } - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; - UIWidgetCreator::registerWidget( "searchbar", [] { return UISearchBar::New(); } ); - mUISceneNode->loadLayoutFromString( baseUI ); - mUISceneNode->bind( "code_container", mBaseLayout ); - mUISceneNode->bind( "search_bar", mSearchBarLayout ); - mUISceneNode->bind( "doc_info", mDocInfo ); - mUISceneNode->bind( "doc_info_text", mDocInfoText ); - mDocInfo->setVisible( mConfig.editor.showDocInfo ); - mSearchBarLayout->setVisible( false )->setEnabled( false ); UIIconTheme* iconTheme = UIIconTheme::New( "remixicon" ); Float menuIconSize = mConfig.ui.fontSize.asPixels( 0, Sizef(), mDisplayDPI ); Float buttonIconSize = @@ -1504,6 +1502,7 @@ void App::init( const std::string& file, const Float& pidelDensity ) { addIcon( "split-vertical", 0xf17b, menuIconSize ); addIcon( "find-replace", 0xed2b, menuIconSize ); addIcon( "folder", 0xed54, menuIconSize ); + addIcon( "folder-open", 0xed70, menuIconSize ); addIcon( "folder-add", 0xed5a, menuIconSize ); addIcon( "file", 0xecc3, menuIconSize ); addIcon( "file-code", 0xecd1, menuIconSize ); @@ -1519,9 +1518,19 @@ void App::init( const std::string& file, const Float& pidelDensity ) { addIcon( "cancel", 0xeb98, buttonIconSize ); addIcon( "color-picker", 0xf13d, buttonIconSize ); addIcon( "pixel-density", 0xed8c, buttonIconSize ); - + addIcon( "tree-expanded", 0xed70, menuIconSize ); + addIcon( "tree-contracted", 0xed54, menuIconSize ); mUISceneNode->getUIIconThemeManager()->setCurrentTheme( iconTheme ); + UIWidgetCreator::registerWidget( "searchbar", [] { return UISearchBar::New(); } ); + mUISceneNode->loadLayoutFromString( baseUI ); + mUISceneNode->bind( "code_container", mBaseLayout ); + mUISceneNode->bind( "search_bar", mSearchBarLayout ); + mUISceneNode->bind( "doc_info", mDocInfo ); + mUISceneNode->bind( "doc_info_text", mDocInfoText ); + mDocInfo->setVisible( mConfig.editor.showDocInfo ); + mSearchBarLayout->setVisible( false )->setEnabled( false ); + mEditorSplitter = UICodeEditorSplitter::New( this, mUISceneNode, SyntaxColorScheme::loadFromFile( mResPath + "assets/colorschemes/colorschemes.conf" ), @@ -1532,11 +1541,47 @@ void App::init( const std::string& file, const Float& pidelDensity ) { createSettingsMenu(); mEditorSplitter->createEditorWithTabWidget( mBaseLayout ); - if ( !file.empty() && FileSystem::fileExists( file ) ) - mEditorSplitter->loadFileFromPath( FileSystem::getRealPath( file ) ); mConsole = eeNew( Console, ( fontMono, true, true, 1024 * 1000, 0, mWindow ) ); + UISplitter* projectSplitter = mUISceneNode->find( "project_splitter" ); + projectSplitter->setDivisionSplit( 0.15f ); + UITreeView* tree = mUISceneNode->find( "project_view" ); + tree->setColumnsHidden( {FileSystemModel::Icon, FileSystemModel::Size, + FileSystemModel::Group, FileSystemModel::Inode, + FileSystemModel::Owner, FileSystemModel::SymlinkTarget, + FileSystemModel::Permissions, FileSystemModel::ModificationTime}, + true ); + tree->setHeadersVisible( false ); + tree->addEventListener( Event::OnModelEvent, [&]( const Event* event ) { + const ModelEvent* modelEvent = static_cast( event ); + if ( modelEvent->getModelEventType() == ModelEventType::Open ) { + Variant vPath( modelEvent->getModel()->data( modelEvent->getModelIndex(), + Model::Role::Custom ) ); + if ( vPath.isValid() && vPath.is( Variant::Type::String ) ) { + std::string path ( vPath.asString() ); + UITab* tab = mEditorSplitter->isDocumentOpen( path ); + if ( !tab ) { + mEditorSplitter->loadFileFromPathInNewTab( path ); + } else { + tab->getTabWidget()->setTabSelected( tab ); + } + } + } + } ); + + if ( !file.empty() && FileSystem::fileExists( file ) ) { + if ( FileSystem::isDirectory( file ) ) { + tree->setModel( FileSystemModel::New( file ) ); + } else { + std::string rpath( FileSystem::getRealPath( file ) ); + tree->setModel( FileSystemModel::New( FileSystem::fileRemoveFileName( rpath ) ) ); + mEditorSplitter->loadFileFromPath( rpath ); + } + } else { + tree->setModel( FileSystemModel::New( "." ) ); + } + mWindow->runMainLoop( &appLoop ); } } diff --git a/src/tools/codeeditor/codeeditor.hpp b/src/tools/codeeditor/codeeditor.hpp index 00db3459e..b55d9d64a 100644 --- a/src/tools/codeeditor/codeeditor.hpp +++ b/src/tools/codeeditor/codeeditor.hpp @@ -98,8 +98,6 @@ class App : public UICodeEditorSplitter::Client { void init( const std::string& file, const Float& pidelDensity ); - bool loadFileFromPath( const std::string& path, UICodeEditor* codeEditor = nullptr ); - void setAppTitle( const std::string& title ); void openFileDialog();