TreeView in ecode and more fixes and improvements for the abstract views, treeview and models.

This commit is contained in:
Martín Lucas Golini
2020-07-19 01:35:20 -03:00
parent 2e1930f51c
commit 08caa0070d
14 changed files with 227 additions and 65 deletions

View File

@@ -48,6 +48,7 @@
#include <eepp/ui/uitheme.hpp>
#include <eepp/ui/uithememanager.hpp>
#include <eepp/ui/uitouchdraggablewidget.hpp>
#include <eepp/ui/uitreeview.hpp>
#include <eepp/ui/uiviewpager.hpp>
#include <eepp/ui/uiwidget.hpp>
#include <eepp/ui/uiwidgetcreator.hpp>
@@ -71,4 +72,8 @@
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
#include <eepp/ui/tools/uicolorpicker.hpp>
#include <eepp/ui/models/filesystemmodel.hpp>
#include <eepp/ui/models/model.hpp>
#include <eepp/ui/models/modelselection.hpp>
#endif

View File

@@ -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<size_t> 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};
};

View File

@@ -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 {

View File

@@ -45,6 +45,7 @@ class EE_API FileSystemModel : public Model {
FileInfo mInfo;
std::vector<Node> mChildren;
bool mHasTraversed{false};
bool mInfoDirty{true};
bool mSelected{false};
ModelIndex index( const FileSystemModel& model, int column ) const;
void traverseIfNeeded( const FileSystemModel& );

View File

@@ -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<void( UICodeEditor* )> run );

View File

@@ -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;
}

View File

@@ -2,6 +2,7 @@
#include <eepp/ui/uilinearlayout.hpp>
#include <eepp/ui/uipushbutton.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uiscrollbar.hpp>
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<size_t> columns, bool hidden ) {
for ( auto col : columns )
columnData( col ).visible = !hidden;
createOrUpdateColumns();
}
}}} // namespace EE::UI::Abstract

View File

@@ -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;
}

View File

@@ -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<UICodeEditor>();
if ( editor->getDocument().getFilePath() == path ) {
return tab;
}
}
}
return nullptr;
}
void UICodeEditorSplitter::applyColorScheme( const SyntaxColorScheme& colorScheme ) {
forEachEditor(
[colorScheme]( UICodeEditor* editor ) { editor->setColorScheme( colorScheme ); } );

View File

@@ -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 ) {

View File

@@ -35,7 +35,8 @@ UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& in
}
template <typename Callback> 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;
} );

View File

@@ -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 );
}

View File

@@ -1435,51 +1435,49 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
}
</style>
<RelativeLayout layout_width="match_parent" layout_height="match_parent">
<vbox layout_width="match_parent" layout_height="match_parent">
<RelativeLayout layout_width="match_parent" layout_height="0" layout_weight="1">
<vbox id="code_container" layout_width="match_parent" layout_height="match_parent"></vbox>
<hbox id="doc_info" layout_width="wrap_content" layout_height="wrap_content" layout_gravity="bottom|right">
<TextView id="doc_info_text" layout_width="wrap_content" layout_height="wrap_content" />
</hbox>
</RelativeLayout>
<searchbar id="search_bar" layout_width="match_parent" layout_height="wrap_content">
<vbox layout_width="wrap_content" layout_height="wrap_content" margin-right="4dp">
<TextView layout_width="wrap_content" layout_height="18dp" text="Find:" margin-bottom="2dp" />
<TextView layout_width="wrap_content" layout_height="18dp" text="Replace with:" />
</vbox>
<vbox layout_width="0" layout_weight="1" layout_height="wrap_content" margin-right="4dp">
<TextInput id="search_find" layout_width="match_parent" layout_height="18dp" padding="0" margin-bottom="2dp" />
<TextInput id="search_replace" layout_width="match_parent" layout_height="18dp" padding="0" />
</vbox>
<vbox layout_width="wrap_content" layout_height="wrap_content">
<hbox layout_width="wrap_content" layout_height="wrap_content" margin-bottom="2dp">
<PushButton id="find_prev" layout_width="wrap_content" layout_height="18dp" text="Previous" margin-right="4dp" />
<PushButton id="find_next" layout_width="wrap_content" layout_height="18dp" text="Next" margin-right="4dp" />
<CheckBox id="case_sensitive" layout_width="wrap_content" layout_height="wrap_content" text="Case sensitive" selected="true" />
<RelativeLayout layout_width="0" layout_weight="1" layout_height="18dp">
<Widget id="searchbar_close" class="close_button" layout_width="wrap_content" layout_height="wrap_content" layout_gravity="center_vertical|right" />
</RelativeLayout>
<Splitter id="project_splitter" layout_width="match_parent" layout_height="match_parent">
<TabWidget id="panel" tabbar-hide-on-single-tab="true" tabbar-allow-rearrange="true">
<TreeView id="project_view" />
<Tab text="Project" owns="project_view" />
</TabWidget>
<vbox>
<RelativeLayout layout_width="match_parent" layout_height="0" layout_weight="1">
<vbox id="code_container" layout_width="match_parent" layout_height="match_parent"></vbox>
<hbox id="doc_info" layout_width="wrap_content" layout_height="wrap_content" layout_gravity="bottom|right">
<TextView id="doc_info_text" layout_width="wrap_content" layout_height="wrap_content" />
</hbox>
<hbox layout_width="wrap_content" layout_height="wrap_content">
<PushButton id="replace" layout_width="wrap_content" layout_height="18dp" text="Replace" margin-right="4dp" />
<PushButton id="replace_find" layout_width="wrap_content" layout_height="18dp" text="Replace & Find" margin-right="4dp" />
<PushButton id="replace_all" layout_width="wrap_content" layout_height="18dp" text="Replace All" />
</hbox>
</vbox>
</searchbar>
</vbox>
</RelativeLayout>
<searchbar id="search_bar" layout_width="match_parent" layout_height="wrap_content">
<vbox layout_width="wrap_content" layout_height="wrap_content" margin-right="4dp">
<TextView layout_width="wrap_content" layout_height="18dp" text="Find:" margin-bottom="2dp" />
<TextView layout_width="wrap_content" layout_height="18dp" text="Replace with:" />
</vbox>
<vbox layout_width="0" layout_weight="1" layout_height="wrap_content" margin-right="4dp">
<TextInput id="search_find" layout_width="match_parent" layout_height="18dp" padding="0" margin-bottom="2dp" />
<TextInput id="search_replace" layout_width="match_parent" layout_height="18dp" padding="0" />
</vbox>
<vbox layout_width="wrap_content" layout_height="wrap_content">
<hbox layout_width="wrap_content" layout_height="wrap_content" margin-bottom="2dp">
<PushButton id="find_prev" layout_width="wrap_content" layout_height="18dp" text="Previous" margin-right="4dp" />
<PushButton id="find_next" layout_width="wrap_content" layout_height="18dp" text="Next" margin-right="4dp" />
<CheckBox id="case_sensitive" layout_width="wrap_content" layout_height="wrap_content" text="Case sensitive" selected="true" />
<RelativeLayout layout_width="0" layout_weight="1" layout_height="18dp">
<Widget id="searchbar_close" class="close_button" layout_width="wrap_content" layout_height="wrap_content" layout_gravity="center_vertical|right" />
</RelativeLayout>
</hbox>
<hbox layout_width="wrap_content" layout_height="wrap_content">
<PushButton id="replace" layout_width="wrap_content" layout_height="18dp" text="Replace" margin-right="4dp" />
<PushButton id="replace_find" layout_width="wrap_content" layout_height="18dp" text="Replace & Find" margin-right="4dp" />
<PushButton id="replace_all" layout_width="wrap_content" layout_height="18dp" text="Replace All" />
</hbox>
</vbox>
</searchbar>
</vbox>
</Splitter>
<TextView id="settings" layout_width="wrap_content" layout_height="wrap_content" text="&#xf0e9;" layout_gravity="top|right" />
</RelativeLayout>
)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<UISplitter>( "project_splitter" );
projectSplitter->setDivisionSplit( 0.15f );
UITreeView* tree = mUISceneNode->find<UITreeView>( "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<const ModelEvent*>( 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 );
}
}

View File

@@ -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();