mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-07-19 17:22:53 +03:00
Added SortingProxyModel to allow sorting tables by column.
Some minor improvements to ecode. Added "delete-current-line" to text document.
This commit is contained in:
@@ -786,6 +786,10 @@ TableView > ScrollBar {
|
||||
background-color: var(--list-back);
|
||||
}
|
||||
|
||||
table::header::column::arrow {
|
||||
margin-left: 2dp;
|
||||
}
|
||||
|
||||
.appbackground {
|
||||
background-color: var(--back);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ class EE_API Event {
|
||||
OnNodeDropped,
|
||||
OnSave,
|
||||
OnModelEvent,
|
||||
OnResourceChange,
|
||||
UserEvent,
|
||||
NoEvent = eeINDEX_NOT_FOUND
|
||||
};
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
#include <eepp/ui/models/filesystemmodel.hpp>
|
||||
#include <eepp/ui/models/model.hpp>
|
||||
#include <eepp/ui/models/modelselection.hpp>
|
||||
#include <eepp/ui/models/sortingproxymodel.hpp>
|
||||
#include <eepp/ui/models/widgettreemodel.hpp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -189,6 +189,8 @@ class EE_API TextDocument {
|
||||
|
||||
void deleteToNextWord();
|
||||
|
||||
void deleteCurrentLine();
|
||||
|
||||
void selectToPreviousChar();
|
||||
|
||||
void selectToNextChar();
|
||||
|
||||
@@ -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<void()>& onUpdate );
|
||||
@@ -91,6 +102,7 @@ class EE_API Model {
|
||||
|
||||
private:
|
||||
std::unordered_set<UIAbstractView*> mViews;
|
||||
std::unordered_set<Client*> mClients;
|
||||
std::function<void()> mOnUpdate;
|
||||
};
|
||||
|
||||
|
||||
@@ -62,11 +62,25 @@ class EE_API ModelSelection {
|
||||
|
||||
void removeMatching( std::function<bool( const ModelIndex& )> );
|
||||
|
||||
template <typename Function> void changeFromModel( Function f ) {
|
||||
{
|
||||
mDisableNotify = true;
|
||||
mNotifyPending = false;
|
||||
f( *this );
|
||||
mDisableNotify = false;
|
||||
}
|
||||
if ( mNotifyPending )
|
||||
notifySelectionChanged();
|
||||
}
|
||||
|
||||
protected:
|
||||
UIAbstractView* mView;
|
||||
std::vector<ModelIndex> mIndexes;
|
||||
bool mDisableNotify{false};
|
||||
bool mNotifyPending{false};
|
||||
void notifySelectionChanged();
|
||||
};
|
||||
|
||||
}}} // namespace EE::UI::Model
|
||||
}}} // namespace EE::UI::Models
|
||||
|
||||
#endif // EE_UI_MODEL_MODELSELECTION_HPP
|
||||
|
||||
69
include/eepp/ui/models/sortingproxymodel.hpp
Normal file
69
include/eepp/ui/models/sortingproxymodel.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef EE_UI_MODELS_SORTINGPROXYMODEL_HPP
|
||||
#define EE_UI_MODELS_SORTINGPROXYMODEL_HPP
|
||||
|
||||
#include <eepp/ui/models/model.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace EE { namespace UI { namespace Models {
|
||||
|
||||
class EE_API SortingProxyModel final : public Model, private Model::Client {
|
||||
public:
|
||||
template <typename T>
|
||||
static std::shared_ptr<SortingProxyModel> New( std::shared_ptr<T> model ) {
|
||||
return std::shared_ptr<SortingProxyModel>(
|
||||
new SortingProxyModel( std::static_pointer_cast<Model>( 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<Model> );
|
||||
|
||||
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<Model> mTarget;
|
||||
std::vector<int> 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
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <eepp/core/core.hpp>
|
||||
#include <eepp/graphics/drawable.hpp>
|
||||
#include <eepp/graphics/drawableresource.hpp>
|
||||
#include <eepp/math/rect.hpp>
|
||||
#include <eepp/ui/uiicon.hpp>
|
||||
#include <string>
|
||||
@@ -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<DrawableResource*>( 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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <eepp/ui/abstract/uiabstracttableview.hpp>
|
||||
#include <eepp/ui/uiimage.hpp>
|
||||
#include <eepp/ui/uilinearlayout.hpp>
|
||||
#include <eepp/ui/uipushbutton.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
@@ -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<UIImage>();
|
||||
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<UIImage>();
|
||||
if ( image && icon )
|
||||
image->setDrawable( icon );
|
||||
model->setKeyColumnAndSortOrder( colIndex, sortOrder );
|
||||
}
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Abstract
|
||||
|
||||
@@ -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(); };
|
||||
|
||||
@@ -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(); } );
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <eepp/core.hpp>
|
||||
#include <eepp/ui/models/modelselection.hpp>
|
||||
#include <eepp/ui/abstract/uiabstractview.hpp>
|
||||
#include <eepp/ui/models/modelselection.hpp>
|
||||
|
||||
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
|
||||
|
||||
156
src/eepp/ui/models/sortingproxymodel.cpp
Normal file
156
src/eepp/ui/models/sortingproxymodel.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include <algorithm>
|
||||
#include <eepp/ui/abstract/uiabstractview.hpp>
|
||||
#include <eepp/ui/models/modelselection.hpp>
|
||||
#include <eepp/ui/models/sortingproxymodel.hpp>
|
||||
#include <eepp/ui/models/variant.hpp>
|
||||
|
||||
using namespace EE::UI::Abstract;
|
||||
|
||||
namespace EE { namespace UI { namespace Models {
|
||||
|
||||
SortingProxyModel::SortingProxyModel( std::shared_ptr<Model> 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<size_t>( index.row() ) >= mRowMappings.size() ||
|
||||
static_cast<size_t>( 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<ModelIndex> 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
|
||||
@@ -34,7 +34,7 @@ const std::map<KeyBindings::Shortcut, std::string> 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"},
|
||||
|
||||
@@ -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<DrawableResource*>( mDrawable )
|
||||
|
||||
@@ -144,10 +144,9 @@ Vector2f UIPushButton::packLayout( const std::vector<UIWidget*>& 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;
|
||||
|
||||
@@ -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<UITableHeaderColumn*>( 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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() ) {
|
||||
|
||||
@@ -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<TestModel>();
|
||||
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 );
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user