From 8f430076ef7d6a0ff41b243845e448ccfbf8337f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 13 Aug 2020 02:22:17 -0300 Subject: [PATCH] Added SortingProxyModel to allow sorting tables by column. Some minor improvements to ecode. Added "delete-current-line" to text document. --- bin/assets/ui/breeze.css | 4 + include/eepp/scene/event.hpp | 1 + include/eepp/ui.hpp | 1 + .../eepp/ui/abstract/uiabstracttableview.hpp | 2 + include/eepp/ui/doc/textdocument.hpp | 2 + include/eepp/ui/models/model.hpp | 12 ++ include/eepp/ui/models/modelselection.hpp | 16 +- include/eepp/ui/models/sortingproxymodel.hpp | 69 ++++++++ include/eepp/ui/models/variant.hpp | 112 +++++++++++++ include/eepp/ui/uitableheadercolumn.hpp | 7 +- projects/linux/ee.files | 2 + src/eepp/ui/abstract/uiabstracttableview.cpp | 23 +++ src/eepp/ui/doc/textdocument.cpp | 17 ++ src/eepp/ui/models/model.cpp | 10 ++ src/eepp/ui/models/modelselection.cpp | 13 +- src/eepp/ui/models/sortingproxymodel.cpp | 156 ++++++++++++++++++ src/eepp/ui/uicodeeditor.cpp | 2 +- src/eepp/ui/uiimage.cpp | 1 + src/eepp/ui/uipushbutton.cpp | 7 +- src/eepp/ui/uitableheadercolumn.cpp | 26 +++ src/eepp/ui/uitableview.cpp | 3 - src/eepp/ui/uitreeview.cpp | 3 - src/tests/ui_perf_test/ui_perf_test.cpp | 10 +- src/tools/codeeditor/codeeditor.cpp | 5 + 24 files changed, 485 insertions(+), 19 deletions(-) create mode 100644 include/eepp/ui/models/sortingproxymodel.hpp create mode 100644 src/eepp/ui/models/sortingproxymodel.cpp diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index c3ae979d6..d5b0fc27b 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -786,6 +786,10 @@ TableView > ScrollBar { background-color: var(--list-back); } +table::header::column::arrow { + margin-left: 2dp; +} + .appbackground { background-color: var(--back); } diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index 53dff7265..cd5c265e1 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -74,6 +74,7 @@ class EE_API Event { OnNodeDropped, OnSave, OnModelEvent, + OnResourceChange, UserEvent, NoEvent = eeINDEX_NOT_FOUND }; diff --git a/include/eepp/ui.hpp b/include/eepp/ui.hpp index bab5dbf17..6f735db10 100644 --- a/include/eepp/ui.hpp +++ b/include/eepp/ui.hpp @@ -76,6 +76,7 @@ #include #include #include +#include #include #endif diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 770fab763..c29794972 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -125,6 +125,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { virtual void onOpenModelIndex( const ModelIndex& index ); + virtual void onSortColumn( const size_t& colIndex ); + void updateHeaderSize(); int visibleColumn(); diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index fcb42ac63..b96fad5d0 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -189,6 +189,8 @@ class EE_API TextDocument { void deleteToNextWord(); + void deleteCurrentLine(); + void selectToPreviousChar(); void selectToNextChar(); diff --git a/include/eepp/ui/models/model.hpp b/include/eepp/ui/models/model.hpp index b2c721f96..5f441115d 100644 --- a/include/eepp/ui/models/model.hpp +++ b/include/eepp/ui/models/model.hpp @@ -22,6 +22,13 @@ enum class SortOrder { None, Ascending, Descending }; class EE_API Model { public: + class Client { + public: + virtual ~Client() {} + + virtual void onModelUpdated( unsigned flags ) = 0; + }; + enum UpdateFlag { DontInvalidateIndexes = 0, InvalidateAllIndexes = 1 << 0, @@ -75,6 +82,10 @@ class EE_API Model { void unregisterView( UIAbstractView* ); + void registerClient( Client* ); + + void unregisterClient( Client* ); + void refreshView(); void setOnUpdate( const std::function& onUpdate ); @@ -91,6 +102,7 @@ class EE_API Model { private: std::unordered_set mViews; + std::unordered_set mClients; std::function mOnUpdate; }; diff --git a/include/eepp/ui/models/modelselection.hpp b/include/eepp/ui/models/modelselection.hpp index 0841fe8ee..06b3924dc 100644 --- a/include/eepp/ui/models/modelselection.hpp +++ b/include/eepp/ui/models/modelselection.hpp @@ -62,11 +62,25 @@ class EE_API ModelSelection { void removeMatching( std::function ); + template void changeFromModel( Function f ) { + { + mDisableNotify = true; + mNotifyPending = false; + f( *this ); + mDisableNotify = false; + } + if ( mNotifyPending ) + notifySelectionChanged(); + } + protected: UIAbstractView* mView; std::vector mIndexes; + bool mDisableNotify{false}; + bool mNotifyPending{false}; + void notifySelectionChanged(); }; -}}} // namespace EE::UI::Model +}}} // namespace EE::UI::Models #endif // EE_UI_MODEL_MODELSELECTION_HPP diff --git a/include/eepp/ui/models/sortingproxymodel.hpp b/include/eepp/ui/models/sortingproxymodel.hpp new file mode 100644 index 000000000..3259d4d86 --- /dev/null +++ b/include/eepp/ui/models/sortingproxymodel.hpp @@ -0,0 +1,69 @@ +#ifndef EE_UI_MODELS_SORTINGPROXYMODEL_HPP +#define EE_UI_MODELS_SORTINGPROXYMODEL_HPP + +#include +#include + +namespace EE { namespace UI { namespace Models { + +class EE_API SortingProxyModel final : public Model, private Model::Client { + public: + template + static std::shared_ptr New( std::shared_ptr model ) { + return std::shared_ptr( + new SortingProxyModel( std::static_pointer_cast( model ) ) ); + } + + virtual ~SortingProxyModel(); + + virtual size_t rowCount( const ModelIndex& = ModelIndex() ) const; + + virtual size_t columnCount( const ModelIndex& = ModelIndex() ) const; + + virtual std::string columnName( const size_t& ) const; + + virtual Variant data( const ModelIndex&, Role = Role::Display ) const; + + virtual void update(); + + virtual int keyColumn() const; + + virtual SortOrder sortOrder() const; + + virtual void setKeyColumnAndSortOrder( const size_t&, const SortOrder& ); + + virtual bool isColumnSortable( const size_t& columnIndex ) const; + + ModelIndex mapToTarget( const ModelIndex& ) const; + + Role sortRole() const; + + void setSortRrole( Role role ); + + private: + SortingProxyModel( std::shared_ptr ); + + virtual void onModelUpdated( unsigned ); + + Model& target(); + + const Model& target() const; + + void resort( unsigned flags = Model::UpdateFlag::DontInvalidateIndexes ); + + void setSortingCaseSensitive( bool b ); + + bool isSortingCaseSensitive(); + + std::shared_ptr mTarget; + std::vector mRowMappings; + int mKeyColumn{-1}; + SortOrder mSortOrder{SortOrder::Ascending}; + Role mSortRole{Role::Sort}; + bool mSortingCaseSensitive{false}; + bool mSorting{false}; +}; + +}}} // namespace EE::UI::Models + +#endif // EE_UI_MODELS_SORTINGPROXYMODEL_HPP diff --git a/include/eepp/ui/models/variant.hpp b/include/eepp/ui/models/variant.hpp index 3b2623062..99bc2155e 100644 --- a/include/eepp/ui/models/variant.hpp +++ b/include/eepp/ui/models/variant.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -57,12 +58,14 @@ class EE_API Variant { const bool& asBool() const { return mValue.asBool; } const Float& asFloat() const { return mValue.asFloat; } const int& asInt() const { return mValue.asInt; } + const unsigned int& asUint() const { return mValue.asUint; } const Int64& asInt64() const { return mValue.asInt64; } const Uint64& asUint64() const { return mValue.asUint64; } const Vector2f& asVector2f() const { return *mValue.asVector2f; } const Rectf& asRectf() const { return *mValue.asRectf; } const char* asCStr() const { return mValue.asCStr; } UIIcon* asIcon() const { return mValue.asIcon; } + void* asDataPtr() const { return mValue.asDataPtr; } bool is( const Type& type ) const { return type == mType; } void reset() { switch ( mType ) { @@ -86,6 +89,115 @@ class EE_API Variant { } bool isValid() { return mType != Type::Invalid; } + std::string toString() const { + switch ( mType ) { + case Type::Bool: + return asBool() ? "true" : "false"; + case Type::Int: + return String::toString( asInt() ); + case Type::Uint: + return String::toString( asUint() ); + case Type::Int64: + return String::toString( asInt64() ); + case Type::Uint64: + return String::toString( asUint64() ); + case Type::Float: + return String::toString( asFloat() ); + case Type::String: + return asString(); + case Type::Drawable: + return asDrawable()->isDrawableResource() + ? static_cast( asDrawable() )->getName() + : "Drawable"; + case Type::Icon: + return asIcon()->getName(); + case Type::DataPtr: + return String::format( "%p", asDataPtr() ); + case Type::Vector2f: + return String::format( "%.2f-%.2f", asVector2f().x, asVector2f().y ); + case Type::Rectf: + return String::format( "%.2f-%.2f-%.2f-%.2f", asRectf().Top, asRectf().Right, + asRectf().Bottom, asRectf().Left ); + case Type::cstr: + return asCStr(); + case Type::Invalid: + break; + } + return ""; + } + + bool operator<( const Variant& other ) const { + if ( mType != other.mType ) + return toString() < other.toString(); + switch ( mType ) { + case Type::Bool: + return asBool() < other.asBool(); + case Type::Int: + return asInt() < other.asInt(); + case Type::Uint: + return asUint() < other.asUint(); + case Type::Int64: + return asInt64() < other.asInt64(); + case Type::Uint64: + return asUint64() < other.asUint64(); + case Type::Float: + return asFloat() < other.asFloat(); + case Type::String: + return asString() < other.asString(); + case Type::Drawable: + return asDrawable() < other.asDrawable(); + case Type::Icon: + return asIcon() < other.asIcon(); + case Type::DataPtr: + return asDataPtr() < other.asDataPtr(); + case Type::Vector2f: + return asVector2f() < other.asVector2f(); + case Type::Rectf: + return asRectf().getSize() < other.asRectf().getSize(); + case Type::cstr: + return strcmp( asCStr(), other.asCStr() ) < 0; + case Type::Invalid: + break; + } + return false; + } + + bool operator==( const Variant& other ) const { + if ( mType != other.mType ) + return toString() == other.toString(); + switch ( mType ) { + case Type::Bool: + return asBool() == other.asBool(); + case Type::Int: + return asInt() == other.asInt(); + case Type::Uint: + return asUint() == other.asUint(); + case Type::Int64: + return asInt64() == other.asInt64(); + case Type::Uint64: + return asUint64() == other.asUint64(); + case Type::Float: + return asFloat() == other.asFloat(); + case Type::String: + return asString() == other.asString(); + case Type::Drawable: + return asDrawable() == other.asDrawable(); + case Type::Icon: + return asIcon() == other.asIcon(); + case Type::DataPtr: + return asDataPtr() == other.asDataPtr(); + case Type::Vector2f: + return asVector2f() == other.asVector2f(); + case Type::Rectf: + return asRectf().getSize() == other.asRectf().getSize(); + case Type::cstr: + return strcmp( asCStr(), other.asCStr() ) == 0; + case Type::Invalid: + break; + } + return false; + } + private: union { void* asDataPtr; diff --git a/include/eepp/ui/uitableheadercolumn.hpp b/include/eepp/ui/uitableheadercolumn.hpp index d75932bec..d248ea1d5 100644 --- a/include/eepp/ui/uitableheadercolumn.hpp +++ b/include/eepp/ui/uitableheadercolumn.hpp @@ -14,9 +14,12 @@ class EE_API UITableHeaderColumn : public UIPushButton { public: UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex ); + virtual UIWidget* getExtraInnerWidget() const; + protected: UIAbstractTableView* mView; size_t mColIndex; + mutable UIImage* mImage{nullptr}; Uint32 onCalculateDrag( const Vector2f& position, const Uint32& flags ); @@ -28,11 +31,13 @@ class EE_API UITableHeaderColumn : public UIPushButton { Uint32 onMouseMove( const Vector2i& position, const Uint32& flags ); + Uint32 onMouseClick( const Vector2i& position, const Uint32& flags ); + Uint32 onMouseDoubleClick( const Vector2i& position, const Uint32& flags ); Uint32 onDragStop( const Vector2i& pos, const Uint32& flags ); }; -}} +}} // namespace EE::UI #endif // EE_UI_UITABLEHEADERCOLUMN_HPP diff --git a/projects/linux/ee.files b/projects/linux/ee.files index a23b40d43..71c34d65d 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -343,6 +343,7 @@ ../../include/eepp/ui/models/model.hpp ../../include/eepp/ui/models/modelindex.hpp ../../include/eepp/ui/models/modelselection.hpp +../../include/eepp/ui/models/sortingproxymodel.hpp ../../include/eepp/ui/models/widgettreemodel.hpp ../../include/eepp/ui/tools/textureatlaseditor.hpp ../../include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -814,6 +815,7 @@ ../../src/eepp/ui/models/filesystemmodel.cpp ../../src/eepp/ui/models/model.cpp ../../src/eepp/ui/models/modelselection.cpp +../../src/eepp/ui/models/sortingproxymodel.cpp ../../src/eepp/ui/models/widgettreemodel.cpp ../../src/eepp/ui/tools/textureatlaseditor.cpp ../../src/eepp/ui/tools/textureatlasnew.cpp diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index c76333984..815c9f0c1 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -399,4 +400,26 @@ void UIAbstractTableView::onOpenModelIndex( const ModelIndex& index ) { sendEvent( &event ); } +void UIAbstractTableView::onSortColumn( const size_t& colIndex ) { + Model* model = getModel(); + if ( !model ) + return; + if ( model->isColumnSortable( colIndex ) ) { + if ( -1 != model->keyColumn() && (Int64)colIndex != model->keyColumn() && + columnData( model->keyColumn() ).widget ) { + UIImage* image = + columnData( model->keyColumn() ).widget->getExtraInnerWidget()->asType(); + image->setDrawable( nullptr ); + } + SortOrder sortOrder = model->sortOrder() == SortOrder::Ascending ? SortOrder::Descending + : SortOrder::Ascending; + Drawable* icon = mUISceneNode->findIconDrawable( + sortOrder == SortOrder::Ascending ? "arrow-down" : "arrow-up", mIconSize ); + UIImage* image = columnData( colIndex ).widget->getExtraInnerWidget()->asType(); + if ( image && icon ) + image->setDrawable( icon ); + model->setKeyColumnAndSortOrder( colIndex, sortOrder ); + } +} + }}} // namespace EE::UI::Abstract diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 1fe3e2227..622854eab 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -841,6 +841,22 @@ void TextDocument::deleteToNextWord() { deleteTo( nextWordBoundary( getSelection().start() ) ); } +void TextDocument::deleteCurrentLine() { + if ( hasSelection() ) { + deleteSelection(); + return; + } + if ( getSelection().start().line() + 1 >= (Int64)linesCount() ) { + remove( {startOfLine( getSelection().start() ), + startOfLine( {getSelection().start().line() - 1, 0} )} ); + setSelection( startOfLine( getSelection().start() ) ); + } else { + remove( {startOfLine( getSelection().start() ), + startOfLine( {getSelection().start().line() + 1, 0} )} ); + setSelection( startOfLine( getSelection().start() ) ); + } +} + void TextDocument::selectToPreviousChar() { selectTo( -1 ); } @@ -1366,6 +1382,7 @@ void TextDocument::initializeCommands() { mCommands["delete-to-previous-char"] = [&] { deleteToPreviousChar(); }; mCommands["delete-to-next-word"] = [&] { deleteToNextWord(); }; mCommands["delete-to-next-char"] = [&] { deleteToNextChar(); }; + mCommands["delete-current-line"] = [&] { deleteCurrentLine(); }; mCommands["delete-selection"] = [&] { deleteSelection(); }; mCommands["move-to-previous-char"] = [&] { moveToPreviousChar(); }; mCommands["move-to-previous-word"] = [&] { moveToPreviousWord(); }; diff --git a/src/eepp/ui/models/model.cpp b/src/eepp/ui/models/model.cpp index c51a4aae1..d0674f546 100644 --- a/src/eepp/ui/models/model.cpp +++ b/src/eepp/ui/models/model.cpp @@ -6,6 +6,8 @@ namespace EE { namespace UI { namespace Models { void Model::onModelUpdate( unsigned flags ) { if ( mOnUpdate ) mOnUpdate(); + for ( auto client : mClients ) + client->onModelUpdated( flags ); forEachView( [&]( UIAbstractView* view ) { view->onModelUpdate( flags ); } ); } @@ -18,6 +20,14 @@ void Model::unregisterView( UIAbstractView* view ) { mViews.erase( view ); } +void Model::registerClient( Model::Client* client ) { + mClients.insert( client ); +} + +void Model::unregisterClient( Model::Client* client ) { + mClients.erase( client ); +} + void Model::refreshView() { forEachView( [&]( UIAbstractView* view ) { view->invalidateDraw(); } ); } diff --git a/src/eepp/ui/models/modelselection.cpp b/src/eepp/ui/models/modelselection.cpp index 96664530f..fa4db83b4 100644 --- a/src/eepp/ui/models/modelselection.cpp +++ b/src/eepp/ui/models/modelselection.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include namespace EE { namespace UI { namespace Models { @@ -59,4 +59,13 @@ void ModelSelection::clear() { mView->notifySelectionChange(); } -}}} // namespace EE::UI::Model +void ModelSelection::notifySelectionChanged() { + if ( !mDisableNotify ) { + mView->notifySelectionChange(); + mNotifyPending = false; + } else { + mNotifyPending = true; + } +} + +}}} // namespace EE::UI::Models diff --git a/src/eepp/ui/models/sortingproxymodel.cpp b/src/eepp/ui/models/sortingproxymodel.cpp new file mode 100644 index 000000000..7a914cd5e --- /dev/null +++ b/src/eepp/ui/models/sortingproxymodel.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include + +using namespace EE::UI::Abstract; + +namespace EE { namespace UI { namespace Models { + +SortingProxyModel::SortingProxyModel( std::shared_ptr target ) : + mTarget( target ), mKeyColumn( -1 ) { + mTarget->registerClient( this ); + resort(); +} + +SortingProxyModel::~SortingProxyModel() { + mTarget->unregisterClient( this ); +} + +void SortingProxyModel::onModelUpdated( unsigned flags ) { + resort( flags ); +} + +Model& SortingProxyModel::target() { + return *mTarget; +} + +const Model& SortingProxyModel::target() const { + return *mTarget; +} + +size_t SortingProxyModel::rowCount( const ModelIndex& index ) const { + auto targetIndex = mapToTarget( index ); + return target().rowCount( targetIndex ); +} + +size_t SortingProxyModel::columnCount( const ModelIndex& index ) const { + auto targetIndex = mapToTarget( index ); + return target().columnCount( targetIndex ); +} + +ModelIndex SortingProxyModel::mapToTarget( const ModelIndex& index ) const { + if ( !index.isValid() ) + return {}; + if ( static_cast( index.row() ) >= mRowMappings.size() || + static_cast( index.column() ) >= columnCount() ) + return {}; + return target().index( mRowMappings[index.row()], index.column() ); +} + +Model::Role SortingProxyModel::sortRole() const { + return mSortRole; +} + +void SortingProxyModel::setSortRrole( Model::Role role ) { + mSortRole = role; +} + +std::string SortingProxyModel::columnName( const size_t& column ) const { + return target().columnName( column ); +} + +Variant SortingProxyModel::data( const ModelIndex& index, Role role ) const { + auto targetIndex = mapToTarget( index ); + eeASSERT( targetIndex.isValid() ); + return target().data( targetIndex, role ); +} + +void SortingProxyModel::update() { + target().update(); +} + +int SortingProxyModel::keyColumn() const { + return mKeyColumn; +} + +SortOrder SortingProxyModel::sortOrder() const { + return mSortOrder; +} + +void SortingProxyModel::setKeyColumnAndSortOrder( const size_t& column, + const SortOrder& sortOrder ) { + if ( column == (size_t)mKeyColumn && sortOrder == mSortOrder ) + return; + eeASSERT( column >= 0 && column < columnCount() ); + mKeyColumn = column; + mSortOrder = sortOrder; + resort(); +} + +void SortingProxyModel::resort( unsigned flags ) { + mSorting = true; + auto old_row_mappings = mRowMappings; + int rowCount = target().rowCount(); + mRowMappings.resize( rowCount ); + for ( int i = 0; i < rowCount; ++i ) + mRowMappings[i] = i; + if ( mKeyColumn == -1 ) { + onModelUpdate( flags ); + return; + } + std::sort( mRowMappings.begin(), mRowMappings.end(), [&]( auto row1, auto row2 ) -> bool { + Variant data1 = target().data( target().index( row1, mKeyColumn ), mSortRole ); + Variant data2 = target().data( target().index( row2, mKeyColumn ), mSortRole ); + if ( data1 == data2 ) + return 0; + bool isLessThan; + if ( !mSortingCaseSensitive && data1.is( Variant::Type::String ) && + data2.is( Variant::Type::String ) ) { + isLessThan = String::toLower( data1.asString() ) < String::toLower( data2.asString() ); + } else if ( !mSortingCaseSensitive && data1.is( Variant::Type::cstr ) && + data2.is( Variant::Type::cstr ) ) { + isLessThan = String::toLower( std::string( data1.asCStr() ) ) < + String::toLower( std::string( data2.asCStr() ) ); + } else { + isLessThan = data1 < data2; + } + return mSortOrder == SortOrder::Ascending ? isLessThan : !isLessThan; + } ); + forEachView( [&]( UIAbstractView* view ) { + view->getSelection().changeFromModel( [&]( ModelSelection& selection ) { + std::vector selectedIndexesInTarget; + selection.forEachIndex( [&]( const ModelIndex& index ) { + selectedIndexesInTarget.emplace_back( + target().index( old_row_mappings[index.row()], index.column() ) ); + } ); + + selection.clear(); + for ( auto& index : selectedIndexesInTarget ) { + for ( size_t i = 0; i < mRowMappings.size(); ++i ) { + if ( mRowMappings[i] == index.row() ) { + selection.add( this->index( i, index.column() ) ); + continue; + } + } + } + } ); + } ); + onModelUpdate( flags ); + mSorting = false; +} + +void SortingProxyModel::setSortingCaseSensitive( bool b ) { + mSortingCaseSensitive = b; +} + +bool SortingProxyModel::isSortingCaseSensitive() { + return mSortingCaseSensitive; +} + +bool SortingProxyModel::isColumnSortable( const size_t& columnIndex ) const { + return target().isColumnSortable( columnIndex ); +} + +}}} // namespace EE::UI::Models diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index c5d6094d3..64e365af3 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -34,7 +34,7 @@ const std::map UICodeEditor::getDefaultKeybi {{KEY_BACKSPACE, KEYMOD_SHIFT}, "delete-to-previous-char"}, {{KEY_BACKSPACE, 0}, "delete-to-previous-char"}, {{KEY_DELETE, KEYMOD_CTRL}, "delete-to-next-word"}, - {{KEY_DELETE, KEYMOD_SHIFT}, "delete-to-next-word"}, + {{KEY_DELETE, KEYMOD_SHIFT}, "delete-current-line"}, {{KEY_DELETE, 0}, "delete-to-next-char"}, {{KEY_KP_ENTER, KEYMOD_CTRL | KEYMOD_SHIFT}, "new-line-above"}, {{KEY_RETURN, KEYMOD_CTRL | KEYMOD_SHIFT}, "new-line-above"}, diff --git a/src/eepp/ui/uiimage.cpp b/src/eepp/ui/uiimage.cpp index 10d9fedd5..de1ca8689 100644 --- a/src/eepp/ui/uiimage.cpp +++ b/src/eepp/ui/uiimage.cpp @@ -53,6 +53,7 @@ UIImage* UIImage::setDrawable( Drawable* drawable, bool ownIt ) { mDrawable = drawable; mDrawableOwner = ownIt; + sendCommonEvent( Event::OnResourceChange ); if ( NULL != mDrawable && mDrawable->isDrawableResource() ) { mResourceChangeCb = static_cast( mDrawable ) diff --git a/src/eepp/ui/uipushbutton.cpp b/src/eepp/ui/uipushbutton.cpp index 6420e027a..5e70a0761 100644 --- a/src/eepp/ui/uipushbutton.cpp +++ b/src/eepp/ui/uipushbutton.cpp @@ -144,10 +144,9 @@ Vector2f UIPushButton::packLayout( const std::vector& widgets, const pos[i].y = padding.Top; break; } - if ( 0 == i ) - pos[i].x += widget->getLayoutPixelsMargin().Left; - if ( widget->getPixelsSize().getWidth() > 0 ) { - totSize.x += widget->getLayoutPixelsMargin().Left + widget->getPixelsSize().getWidth(); + pos[i].x += 0 == i || widget->isVisible() ? widget->getLayoutPixelsMargin().Left : 0; + if ( widget->isVisible() && widget->getPixelsSize().getWidth() > 0 ) { + totSize.x += widget->getPixelsSize().getWidth(); if ( ( i + 1 < widgets.size() && widgets[i + 1] && widgets[i + 1]->isVisible() ) || ( i + 2 < widgets.size() && widgets[i + 2] && widgets[i + 2]->isVisible() ) ) { totSize.x += widget->getLayoutPixelsMargin().Right; diff --git a/src/eepp/ui/uitableheadercolumn.cpp b/src/eepp/ui/uitableheadercolumn.cpp index 07e3aabe8..6cc21bd3c 100644 --- a/src/eepp/ui/uitableheadercolumn.cpp +++ b/src/eepp/ui/uitableheadercolumn.cpp @@ -7,6 +7,18 @@ namespace EE { namespace UI { UITableHeaderColumn::UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex ) : UIPushButton( "table::header::column" ), mView( view ), mColIndex( colIndex ) { setDragEnabled( true ); + mInnerWidgetOrientation = InnerWidgetOrientation::Right; + auto cb = [&]( const Event* ) { updateLayout(); }; + mImage = UIImage::NewWithTag( mTag + "::arrow" ); + mImage->setScaleType( UIScaleType::FitInside ) + ->setLayoutSizePolicy( SizePolicy::WrapContent, SizePolicy::WrapContent ) + ->setFlags( UI_VALIGN_CENTER | UI_HALIGN_CENTER ) + ->setParent( const_cast( this ) ); + mImage->setEnabled( false ); + mImage->addEventListener( Event::OnPaddingChange, cb ); + mImage->addEventListener( Event::OnMarginChange, cb ); + mImage->addEventListener( Event::OnSizeChange, cb ); + mImage->addEventListener( Event::OnVisibleChange, cb ); } Uint32 UITableHeaderColumn::onCalculateDrag( const Vector2f& position, const Uint32& flags ) { @@ -41,6 +53,16 @@ Uint32 UITableHeaderColumn::onMouseDown( const Vector2i& position, const Uint32& return Node::onMouseDown( position, flags ); } +Uint32 UITableHeaderColumn::onMouseClick( const Vector2i& position, const Uint32& flags ) { + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + if ( ( flags & EE_BUTTON_LMASK ) && !isDragging() && + localPos.x < mSize.getWidth() - mView->getDragBorderDistance() ) { + mView->onSortColumn( mColIndex ); + return 1; + } + return UIPushButton::onMouseClick( position, flags ); +} + Uint32 UITableHeaderColumn::onDrag( const Vector2f& position, const Uint32&, const Sizef& dragDiff ) { Vector2f localPos( convertToNodeSpace( position ) ); @@ -87,4 +109,8 @@ Uint32 UITableHeaderColumn::onDragStop( const Vector2i& pos, const Uint32& flags return UIPushButton::onDragStop( pos, flags ); } +UIWidget* UITableHeaderColumn::getExtraInnerWidget() const { + return mImage; +} + }} // namespace EE::UI diff --git a/src/eepp/ui/uitableview.cpp b/src/eepp/ui/uitableview.cpp index dee6bc7cc..1e574abbd 100644 --- a/src/eepp/ui/uitableview.cpp +++ b/src/eepp/ui/uitableview.cpp @@ -141,9 +141,6 @@ void UITableView::onColumnSizeChange( const size_t& ) { } Uint32 UITableView::onKeyDown( const KeyEvent& event ) { - if ( event.getMod() != 0 ) - return UIAbstractTableView::onKeyDown( event ); - auto curIndex = getSelection().first(); int pageSize = eefloor( getVisibleArea().getHeight() / getRowHeight() ) - 1; diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index f91ea35a9..8775a8856 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -484,9 +484,6 @@ void UITreeView::setExpanderIconSize( const size_t& expanderSize ) { } Uint32 UITreeView::onKeyDown( const KeyEvent& event ) { - if ( event.getMod() != 0 ) - return UIAbstractTableView::onKeyDown( event ); - auto curIndex = getSelection().first(); switch ( event.getKeyCode() ) { diff --git a/src/tests/ui_perf_test/ui_perf_test.cpp b/src/tests/ui_perf_test/ui_perf_test.cpp index ea3f41a45..7b062b36f 100644 --- a/src/tests/ui_perf_test/ui_perf_test.cpp +++ b/src/tests/ui_perf_test/ui_perf_test.cpp @@ -164,6 +164,8 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { addIcon( "tree-expanded", 0xea50 ); addIcon( "tree-contracted", 0xea54 ); addIcon( "file", 0xecc3 ); + addIcon( "arrow-up", 0xea77 ); + addIcon( "arrow-down", 0xea4d ); UISceneNode* uiSceneNode = UISceneNode::New(); SceneManager::instance()->add( uiSceneNode ); uiSceneNode->getUIThemeManager()->setDefaultFont( font ); @@ -192,15 +194,15 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { Clock clock; auto model = FileSystemModel::New( "." ); // std::make_shared(); - UITreeView* view = UITreeView::New(); - // UITableView* view = UITableView::New(); - view->setExpanderIconSize( PixelDensity::dpToPx( 20 ) ); + //UITreeView* view = UITreeView::New(); + UITableView* view = UITableView::New(); + //view->setExpanderIconSize( PixelDensity::dpToPx( 20 ) ); view->setId( "treeview" ); /*view->setExpandedIcon( open ); view->setContractedIcon( closed );*/ view->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); view->setParent( vlay ); - view->setModel( model ); + view->setModel( SortingProxyModel::New( model ) ); eePRINTL( "Total time: %.2fms", clock.getElapsedTime().asMilliseconds() ); UIWindow* uiWin = UIWindow::NewOpt( UIWindow::LINEAR_LAYOUT ); diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index fe532b5a6..8fc9f576e 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -619,6 +619,11 @@ void App::showGlobalSearch() { mGlobalSearchBarLayout->setVisible( true )->setEnabled( true ); mGlobalSearchInput->setFocus(); mGlobalSearchTree->setVisible( true ); + if ( mEditorSplitter->getCurEditor() && + mEditorSplitter->getCurEditor()->getDocument().hasSelection() ) { + mGlobalSearchInput->setText( + mEditorSplitter->getCurEditor()->getDocument().getSelectedText() ); + } mGlobalSearchInput->getDocument().selectAll(); updateGlobalSearchBar(); }