From ad4e4b5367d5d9607ea0de512f99f26eba03f5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 19 Jul 2020 21:12:23 -0300 Subject: [PATCH] Minor improvements to UITreeView and related views. Also improved UISplitter. Improved side panel in ecode. --- TODO.md | 10 +- docs/articles/cssspecification.md | 21 ++ .../eepp/ui/abstract/uiabstracttableview.hpp | 10 + include/eepp/ui/css/propertydefinition.hpp | 2 + include/eepp/ui/css/stylesheetlength.hpp | 6 +- include/eepp/ui/models/filesystemmodel.hpp | 23 +- include/eepp/ui/models/model.hpp | 2 + include/eepp/ui/uipushbutton.hpp | 2 + include/eepp/ui/uiscrollablewidget.hpp | 5 + include/eepp/ui/uisplitter.hpp | 16 +- include/eepp/ui/uitableheadercolumn.hpp | 2 +- include/eepp/ui/uitablerow.hpp | 39 +++ include/eepp/ui/uitreeview.hpp | 9 +- projects/linux/ee.files | 1 + src/eepp/ui/abstract/uiabstracttableview.cpp | 25 +- src/eepp/ui/css/stylesheetlength.cpp | 8 +- src/eepp/ui/css/stylesheetspecification.cpp | 5 + src/eepp/ui/models/filesystemmodel.cpp | 41 ++- src/eepp/ui/models/model.cpp | 6 +- src/eepp/ui/uipushbutton.cpp | 32 ++- src/eepp/ui/uiscrollablewidget.cpp | 25 +- src/eepp/ui/uisplitter.cpp | 65 ++++- src/eepp/ui/uitreeview.cpp | 106 +++++--- src/tools/codeeditor/codeeditor.cpp | 256 +++++++++++------- src/tools/codeeditor/codeeditor.hpp | 9 + 25 files changed, 525 insertions(+), 201 deletions(-) create mode 100644 include/eepp/ui/uitablerow.hpp diff --git a/TODO.md b/TODO.md index ab9784618..2aed4a029 100644 --- a/TODO.md +++ b/TODO.md @@ -25,20 +25,18 @@ Detect errors and log them. * Add XML tags auto-close. -## UITreeView - -Implement a simple tree view widget, to at least cover the most common use cases. - ## Code Editor * Support single instance (when a new file is opened while a previous instance exists, open it in the first instance). -* Once `UITreeView` is finished add a directory/project tree view. - * Add support for function listing. * Display number of results in search and number of replacements. +* Add support for global/project search. + +* Add support for find and open file. + ## UI Editor * Integrate the `UICodeEditor` into the editor in order to be able to edit the layouts and CSS in app. diff --git a/docs/articles/cssspecification.md b/docs/articles/cssspecification.md index 05e8deab9..f4fd18eda 100644 --- a/docs/articles/cssspecification.md +++ b/docs/articles/cssspecification.md @@ -1508,6 +1508,27 @@ Sets the [skin](#skin) tint color. --- +### splitter-always-show + +Sets if the splitter divisor is always visible. If false it will be shown only if two views are +attached to the splitter. + +* Applicable to: EE::UI::UISplitter (Splitter) +* Data Type: [boolean](#boolean-data-type) +* Default value: `true` + +--- + +### splitter-partition + +Sets the space ocuppied by the first view contained by the splitter. + +* Applicable to: EE::UI::UISplitter (Splitter) +* Data Type: [length-percentage](#length-percentage-data-type) +* Default value: `50%` + +--- + ### src Sets the source of a resource in an element that supports source. diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index aa8681dde..392229146 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -46,6 +46,14 @@ class EE_API UIAbstractTableView : public UIAbstractView { int visibleColumnCount() const; + /** In pixels. */ + void setRowHeight( const Float& rowHeight ); + + void columnResizeToContent( const size_t& colIndex ); + + /** In pixels. */ + void setColumnWidth( const size_t& colIndex, const Float& width ); + protected: friend class EE::UI::UITableHeaderColumn; @@ -53,6 +61,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { UIAbstractTableView( const std::string& tag ); + Float mRowHeight{0}; + struct ColumnData { Float width{0}; bool visible{true}; diff --git a/include/eepp/ui/css/propertydefinition.hpp b/include/eepp/ui/css/propertydefinition.hpp index c8cfb98f7..3adf4a7d0 100644 --- a/include/eepp/ui/css/propertydefinition.hpp +++ b/include/eepp/ui/css/propertydefinition.hpp @@ -191,6 +191,8 @@ enum class PropertyId : Uint32 { TabBarHideOnSingleTab = String::hash( "tabbar-hide-on-single-tab" ), TabBarAllowRearrange = String::hash( "tabbar-allow-rearrange" ), TabBarAllowDragAndDrop = String::hash( "tabbar-allow-drag-and-drop-tabs" ), + SplitterPartition = String::hash( "splitter-partition" ), + SplitterAlwaysShow = String::hash( "splitter-always-show" ), }; enum class PropertyType : Uint32 { diff --git a/include/eepp/ui/css/stylesheetlength.hpp b/include/eepp/ui/css/stylesheetlength.hpp index 7ec24741d..9b2011715 100644 --- a/include/eepp/ui/css/stylesheetlength.hpp +++ b/include/eepp/ui/css/stylesheetlength.hpp @@ -40,7 +40,7 @@ class EE_API StyleSheetLength { StyleSheetLength( const Float& val, const Unit& unit ); - StyleSheetLength( std::string val, const Float& defaultValue = 0 ); + StyleSheetLength( const std::string& val, const Float& defaultValue = 0 ); StyleSheetLength( const StyleSheetLength& val ); @@ -56,7 +56,9 @@ class EE_API StyleSheetLength { Float asDp( const Float& parentSize, const Sizef& viewSize, const Float& displayDpi, const Float& elFontSize = 12, const Float& globalFontSize = 12 ) const; - bool operator==( const StyleSheetLength& val ); + bool operator==( const StyleSheetLength& val ) const; + + bool operator!=( const StyleSheetLength& val ) const; StyleSheetLength& operator=( const StyleSheetLength& val ); diff --git a/include/eepp/ui/models/filesystemmodel.hpp b/include/eepp/ui/models/filesystemmodel.hpp index 9ccb38993..600eefdb6 100644 --- a/include/eepp/ui/models/filesystemmodel.hpp +++ b/include/eepp/ui/models/filesystemmodel.hpp @@ -20,6 +20,7 @@ class EE_API FileSystemModel : public Model { Permissions, ModificationTime, Inode, + Path, SymlinkTarget, Count, }; @@ -32,10 +33,14 @@ class EE_API FileSystemModel : public Model { Node* getParent() const { return mParent; } const FileInfo& info() const { return mInfo; } bool isSelected() const { return mSelected; } - void setSelected( bool selected ); + void setSelected( bool selected ) { mSelected = selected; }; const std::string& fullPath() const; const std::string& getMimeType() const { return mMimeType; } size_t childCount() const { return mChildren.size(); } + const Node& getChild( const size_t& index ) { + eeASSERT( index < mChildren.size() ); + return mChildren[index]; + } private: friend class FileSystemModel; @@ -49,7 +54,7 @@ class EE_API FileSystemModel : public Model { bool mSelected{false}; ModelIndex index( const FileSystemModel& model, int column ) const; void traverseIfNeeded( const FileSystemModel& ); - void reifyIfNeeded( const FileSystemModel& ); + void refreshIfNeeded( const FileSystemModel& ); bool fetchData( const String& fullPath ); }; @@ -62,6 +67,8 @@ class EE_API FileSystemModel : public Model { void setRootPath( const std::string& rootPath ); + void reload(); + void update(); const Node& node( const ModelIndex& index ) const; @@ -76,14 +83,24 @@ class EE_API FileSystemModel : public Model { virtual Drawable* iconFor( const Node& node, const ModelIndex& index ) const; - FileSystemModel( const std::string& rootPath, const Mode& mode ); + void setMode( const Mode& mode ); + + void updateNodeSelection( const ModelIndex& index, const bool selected ); + + ModelIndex getPreviouslySelectedIndex() const; + + void setPreviouslySelectedIndex( const ModelIndex& previouslySelectedIndex ); protected: std::string mRootPath; std::unique_ptr mRoot{nullptr}; Mode mMode{Mode::FilesAndDirectories}; + ModelIndex mPreviouslySelectedIndex{}; + Node& nodeRef( const ModelIndex& index ) const; + + FileSystemModel( const std::string& rootPath, const Mode& mode ); }; }}} // namespace EE::UI::Models diff --git a/include/eepp/ui/models/model.hpp b/include/eepp/ui/models/model.hpp index 5d7f7e1cf..a1793b6ea 100644 --- a/include/eepp/ui/models/model.hpp +++ b/include/eepp/ui/models/model.hpp @@ -80,6 +80,8 @@ class EE_API Model { void unregisterView( UIAbstractView* ); + void refreshView(); + void setOnUpdate( const std::function& onUpdate ); protected: diff --git a/include/eepp/ui/uipushbutton.hpp b/include/eepp/ui/uipushbutton.hpp index 99a7b4ccf..1761f6e26 100644 --- a/include/eepp/ui/uipushbutton.hpp +++ b/include/eepp/ui/uipushbutton.hpp @@ -53,6 +53,8 @@ class EE_API UIPushButton : public UIWidget { explicit UIPushButton( const std::string& tag ); + virtual void updateLayout(); + virtual void onSizeChange(); virtual void onAlphaChange(); diff --git a/include/eepp/ui/uiscrollablewidget.hpp b/include/eepp/ui/uiscrollablewidget.hpp index 76e40860e..399bbfb3d 100644 --- a/include/eepp/ui/uiscrollablewidget.hpp +++ b/include/eepp/ui/uiscrollablewidget.hpp @@ -42,10 +42,15 @@ class EE_API UIScrollableWidget : public UIWidget { void scrollToBottom(); + void scrollToPosition( const Vector2f& pos, const bool& scrollVertically = true, + const bool& scrollHorizontally = false ); + Sizef getScrollableArea() const; Sizef getVisibleArea() const; + Rectf getVisibleRect() const; + protected: ScrollViewType mViewType; ScrollBarMode mVScrollMode; diff --git a/include/eepp/ui/uisplitter.hpp b/include/eepp/ui/uisplitter.hpp index 74451a441..7a624c67e 100644 --- a/include/eepp/ui/uisplitter.hpp +++ b/include/eepp/ui/uisplitter.hpp @@ -9,8 +9,6 @@ class EE_API UISplitter : public UILayout { public: static UISplitter* New(); - UISplitter(); - ~UISplitter(); Uint32 getType() const; @@ -25,9 +23,9 @@ class EE_API UISplitter : public UILayout { void setAlwaysShowSplitter( bool alwaysShowSplitter ); - const Float& getDivisionSplit() const; + const StyleSheetLength& getSplitPartition() const; - void setDivisionSplit( const Float& divisionSplit ); + void setSplitPartition( const StyleSheetLength& divisionSplit ); void swap(); @@ -39,15 +37,21 @@ class EE_API UISplitter : public UILayout { UIWidget* getLastWidget() const; + bool applyProperty( const StyleSheetProperty& attribute ); + + virtual std::string getPropertyString( const PropertyDefinition* propertyDef, + const Uint32& propertyIndex = 0 ); + protected: UIOrientation mOrientation; - bool mSplitOnlyWhenNeeded; bool mAlwaysShowSplitter; - Float mDivisionSplit; + StyleSheetLength mSplitPartition; UIWidget* mSplitter; UIWidget* mFirstWidget; UIWidget* mLastWidget; + UISplitter(); + virtual void onChildCountChange( Node* child, const bool& removed ); virtual void updateLayout(); diff --git a/include/eepp/ui/uitableheadercolumn.hpp b/include/eepp/ui/uitableheadercolumn.hpp index 3c83c159e..d75932bec 100644 --- a/include/eepp/ui/uitableheadercolumn.hpp +++ b/include/eepp/ui/uitableheadercolumn.hpp @@ -10,7 +10,7 @@ class UIAbstractTableView; } using namespace Abstract; -class UITableHeaderColumn : public UIPushButton { +class EE_API UITableHeaderColumn : public UIPushButton { public: UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex ); diff --git a/include/eepp/ui/uitablerow.hpp b/include/eepp/ui/uitablerow.hpp new file mode 100644 index 000000000..37b15c630 --- /dev/null +++ b/include/eepp/ui/uitablerow.hpp @@ -0,0 +1,39 @@ +#ifndef EE_UI_UITABLEROW_HPP +#define EE_UI_UITABLEROW_HPP + +#include +#include + +using namespace EE::UI::Models; + +namespace EE { namespace UI { + +class EE_API UITableRow : public UIWidget { + public: + static UITableRow* New( const std::string& tag ) { return eeNew( UITableRow, ( tag ) ); } + + ModelIndex getCurIndex() const { return mCurIndex; } + + void setCurIndex( const ModelIndex& curIndex ) { mCurIndex = curIndex; } + + protected: + UITableRow( const std::string& tag ) : UIWidget( tag ) {} + + virtual Uint32 onMessage( const NodeMessage* msg ) { + if ( msg->getMsg() == NodeMessage::MouseDown && ( msg->getFlags() & EE_BUTTON_LMASK ) && + ( !getEventDispatcher()->getMouseDownNode() || + getEventDispatcher()->getMouseDownNode() == this || + isParentOf( getEventDispatcher()->getMouseDownNode() ) ) && + getEventDispatcher()->getNodeDragging() == nullptr ) { + sendMouseEvent( Event::MouseDown, getEventDispatcher()->getMousePos(), + msg->getFlags() ); + } + return 0; + } + + ModelIndex mCurIndex; +}; + +}} // namespace EE::UI + +#endif // EE_UI_UITABLEROW_HPP diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index 9dbe1e93e..1b267f287 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -2,6 +2,7 @@ #define EE_UI_UITREEVIEW_HPP #include +#include #include #include @@ -71,12 +72,14 @@ class EE_API UITreeView : public UIAbstractTableView { virtual void onColumnSizeChange( const size_t& colIndex ); + virtual UITableRow* createRow(); + + virtual UITableRow* updateRow( const int& rowIndex, const ModelIndex& index, + const Float& yOffset ); + virtual UIWidget* updateCell( const int& rowIndex, const ModelIndex& index, const size_t& col, const size_t& indentLevel, const Float& yOffset ); - virtual UIWidget* updateRow( const int& rowIndex, const ModelIndex& index, - const Float& yOffset ); - virtual UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index, const size_t& col ); virtual void onScrollChange(); diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 9f6ec6cc7..429a16f6e 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -399,6 +399,7 @@ ../../include/eepp/ui/uitablecell.hpp ../../include/eepp/ui/uitable.hpp ../../include/eepp/ui/uitableheadercolumn.hpp +../../include/eepp/ui/uitablerow.hpp ../../include/eepp/ui/uitabwidget.hpp ../../include/eepp/ui/uitextedit.hpp ../../include/eepp/ui/uitextinput.hpp diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 8e0fd71aa..30d404ffc 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -26,8 +26,29 @@ bool UIAbstractTableView::isType( const Uint32& type ) const { } Float UIAbstractTableView::getRowHeight() const { - return eeceil( columnData( 0 ).widget ? columnData( 0 ).widget->getPixelsSize().getHeight() - : 16 ); + return mRowHeight != 0 ? mRowHeight + : ( eeceil( columnData( 0 ).widget + ? columnData( 0 ).widget->getPixelsSize().getHeight() + : 16 ) ); +} + +void UIAbstractTableView::setRowHeight( const Float& rowHeight ) { + if ( mRowHeight != rowHeight ) { + mRowHeight = rowHeight; + createOrUpdateColumns(); + } +} + +void UIAbstractTableView::columnResizeToContent( const size_t& colIndex ) { + onColumnResizeToContent( colIndex ); +} + +void UIAbstractTableView::setColumnWidth( const size_t& colIndex, const Float& width ) { + if ( columnData( colIndex ).width != width ) { + columnData( colIndex ).width = width; + updateHeaderSize(); + onColumnSizeChange( colIndex ); + } } void UIAbstractTableView::selectAll() { diff --git a/src/eepp/ui/css/stylesheetlength.cpp b/src/eepp/ui/css/stylesheetlength.cpp index 9c2aa53a8..9e0d68e6a 100644 --- a/src/eepp/ui/css/stylesheetlength.cpp +++ b/src/eepp/ui/css/stylesheetlength.cpp @@ -91,7 +91,7 @@ StyleSheetLength::StyleSheetLength() : mUnit( Px ), mValue( 0 ) {} StyleSheetLength::StyleSheetLength( const Float& val, const StyleSheetLength::Unit& unit ) : mUnit( unit ), mValue( val ) {} -StyleSheetLength::StyleSheetLength( std::string val, const Float& defaultValue ) : +StyleSheetLength::StyleSheetLength( const std::string& val, const Float& defaultValue ) : StyleSheetLength( fromString( val, defaultValue ) ) {} StyleSheetLength::StyleSheetLength( const StyleSheetLength& val ) { @@ -170,10 +170,14 @@ Float StyleSheetLength::asDp( const Float& parentSize, const Sizef& viewSize, asPixels( parentSize, viewSize, displayDpi, elFontSize, globalFontSize ) ); } -bool StyleSheetLength::operator==( const StyleSheetLength& length ) { +bool StyleSheetLength::operator==( const StyleSheetLength& length ) const { return mValue == length.mValue && mUnit == length.mUnit; } +bool StyleSheetLength::operator!=( const StyleSheetLength& length ) const { + return !( *this == length ); +} + StyleSheetLength& StyleSheetLength::operator=( const Float& val ) { mValue = val; mUnit = Unit::Px; diff --git a/src/eepp/ui/css/stylesheetspecification.cpp b/src/eepp/ui/css/stylesheetspecification.cpp index b8d8e6269..8f33fc251 100644 --- a/src/eepp/ui/css/stylesheetspecification.cpp +++ b/src/eepp/ui/css/stylesheetspecification.cpp @@ -364,6 +364,11 @@ void StyleSheetSpecification::registerDefaultProperties() { registerProperty( "tabbar-allow-rearrange", "false" ); registerProperty( "tabbar-allow-drag-and-drop-tabs", "false" ); + registerProperty( "splitter-partition", "50%" ) + .setType( PropertyType::NumberLength ) + .setRelativeTarget( PropertyRelativeTarget::LocalBlockWidth ); + registerProperty( "splitter-always-show", "true" ).setType( PropertyType::Bool ); + // Shorthands registerShorthand( "margin", {"margin-top", "margin-right", "margin-bottom", "margin-left"}, "box" ); diff --git a/src/eepp/ui/models/filesystemmodel.cpp b/src/eepp/ui/models/filesystemmodel.cpp index 7e703fa29..ebf205e2c 100644 --- a/src/eepp/ui/models/filesystemmodel.cpp +++ b/src/eepp/ui/models/filesystemmodel.cpp @@ -59,7 +59,7 @@ void FileSystemModel::Node::traverseIfNeeded( const FileSystemModel& model ) { } } -void FileSystemModel::Node::reifyIfNeeded( const FileSystemModel& model ) { +void FileSystemModel::Node::refreshIfNeeded( const FileSystemModel& model ) { traverseIfNeeded( model ); if ( mInfoDirty ) fetchData( fullPath() ); @@ -75,7 +75,7 @@ bool FileSystemModel::Node::fetchData( const String& fullPath ) { std::shared_ptr FileSystemModel::New( const std::string& rootPath, const FileSystemModel::Mode& mode ) { - return std::make_shared( rootPath, mode ); + return std::shared_ptr( new FileSystemModel( rootPath, mode ) ); } FileSystemModel::FileSystemModel( const std::string& rootPath, const FileSystemModel::Mode& mode ) : @@ -92,6 +92,10 @@ void FileSystemModel::setRootPath( const std::string& rootPath ) { update(); } +void FileSystemModel::reload() { + setRootPath( mRootPath ); +} + void FileSystemModel::update() { mRoot = std::make_unique( mRootPath, *this ); onModelUpdate(); @@ -109,7 +113,7 @@ FileSystemModel::Node& FileSystemModel::nodeRef( const ModelIndex& index ) const size_t FileSystemModel::rowCount( const ModelIndex& index ) const { Node& node = const_cast( this->node( index ) ); - node.reifyIfNeeded( *this ); + node.refreshIfNeeded( *this ); if ( node.info().isDirectory() ) return node.mChildren.size(); return 0; @@ -139,6 +143,8 @@ std::string FileSystemModel::columnName( const size_t& column ) const { return "Inode"; case Column::SymlinkTarget: return "Symlink target"; + case Column::Path: + return "Path"; default: return ""; } @@ -172,7 +178,7 @@ Variant FileSystemModel::data( const ModelIndex& index, Model::Role role ) const if ( role == Role::Custom ) { eeASSERT( index.column() == Column::Name ); - return Variant( node.info().getFilepath() ); + return Variant( node.info().getFilepath().c_str() ); } if ( role == Role::Sort ) { @@ -195,6 +201,8 @@ Variant FileSystemModel::data( const ModelIndex& index, Model::Role role ) const return node.info().getInode(); case Column::SymlinkTarget: return Variant( node.info().linksTo() ); + case Column::Path: + return Variant( node.info().getFilepath().c_str() ); default: eeASSERT( false ); } @@ -220,6 +228,8 @@ Variant FileSystemModel::data( const ModelIndex& index, Model::Role role ) const return Variant( String::toString( node.info().getInode() ) ); case Column::SymlinkTarget: return node.info().isLink() ? Variant( node.info().linksTo() ) : Variant(); + case Column::Path: + return Variant( node.info().getFilepath().c_str() ); } } @@ -244,7 +254,7 @@ ModelIndex FileSystemModel::index( int row, int column, const ModelIndex& parent if ( row < 0 || column < 0 ) return {}; auto& node = this->node( parent ); - const_cast( node ).reifyIfNeeded( *this ); + const_cast( node ).refreshIfNeeded( *this ); if ( static_cast( row ) >= node.mChildren.size() ) return {}; return createIndex( row, column, &node.mChildren[row] ); @@ -258,4 +268,25 @@ Drawable* FileSystemModel::iconFor( const Node& node, const ModelIndex& index ) return nullptr; } +void FileSystemModel::setMode( const Mode& mode ) { + if ( mode != mMode ) { + mMode = mode; + reload(); + } +} + +void FileSystemModel::updateNodeSelection( const ModelIndex& index, const bool selected ) { + Node& node = const_cast( this->node( index ) ); + node.setSelected( selected ); + setPreviouslySelectedIndex( index ); +} + +ModelIndex FileSystemModel::getPreviouslySelectedIndex() const { + return mPreviouslySelectedIndex; +} + +void FileSystemModel::setPreviouslySelectedIndex( const ModelIndex& previouslySelectedIndex ) { + mPreviouslySelectedIndex = previouslySelectedIndex; +} + }}} // namespace EE::UI::Models diff --git a/src/eepp/ui/models/model.cpp b/src/eepp/ui/models/model.cpp index c0b3c3ce0..f91c3eb53 100644 --- a/src/eepp/ui/models/model.cpp +++ b/src/eepp/ui/models/model.cpp @@ -18,6 +18,10 @@ void Model::unregisterView( UIAbstractView* view ) { mViews.erase( view ); } +void Model::refreshView() { + forEachView( [&]( UIAbstractView* view ) { view->invalidateDraw(); } ); +} + void Model::registerView( UIAbstractView* view ) { mViews.insert( view ); } @@ -43,4 +47,4 @@ bool Model::acceptsDrag( const ModelIndex&, const std::string& ) { return false; } -}}} // namespace EE::UI::Model +}}} // namespace EE::UI::Models diff --git a/src/eepp/ui/uipushbutton.cpp b/src/eepp/ui/uipushbutton.cpp index 9ad12c559..e808a655f 100644 --- a/src/eepp/ui/uipushbutton.cpp +++ b/src/eepp/ui/uipushbutton.cpp @@ -108,15 +108,7 @@ void UIPushButton::onAutoSize() { } } -void UIPushButton::onPaddingChange() { - onSizeChange(); - - UIWidget::onPaddingChange(); -} - -void UIPushButton::onSizeChange() { - onAutoSize(); - +void UIPushButton::updateLayout() { Rectf autoPadding; if ( mFlags & UI_AUTO_PADDING ) { @@ -225,6 +217,18 @@ void UIPushButton::onSizeChange() { if ( NULL != eWidget && eWidget->isVisible() ) { eWidget->setPixelsPosition( ePos ); } +} + +void UIPushButton::onPaddingChange() { + onSizeChange(); + + UIWidget::onPaddingChange(); +} + +void UIPushButton::onSizeChange() { + onAutoSize(); + + updateLayout(); UIWidget::onSizeChange(); } @@ -237,7 +241,7 @@ void UIPushButton::setTheme( UITheme* Theme ) { } void UIPushButton::onThemeLoaded() { - onSizeChange(); + updateLayout(); UIWidget::onThemeLoaded(); } @@ -245,7 +249,7 @@ void UIPushButton::onThemeLoaded() { UIPushButton* UIPushButton::setIcon( Drawable* Icon ) { if ( mIcon->getDrawable() != Icon ) { mIcon->setDrawable( Icon ); - onSizeChange(); + updateLayout(); } return this; } @@ -255,8 +259,10 @@ UIImage* UIPushButton::getIcon() const { } UIPushButton* UIPushButton::setText( const String& text ) { - mTextBox->setText( text ); - onSizeChange(); + if ( text != mTextBox->getText() ) { + mTextBox->setText( text ); + updateLayout(); + } return this; } diff --git a/src/eepp/ui/uiscrollablewidget.cpp b/src/eepp/ui/uiscrollablewidget.cpp index 9c1e57f94..424c0e1ef 100644 --- a/src/eepp/ui/uiscrollablewidget.cpp +++ b/src/eepp/ui/uiscrollablewidget.cpp @@ -172,7 +172,7 @@ void UIScrollableWidget::onContentSizeChange() { Sizef UIScrollableWidget::getScrollableArea() const { Sizef contentSize( getContentSize() ); - Sizef size = getVisibleArea(); + Sizef size( getVisibleArea() ); return contentSize - size; } @@ -185,6 +185,10 @@ Sizef UIScrollableWidget::getVisibleArea() const { return size; } +Rectf UIScrollableWidget::getVisibleRect() const { + return Rectf( mScrollOffset, getVisibleArea() ); +} + void UIScrollableWidget::updateScroll() { Sizef totalScroll = getScrollableArea(); Vector2f initScroll( mScrollOffset ); @@ -238,6 +242,25 @@ void UIScrollableWidget::scrollToBottom() { mVScroll->setValue( 1 ); } +void UIScrollableWidget::scrollToPosition( const Vector2f& pos, const bool& scrollVertically, + const bool& scrollHorizontally ) { + Rectf visibleRect( getVisibleRect() ); + if ( visibleRect.contains( pos ) ) + return; + + if ( scrollVertically ) { + if ( pos.y < visibleRect.Top || pos.y > visibleRect.Bottom ) { + mVScroll->setValue( pos.y / getContentSize().y ); + } + } + + if ( scrollHorizontally ) { + if ( pos.x < visibleRect.Left || pos.x > visibleRect.Right ) { + mHScroll->setValue( pos.x / getContentSize().x ); + } + } +} + bool UIScrollableWidget::applyProperty( const StyleSheetProperty& attribute ) { if ( !checkPropertyDefinition( attribute ) ) return false; diff --git a/src/eepp/ui/uisplitter.cpp b/src/eepp/ui/uisplitter.cpp index efb90e08a..98e31e248 100644 --- a/src/eepp/ui/uisplitter.cpp +++ b/src/eepp/ui/uisplitter.cpp @@ -11,9 +11,8 @@ UISplitter* UISplitter::New() { UISplitter::UISplitter() : UILayout( "splitter" ), mOrientation( UIOrientation::Horizontal ), - mSplitOnlyWhenNeeded( true ), mAlwaysShowSplitter( true ), - mDivisionSplit( 0.5f ), + mSplitPartition( StyleSheetLength( "50%" ) ), mFirstWidget( NULL ), mLastWidget( NULL ) { mFlags |= UI_OWNS_CHILDS_POSITION; @@ -77,13 +76,13 @@ void UISplitter::setAlwaysShowSplitter( bool alwaysShowSplitter ) { } } -const Float& UISplitter::getDivisionSplit() const { - return mDivisionSplit; +const StyleSheetLength& UISplitter::getSplitPartition() const { + return mSplitPartition; } -void UISplitter::setDivisionSplit( const Float& divisionSplit ) { - if ( eeclamp( divisionSplit, 0.f, 1.f ) != mDivisionSplit ) { - mDivisionSplit = eeclamp( divisionSplit, 0.f, 1.f ); +void UISplitter::setSplitPartition( const StyleSheetLength& divisionSplit ) { + if ( divisionSplit != mSplitPartition ) { + mSplitPartition = divisionSplit; setLayoutDirty(); } } @@ -113,6 +112,35 @@ UIWidget* UISplitter::getLastWidget() const { return mLastWidget; } +bool UISplitter::applyProperty( const StyleSheetProperty& attribute ) { + if ( !checkPropertyDefinition( attribute ) ) + return false; + + switch ( attribute.getPropertyDefinition()->getPropertyId() ) { + case PropertyId::SplitterPartition: + setSplitPartition( StyleSheetLength( attribute.asString() ) ); + case PropertyId::SplitterAlwaysShow: + setAlwaysShowSplitter( attribute.asBool() ); + default: + return UILayout::applyProperty( attribute ); + } +} + +std::string UISplitter::getPropertyString( const PropertyDefinition* propertyDef, + const Uint32& propertyIndex ) { + if ( NULL == propertyDef ) + return ""; + + switch ( propertyDef->getPropertyId() ) { + case PropertyId::SplitterPartition: + return getSplitPartition().toString(); + case PropertyId::SplitterAlwaysShow: + return alwaysShowSplitter() ? "true" : "false"; + default: + return UILayout::getPropertyString( propertyDef, propertyIndex ); + } +} + void UISplitter::onChildCountChange( Node* child, const bool& removed ) { if ( child != mSplitter ) { if ( !removed ) { @@ -131,6 +159,7 @@ void UISplitter::onChildCountChange( Node* child, const bool& removed ) { } else { mLastWidget = childWidget; } + childWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); mSplitter->toFront(); } } else { @@ -234,13 +263,19 @@ void UISplitter::updateFromDrag() { } if ( UIOrientation::Horizontal == mOrientation ) { - mDivisionSplit = - ( mSplitter->getPixelsPosition().x + mSplitter->getPixelsSize().getWidth() ) / - ( mSize.getWidth() - mRealPadding.Left - mRealPadding.Right ); + mSplitPartition = StyleSheetLength( + eeclamp( + ( mSplitter->getPixelsPosition().x + mSplitter->getPixelsSize().getWidth() ) / + ( mSize.getWidth() - mRealPadding.Left - mRealPadding.Right ) * 100, + 0, 100 ), + StyleSheetLength::Percentage ); } else { - mDivisionSplit = - ( mSplitter->getPixelsPosition().y + mSplitter->getPixelsSize().getHeight() ) / - ( mSize.getHeight() - mRealPadding.Top - mRealPadding.Bottom ); + mSplitPartition = StyleSheetLength( + eeclamp( + ( mSplitter->getPixelsPosition().y + mSplitter->getPixelsSize().getHeight() ) / + ( mSize.getHeight() - mRealPadding.Top - mRealPadding.Bottom ) * 100, + 0, 100 ), + StyleSheetLength::Percentage ); } mDirtyLayout = false; @@ -286,7 +321,7 @@ void UISplitter::updateLayout() { } } - if ( mSplitOnlyWhenNeeded && !mLastWidget ) { + if ( !mLastWidget ) { mSplitter->setVisible( false )->setEnabled( false ); if ( mFirstWidget ) { @@ -311,7 +346,7 @@ void UISplitter::updateLayout() { : mSplitter->getPixelsSize().getHeight(); } - Float firstSplit = ( totalSpace * mDivisionSplit ); + Float firstSplit = convertLength( mSplitPartition, totalSpace ); Float secondSplit = totalSpace - firstSplit; if ( mFirstWidget ) { diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 8a83185d9..5f9c30bd2 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -96,49 +97,29 @@ void UITreeView::onColumnSizeChange( const size_t& ) { updateContentSize(); } -class UITableRow : public UIWidget { - public: - UITableRow( const std::string& tag ) : UIWidget( tag ) {} - - ModelIndex getCurIndex() const { return mCurIndex; } - - void setCurIndex( const ModelIndex& curIndex ) { mCurIndex = curIndex; } - - protected: - virtual Uint32 onMessage( const NodeMessage* msg ) { - if ( msg->getMsg() == NodeMessage::MouseDown && ( msg->getFlags() & EE_BUTTON_LMASK ) && - ( !getEventDispatcher()->getMouseDownNode() || +UITableRow* UITreeView::createRow() { + UITableRow* rowWidget = UITableRow::New( "table::row" ); + rowWidget->setParent( this ); + rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + rowWidget->reloadStyle( true, true, true ); + rowWidget->addEventListener( Event::MouseDown, [&]( const Event* event ) { + if ( ( !getEventDispatcher()->getMouseDownNode() || getEventDispatcher()->getMouseDownNode() == this || isParentOf( getEventDispatcher()->getMouseDownNode() ) ) && getEventDispatcher()->getNodeDragging() == nullptr ) { - sendMouseEvent( Event::MouseDown, getEventDispatcher()->getMousePos(), - msg->getFlags() ); + getSelection().set( event->getNode()->asType()->getCurIndex() ); } - return 0; - } + } ); + return rowWidget; +} - ModelIndex mCurIndex; -}; - -UIWidget* UITreeView::updateRow( const int& rowIndex, const ModelIndex& index, - const Float& yOffset ) { +UITableRow* UITreeView::updateRow( const int& rowIndex, const ModelIndex& index, + const Float& yOffset ) { if ( rowIndex >= (int)mRows.size() ) mRows.resize( rowIndex + 1, nullptr ); UITableRow* rowWidget = nullptr; if ( mRows[rowIndex] == nullptr ) { - rowWidget = eeNew( UITableRow, ( "table::row" ) ); - rowWidget->clipEnable(); - rowWidget->setParent( this ); - rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); - rowWidget->reloadStyle( true, true, true ); - rowWidget->addEventListener( Event::MouseDown, [&]( const Event* event ) { - if ( ( !getEventDispatcher()->getMouseDownNode() || - getEventDispatcher()->getMouseDownNode() == this || - isParentOf( getEventDispatcher()->getMouseDownNode() ) ) && - getEventDispatcher()->getNodeDragging() == nullptr ) { - getSelection().set( event->getNode()->asType()->getCurIndex() ); - } - } ); + rowWidget = createRow(); mRows[rowIndex] = rowWidget; } else { rowWidget = mRows[rowIndex]; @@ -375,10 +356,63 @@ Uint32 UITreeView::onKeyDown( const KeyEvent& event ) { auto curIndex = getSelection().first(); switch ( event.getKeyCode() ) { + case KEY_PAGEUP: { + int pageSize = eefloor( getVisibleArea().getHeight() / getRowHeight() ) - 1; + std::deque> deque; + Float curY; + traverseTree( + [&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) { + deque.push_back( {index, offsetY} ); + if ( (int)deque.size() > pageSize ) + deque.pop_front(); + if ( index == curIndex ) + return IterationDecision::Break; + return IterationDecision::Continue; + } ); + curY = deque.front().second - getHeaderHeight(); + getSelection().set( deque.front().first ); + scrollToPosition( {mScrollOffset.x, curY} ); + break; + } + case KEY_PAGEDOWN: { + int pageSize = eefloor( getVisibleArea().getHeight() / getRowHeight() ) - 1; + int counted = 0; + bool foundStart = false; + bool resultFound = false; + ModelIndex foundIndex; + Float curY; + Float lastOffsetY; + ModelIndex lastIndex; + traverseTree( + [&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) { + if ( index == curIndex ) { + foundStart = true; + } else if ( foundStart ) { + counted++; + if ( counted == pageSize ) { + foundIndex = index; + curY = offsetY; + resultFound = true; + return IterationDecision::Break; + } + } + lastOffsetY = offsetY; + lastIndex = index; + return IterationDecision::Continue; + } ); + if ( !resultFound ) { + foundIndex = lastIndex; + curY = lastOffsetY; + } + curY += getRowHeight(); + getSelection().set( foundIndex ); + scrollToPosition( {mScrollOffset.x, curY} ); + break; + } case KEY_UP: { ModelIndex prevIndex; ModelIndex foundIndex; - Float curY; + Float curY = 0; traverseTree( [&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) { if ( index == curIndex ) { @@ -404,7 +438,7 @@ Uint32 UITreeView::onKeyDown( const KeyEvent& event ) { case KEY_DOWN: { ModelIndex prevIndex; ModelIndex foundIndex; - Float curY; + Float curY = 0; traverseTree( [&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) { if ( prevIndex == curIndex ) { diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 157fd932a..6d90eb3f7 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -257,6 +257,7 @@ void App::loadConfig() { mConfig.window.maximized = mIniState.getValueB( "window", "maximized", false ); mConfig.window.pixelDensity = mIniState.getValueF( "window", "pixeldensity" ); mConfig.window.winIcon = mIni.getValue( "window", "winicon", mResPath + "assets/icon/ee.png" ); + mConfig.window.panelPartition = mIniState.getValue( "window", "panel_partition", "15%" ); mConfig.editor.showLineNumbers = mIni.getValueB( "editor", "show_line_numbers", true ); mConfig.editor.showWhiteSpaces = mIni.getValueB( "editor", "show_white_spaces", true ); mConfig.editor.highlightMatchingBracket = @@ -265,6 +266,7 @@ void App::loadConfig() { mIni.getValueB( "editor", "highlight_current_line", true ); mConfig.editor.horizontalScrollbar = mIni.getValueB( "editor", "horizontal_scrollbar", false ); mConfig.ui.fontSize = mIni.getValue( "ui", "font_size", "11dp" ); + mConfig.ui.showSidePanel = mIni.getValueB( "ui", "show_side_panel", true ); mConfig.editor.trimTrailingWhitespaces = mIni.getValueB( "editor", "trim_trailing_whitespaces", false ); mConfig.editor.forceNewLineAtEndOfFile = @@ -296,6 +298,9 @@ void App::saveConfig() { mIniState.setValueI( "window", "height", mConfig.window.size.getHeight() ); mIniState.setValueB( "window", "maximized", mConfig.window.maximized ); mIniState.setValueF( "window", "pixeldensity", mConfig.window.pixelDensity ); + mIniState.setValue( "window", "panel_partition", + mProjectSplitter ? mProjectSplitter->getSplitPartition().toString() + : "15%" ); mIniState.setValue( "files", "recentfiles", String::join( mRecentFiles, ';' ) ); mIni.setValueB( "editor", "show_line_numbers", mConfig.editor.showLineNumbers ); mIni.setValueB( "editor", "show_white_spaces", mConfig.editor.showWhiteSpaces ); @@ -305,6 +310,7 @@ void App::saveConfig() { mIni.setValueB( "editor", "horizontal_scrollbar", mConfig.editor.horizontalScrollbar ); mIni.setValue( "editor", "font_size", mConfig.editor.fontSize.toString() ); mIni.setValue( "ui", "font_size", mConfig.ui.fontSize.toString() ); + mIni.setValueB( "ui", "show_side_panel", mConfig.ui.showSidePanel ); mIni.setValueB( "editor", "trim_trailing_whitespaces", mConfig.editor.trimTrailingWhitespaces ); mIni.setValueB( "editor", "force_new_line_at_end_of_file", mConfig.editor.forceNewLineAtEndOfFile ); @@ -547,101 +553,46 @@ void App::updateRecentFiles() { } } -UIMenu* App::createViewMenu() { - mViewMenu = UIPopUpMenu::New(); - mViewMenu->addCheckBox( "Show Line Numbers" )->setActive( mConfig.editor.showLineNumbers ); - mViewMenu->addCheckBox( "Show White Space" )->setActive( mConfig.editor.showWhiteSpaces ); - mViewMenu->addCheckBox( "Show Document Info" )->setActive( mConfig.editor.showDocInfo ); - mViewMenu->addCheckBox( "Highlight Matching Bracket" ) - ->setActive( mConfig.editor.highlightMatchingBracket ); - mViewMenu->addCheckBox( "Highlight Current Line" ) - ->setActive( mConfig.editor.highlightCurrentLine ); - mViewMenu->addCheckBox( "Highlight Selection Match" ) - ->setActive( mConfig.editor.highlightSelectionMatch ); - mViewMenu->addCheckBox( "Enable Horizontal ScrollBar" ) - ->setActive( mConfig.editor.horizontalScrollbar ); - mViewMenu->addCheckBox( "Enable Color Preview" ) - ->setActive( mConfig.editor.colorPreview ) - ->setTooltipText( "Enables a quick preview of a color when the mouse\n" - "is hover a word that represents a color." ); - mViewMenu->addCheckBox( "Enable Color Picker" ) - ->setActive( mConfig.editor.colorPickerSelection ) - ->setTooltipText( "Enables the color picker tool when a double click selection\n" - "is done over a word representing a color." ); - mViewMenu->addCheckBox( "Enable Auto Complete" ) - ->setActive( mConfig.editor.autoComplete ) - ->setTooltipText( "Auto complete shows the completion popup as you type, so you can fill\n" - "in long words by typing only a few characters." ); - mViewMenu->add( "Line Breaking Column" ); - mViewMenu->addSeparator(); - mViewMenu->add( "UI Scale Factor (Pixel Density)", findIcon( "pixel-density" ) ); - mViewMenu->add( "UI Font Size", findIcon( "font-size" ) ); - mViewMenu->add( "Editor Font Size", findIcon( "font-size" ) ); - mViewMenu->addSeparator(); - mViewMenu->addCheckBox( "Full Screen Mode" ) +void App::showSidePanel( bool show ) { + if ( show == mSidePanel->isVisible() ) + return; + + if ( show ) { + mSidePanel->setVisible( true ); + mSidePanel->setParent( mProjectSplitter ); + mProjectSplitter->swap(); + } else { + mSidePanel->setVisible( false ); + mSidePanel->setParent( mUISceneNode->getRoot() ); + } +} + +UIMenu* App::createWindowMenu() { + mWindowMenu = UIPopUpMenu::New(); + mWindowMenu->add( "UI Scale Factor (Pixel Density)", findIcon( "pixel-density" ) ); + mWindowMenu->add( "UI Font Size", findIcon( "font-size" ) ); + mWindowMenu->add( "Editor Font Size", findIcon( "font-size" ) ); + mWindowMenu->addSeparator(); + mWindowMenu->addCheckBox( "Full Screen Mode" ) ->setShortcutText( getKeybind( "fullscreen-toggle" ) ) ->setId( "fullscreen-mode" ); - mViewMenu->addSeparator(); - mViewMenu->add( "Split Left", findIcon( "split-horizontal" ), getKeybind( "split-left" ) ); - mViewMenu->add( "Split Right", findIcon( "split-horizontal" ), getKeybind( "split-right" ) ); - mViewMenu->add( "Split Top", findIcon( "split-vertical" ), getKeybind( "split-top" ) ); - mViewMenu->add( "Split Bottom", findIcon( "split-vertical" ), getKeybind( "split-bottom" ) ); - mViewMenu->addSeparator(); - mViewMenu->add( "Zoom In", findIcon( "zoom-in" ), getKeybind( "font-size-grow" ) ); - mViewMenu->add( "Zoom Out", findIcon( "zoom-out" ), getKeybind( "font-size-shrink" ) ); - mViewMenu->add( "Zoom Reset", findIcon( "zoom-reset" ), getKeybind( "font-size-reset" ) ); - mViewMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) { + mWindowMenu->addCheckBox( "Show Side Panel" )->setActive( mConfig.ui.showSidePanel ); + mWindowMenu->addSeparator(); + mWindowMenu->add( "Split Left", findIcon( "split-horizontal" ), getKeybind( "split-left" ) ); + mWindowMenu->add( "Split Right", findIcon( "split-horizontal" ), getKeybind( "split-right" ) ); + mWindowMenu->add( "Split Top", findIcon( "split-vertical" ), getKeybind( "split-top" ) ); + mWindowMenu->add( "Split Bottom", findIcon( "split-vertical" ), getKeybind( "split-bottom" ) ); + mWindowMenu->addSeparator(); + mWindowMenu->add( "Zoom In", findIcon( "zoom-in" ), getKeybind( "font-size-grow" ) ); + mWindowMenu->add( "Zoom Out", findIcon( "zoom-out" ), getKeybind( "font-size-shrink" ) ); + mWindowMenu->add( "Zoom Reset", findIcon( "zoom-reset" ), getKeybind( "font-size-reset" ) ); + mWindowMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) { if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) return; UIMenuItem* item = event->getNode()->asType(); - if ( item->getText() == "Show Line Numbers" ) { - mConfig.editor.showLineNumbers = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setShowLineNumber( mConfig.editor.showLineNumbers ); - } ); - } else if ( item->getText() == "Show White Space" ) { - mConfig.editor.showWhiteSpaces = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setShowWhitespaces( mConfig.editor.showWhiteSpaces ); - } ); - } else if ( item->getText() == "Highlight Matching Bracket" ) { - mConfig.editor.highlightMatchingBracket = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setHighlightMatchingBracket( mConfig.editor.highlightMatchingBracket ); - } ); - } else if ( item->getText() == "Highlight Current Line" ) { - mConfig.editor.highlightCurrentLine = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setHighlightCurrentLine( mConfig.editor.highlightCurrentLine ); - } ); - } else if ( item->getText() == "Highlight Selection Match" ) { - mConfig.editor.highlightSelectionMatch = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setHighlightSelectionMatch( mConfig.editor.highlightSelectionMatch ); - } ); - } else if ( item->getText() == "Enable Horizontal ScrollBar" ) { - mConfig.editor.horizontalScrollbar = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setHorizontalScrollBarEnabled( mConfig.editor.horizontalScrollbar ); - } ); - } else if ( item->getText() == "Enable Color Picker" ) { - mConfig.editor.colorPickerSelection = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setEnableColorPickerOnSelection( mConfig.editor.colorPickerSelection ); - } ); - } else if ( item->getText() == "Enable Auto Complete" ) { - setAutoComplete( item->asType()->isActive() ); - } else if ( item->getText() == "Enable Color Preview" ) { - mConfig.editor.colorPreview = item->asType()->isActive(); - mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { - editor->setEnableColorPickerOnSelection( mConfig.editor.colorPreview ); - } ); - } else if ( item->getText() == "Show Document Info" ) { - mConfig.editor.showDocInfo = item->asType()->isActive(); - if ( mDocInfo ) - mDocInfo->setVisible( mConfig.editor.showDocInfo ); - if ( mEditorSplitter->getCurEditor() ) - updateDocInfo( mEditorSplitter->getCurEditor()->getDocument() ); + if ( item->getText() == "Show Side Panel" ) { + mConfig.ui.showSidePanel = item->asType()->isActive(); + showSidePanel( mConfig.ui.showSidePanel ); } else if ( item->getText() == "UI Scale Factor (Pixel Density)" ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, "Set the UI scale factor (pixel density):\nMinimum value is " @@ -712,6 +663,104 @@ UIMenu* App::createViewMenu() { msgBox->closeWindow(); } ); setFocusEditorOnClose( msgBox ); + } else if ( "Zoom In" == item->getText() ) { + mEditorSplitter->zoomIn(); + } else if ( "Zoom Out" == item->getText() ) { + mEditorSplitter->zoomOut(); + } else if ( "Zoom Reset" == item->getText() ) { + mEditorSplitter->zoomReset(); + } else if ( "Full Screen Mode" == item->getText() ) { + runCommand( "fullscreen-toggle" ); + } else { + String text = String( event->getNode()->asType()->getText() ).toLower(); + String::replaceAll( text, " ", "-" ); + String::replaceAll( text, "/", "-" ); + runCommand( text ); + } + } ); + return mWindowMenu; +} + +UIMenu* App::createViewMenu() { + mViewMenu = UIPopUpMenu::New(); + mViewMenu->addCheckBox( "Show Line Numbers" )->setActive( mConfig.editor.showLineNumbers ); + mViewMenu->addCheckBox( "Show White Space" )->setActive( mConfig.editor.showWhiteSpaces ); + mViewMenu->addCheckBox( "Show Document Info" )->setActive( mConfig.editor.showDocInfo ); + mViewMenu->addCheckBox( "Highlight Matching Bracket" ) + ->setActive( mConfig.editor.highlightMatchingBracket ); + mViewMenu->addCheckBox( "Highlight Current Line" ) + ->setActive( mConfig.editor.highlightCurrentLine ); + mViewMenu->addCheckBox( "Highlight Selection Match" ) + ->setActive( mConfig.editor.highlightSelectionMatch ); + mViewMenu->addCheckBox( "Enable Horizontal ScrollBar" ) + ->setActive( mConfig.editor.horizontalScrollbar ); + mViewMenu->addCheckBox( "Enable Color Preview" ) + ->setActive( mConfig.editor.colorPreview ) + ->setTooltipText( "Enables a quick preview of a color when the mouse\n" + "is hover a word that represents a color." ); + mViewMenu->addCheckBox( "Enable Color Picker" ) + ->setActive( mConfig.editor.colorPickerSelection ) + ->setTooltipText( "Enables the color picker tool when a double click selection\n" + "is done over a word representing a color." ); + mViewMenu->addCheckBox( "Enable Auto Complete" ) + ->setActive( mConfig.editor.autoComplete ) + ->setTooltipText( "Auto complete shows the completion popup as you type, so you can fill\n" + "in long words by typing only a few characters." ); + mViewMenu->add( "Line Breaking Column" ); + + mViewMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) { + if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) + return; + UIMenuItem* item = event->getNode()->asType(); + if ( item->getText() == "Show Line Numbers" ) { + mConfig.editor.showLineNumbers = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setShowLineNumber( mConfig.editor.showLineNumbers ); + } ); + } else if ( item->getText() == "Show White Space" ) { + mConfig.editor.showWhiteSpaces = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setShowWhitespaces( mConfig.editor.showWhiteSpaces ); + } ); + } else if ( item->getText() == "Highlight Matching Bracket" ) { + mConfig.editor.highlightMatchingBracket = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setHighlightMatchingBracket( mConfig.editor.highlightMatchingBracket ); + } ); + } else if ( item->getText() == "Highlight Current Line" ) { + mConfig.editor.highlightCurrentLine = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setHighlightCurrentLine( mConfig.editor.highlightCurrentLine ); + } ); + } else if ( item->getText() == "Highlight Selection Match" ) { + mConfig.editor.highlightSelectionMatch = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setHighlightSelectionMatch( mConfig.editor.highlightSelectionMatch ); + } ); + } else if ( item->getText() == "Enable Horizontal ScrollBar" ) { + mConfig.editor.horizontalScrollbar = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setHorizontalScrollBarEnabled( mConfig.editor.horizontalScrollbar ); + } ); + } else if ( item->getText() == "Enable Color Picker" ) { + mConfig.editor.colorPickerSelection = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setEnableColorPickerOnSelection( mConfig.editor.colorPickerSelection ); + } ); + } else if ( item->getText() == "Enable Auto Complete" ) { + setAutoComplete( item->asType()->isActive() ); + } else if ( item->getText() == "Enable Color Preview" ) { + mConfig.editor.colorPreview = item->asType()->isActive(); + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setEnableColorPickerOnSelection( mConfig.editor.colorPreview ); + } ); + } else if ( item->getText() == "Show Document Info" ) { + mConfig.editor.showDocInfo = item->asType()->isActive(); + if ( mDocInfo ) + mDocInfo->setVisible( mConfig.editor.showDocInfo ); + if ( mEditorSplitter->getCurEditor() ) + updateDocInfo( mEditorSplitter->getCurEditor()->getDocument() ); + } else if ( item->getText() == "Line Breaking Column" ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, "Set Line Breaking Column:\n" @@ -732,14 +781,6 @@ UIMenu* App::createViewMenu() { } } ); setFocusEditorOnClose( msgBox ); - } else if ( "Zoom In" == item->getText() ) { - mEditorSplitter->zoomIn(); - } else if ( "Zoom Out" == item->getText() ) { - mEditorSplitter->zoomOut(); - } else if ( "Zoom Reset" == item->getText() ) { - mEditorSplitter->zoomReset(); - } else if ( "Full Screen Mode" == item->getText() ) { - runCommand( "fullscreen-toggle" ); } else { String text = String( event->getNode()->asType()->getText() ).toLower(); String::replaceAll( text, " ", "-" ); @@ -1009,7 +1050,7 @@ void App::updateDocInfo( TextDocument& doc ) { if ( mConfig.editor.showDocInfo && mDocInfoText ) { mDocInfoText->setText( String::format( "line: %lld / %lu col: %lld %s", doc.getSelection().start().line() + 1, - doc.linesCount(), doc.getSelection().start().column() + 1, + doc.linesCount(), doc.getSelection().start().column(), doc.getLineEnding() == TextDocument::LineEnding::LF ? "LF" : "CRLF" ) ); } } @@ -1115,7 +1156,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { doc.setCommand( "close-app", [&] { closeApp(); } ); doc.setCommand( "fullscreen-toggle", [&]() { mWindow->toggleFullscreen(); - mViewMenu->find( "fullscreen-mode" ) + mWindowMenu->find( "fullscreen-mode" ) ->asType() ->setActive( !mWindow->isWindowed() ); } ); @@ -1208,6 +1249,7 @@ void App::createSettingsMenu() { mSettingsMenu->addSubMenu( "Document", nullptr, createDocumentMenu() ); mSettingsMenu->addSubMenu( "Edit", nullptr, createEditMenu() ); mSettingsMenu->addSubMenu( "View", nullptr, createViewMenu() ); + mSettingsMenu->addSubMenu( "Window", nullptr, createWindowMenu() ); mSettingsMenu->addSeparator(); mSettingsMenu->add( "Close", findIcon( "document-close" ), getKeybind( "close-doc" ) ); mSettingsMenu->addSeparator(); @@ -1528,8 +1570,13 @@ void App::init( const std::string& file, const Float& pidelDensity ) { mUISceneNode->bind( "search_bar", mSearchBarLayout ); mUISceneNode->bind( "doc_info", mDocInfo ); mUISceneNode->bind( "doc_info_text", mDocInfoText ); + mUISceneNode->bind( "panel", mSidePanel ); + mUISceneNode->bind( "project_splitter", mProjectSplitter ); mDocInfo->setVisible( mConfig.editor.showDocInfo ); mSearchBarLayout->setVisible( false )->setEnabled( false ); + mProjectSplitter->setSplitPartition( StyleSheetLength( mConfig.window.panelPartition ) ); + if ( !mConfig.ui.showSidePanel ) + showSidePanel( mConfig.ui.showSidePanel ); mEditorSplitter = UICodeEditorSplitter::New( this, mUISceneNode, @@ -1544,13 +1591,12 @@ void App::init( const std::string& file, const Float& pidelDensity ) { 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}, + FileSystemModel::Permissions, FileSystemModel::ModificationTime, + FileSystemModel::Path}, true ); tree->setHeadersVisible( false ); tree->addEventListener( Event::OnModelEvent, [&]( const Event* event ) { @@ -1558,8 +1604,8 @@ void App::init( const std::string& file, const Float& pidelDensity ) { 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() ); + if ( vPath.isValid() && vPath.is( Variant::Type::cstr ) ) { + std::string path( vPath.asCStr() ); UITab* tab = mEditorSplitter->isDocumentOpen( path ); if ( !tab ) { mEditorSplitter->loadFileFromPathInNewTab( path ); diff --git a/src/tools/codeeditor/codeeditor.hpp b/src/tools/codeeditor/codeeditor.hpp index b55d9d64a..e5c3e2ba1 100644 --- a/src/tools/codeeditor/codeeditor.hpp +++ b/src/tools/codeeditor/codeeditor.hpp @@ -37,6 +37,7 @@ class UISearchBar : public UILinearLayout { struct UIConfig { StyleSheetLength fontSize{12, StyleSheetLength::Dp}; + bool showSidePanel{true}; }; struct WindowConfig { @@ -44,6 +45,7 @@ struct WindowConfig { Sizei size{1280, 720}; std::string winIcon; bool maximized{false}; + std::string panelPartition; }; struct CodeEditorConfig { @@ -148,6 +150,9 @@ class App : public UICodeEditorSplitter::Client { AppConfig mConfig; UIPopUpMenu* mDocMenu{nullptr}; UIPopUpMenu* mViewMenu{nullptr}; + UIPopUpMenu* mWindowMenu{nullptr}; + UISplitter* mProjectSplitter{nullptr}; + UITabWidget* mSidePanel{nullptr}; UICodeEditorSplitter* mEditorSplitter{nullptr}; std::string mInitColorScheme; std::map mKeybindings; @@ -159,6 +164,8 @@ class App : public UICodeEditorSplitter::Client { std::string mResPath; AutoCompleteModule* mAutoCompleteModule{nullptr}; + void showSidePanel( bool show ); + void onFileDropped( String file ); void onTextDropped( String text ); @@ -195,6 +202,8 @@ class App : public UICodeEditorSplitter::Client { UIMenu* createEditMenu(); + UIMenu* createWindowMenu(); + Drawable* findIcon( const std::string& name ); UIMenu* createDocumentMenu();