From 994cee2bbfaf36a8132df6d7679ecb645a347469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 30 Sep 2022 01:58:12 -0300 Subject: [PATCH] Improved UITreeView selection. ecode: Widget picker in scene node inspector. Added support for @string(key, "fallback_text") in translator and text properties. --- include/eepp/scene/eventdispatcher.hpp | 6 ++ .../eepp/ui/abstract/uiabstracttableview.hpp | 3 +- include/eepp/ui/models/widgettreemodel.hpp | 4 ++ include/eepp/ui/uitreeview.hpp | 7 +- src/eepp/scene/eventdispatcher.cpp | 16 +++++ src/eepp/ui/abstract/uiabstracttableview.cpp | 5 +- src/eepp/ui/models/widgettreemodel.cpp | 17 +++++ src/eepp/ui/uiscenenode.cpp | 41 +++++++++--- src/eepp/ui/uitreeview.cpp | 67 ++++++++++++++----- src/tools/ecode/ecode.cpp | 42 ++++++++++-- src/tools/ecode/ecode.hpp | 2 + .../ecode/plugins/linter/linterplugin.cpp | 1 + 12 files changed, 176 insertions(+), 35 deletions(-) diff --git a/include/eepp/scene/eventdispatcher.hpp b/include/eepp/scene/eventdispatcher.hpp index 4f8ac0f61..dfc42cfc6 100644 --- a/include/eepp/scene/eventdispatcher.hpp +++ b/include/eepp/scene/eventdispatcher.hpp @@ -98,6 +98,10 @@ class EE_API EventDispatcher { Node* getNodeWasDragging() const; + bool getDisableMousePress() const; + + void setDisableMousePress( bool disableMousePress ); + protected: EE::Window::Window* mWindow; Input* mInput; @@ -113,6 +117,8 @@ class EE_API EventDispatcher { Vector2i mClickPos; Int32 mCbId; bool mFirstPress; + bool mDisableMousePress{ false }; + bool mJustDisabledMousePress{ false }; Node* mNodeWasDragging; Node* mNodeDragging; Time mElapsed; diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 0e4b41c49..c5d797bb9 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -69,7 +69,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { void moveSelection( int steps ); - virtual void setSelection( const ModelIndex& index, bool scrollToSelection = true ); + virtual void setSelection( const ModelIndex& index, bool scrollToSelection = true, + bool openModelIndexTree = false ); const size_t& getIconSize() const; diff --git a/include/eepp/ui/models/widgettreemodel.hpp b/include/eepp/ui/models/widgettreemodel.hpp index 044595997..4cb8a272b 100644 --- a/include/eepp/ui/models/widgettreemodel.hpp +++ b/include/eepp/ui/models/widgettreemodel.hpp @@ -34,6 +34,10 @@ class EE_API WidgetTreeModel : public Model { virtual void update() override; + ModelIndex getRoot() const; + + ModelIndex getModelIndex( const Node* node ) const; + protected: Node* mRoot; diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index 853e20631..5d839cdac 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -131,12 +131,17 @@ class EE_API UITreeView : public UIAbstractTableView { virtual ModelIndex selectRowWithPath( std::string path ); - virtual void setSelection( const ModelIndex& index, bool scrollToSelection = true ); + virtual void setSelection( const ModelIndex& index, bool scrollToSelection = true, + bool openModelIndexTree = true ); + + virtual void openModelIndexParentTree( const ModelIndex& index ); bool getFocusOnSelection() const; void setFocusOnSelection( bool focusOnSelection ); + bool tryOpenModelIndex( const ModelIndex& index, bool forceUpdate = true ); + protected: enum class IterationDecision { Continue, diff --git a/src/eepp/scene/eventdispatcher.cpp b/src/eepp/scene/eventdispatcher.cpp index 54e49d401..83be34f5a 100644 --- a/src/eepp/scene/eventdispatcher.cpp +++ b/src/eepp/scene/eventdispatcher.cpp @@ -89,6 +89,11 @@ void EventDispatcher::update( const Time& time ) { } } + if ( mDisableMousePress || mJustDisabledMousePress ) { + mJustDisabledMousePress = false; + return; + } + if ( NULL != mNodeDragging ) mNodeDragging->onCalculateDrag( mMousePos, mInput->getPressTrigger() ); @@ -331,4 +336,15 @@ Node* EventDispatcher::getNodeWasDragging() const { return mNodeWasDragging; } +bool EventDispatcher::getDisableMousePress() const { + return mDisableMousePress; +} + +void EventDispatcher::setDisableMousePress( bool disableMousePress ) { + if ( mDisableMousePress && !disableMousePress ) + mJustDisabledMousePress = true; + mDisableMousePress = disableMousePress; + +} + }} // namespace EE::Scene diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 44b961abc..292d962cd 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -489,11 +489,14 @@ void UIAbstractTableView::moveSelection( int steps ) { setSelection( newIndex ); } -void UIAbstractTableView::setSelection( const ModelIndex& index, bool scrollToSelection ) { +void UIAbstractTableView::setSelection( const ModelIndex& index, bool scrollToSelection, + bool openModelIndexTree ) { if ( !getModel() ) return; auto& model = *this->getModel(); if ( model.isValid( index ) ) { + if ( openModelIndexTree ) + onOpenMenuModelIndex( index ); getSelection().set( index ); if ( scrollToSelection ) scrollToPosition( diff --git a/src/eepp/ui/models/widgettreemodel.cpp b/src/eepp/ui/models/widgettreemodel.cpp index cd7991cc2..413ad6290 100644 --- a/src/eepp/ui/models/widgettreemodel.cpp +++ b/src/eepp/ui/models/widgettreemodel.cpp @@ -77,4 +77,21 @@ void WidgetTreeModel::update() { onModelUpdate(); } +ModelIndex WidgetTreeModel::getRoot() const { + return createIndex( 0, 0, mRoot ); +} + +ModelIndex WidgetTreeModel::getModelIndex( const Node* node ) const { + eeASSERT( node != nullptr ); + const Node* fNode = node; + while ( fNode ) { + if ( fNode == mRoot ) + break; + fNode = fNode->getParent(); + } + if ( fNode != mRoot || nullptr == node->getParent() ) + return {}; + return createIndex( node->getNodeIndex(), 0, node ); +} + }}} // namespace EE::UI::Models diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 1d4f76246..fe4d99688 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -162,24 +162,43 @@ Translator& UISceneNode::getTranslator() { } String UISceneNode::getTranslatorString( const std::string& str ) { - if ( String::startsWith( str, "@string/" ) ) { - String tstr = mTranslator.getString( str.substr( 8 ) ); + if ( str.size() >= 8 && String::startsWith( str, "@string" ) ) { + if ( str[7] == '/' ) { + String tstr = mTranslator.getString( str.substr( 8 ) ); - if ( !tstr.empty() ) - return tstr; + if ( !tstr.empty() ) + return tstr; + } else if ( str[7] == '(' ) { + FunctionString fun( FunctionString::parse( str ) ); + if ( !fun.isEmpty() ) { + String tstr( mTranslator.getString( fun.getParameters()[0] ) ); + if ( !tstr.empty() ) + return tstr; + if ( fun.getParameters().size() >= 2 ) + return fun.getParameters()[1]; + } + } } - return String( str ); } String UISceneNode::getTranslatorString( const std::string& str, const String& defaultValue ) { - if ( String::startsWith( str, "@string/" ) ) { - String tstr = mTranslator.getString( str.substr( 8 ) ); - - if ( !tstr.empty() ) - return tstr; + if ( str.size() >= 8 && String::startsWith( str, "@string" ) ) { + if ( str[7] == '/' ) { + String tstr( mTranslator.getString( str.substr( 8 ) ) ); + if ( !tstr.empty() ) + return tstr; + } else if ( str[7] == '(' ) { + FunctionString fun( FunctionString::parse( str ) ); + if ( !fun.isEmpty() ) { + String tstr( mTranslator.getString( fun.getParameters()[0] ) ); + if ( !tstr.empty() ) + return tstr; + if ( fun.getParameters().size() >= 2 ) + return fun.getParameters()[1]; + } + } } - return defaultValue; } diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 58209e8b4..e12c03c84 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace EE { namespace UI { @@ -140,6 +141,26 @@ void UITreeView::bindNavigationClick( UIWidget* widget ) { } ); } +bool UITreeView::tryOpenModelIndex( const ModelIndex& index, bool forceUpdate ) { + size_t rowCount = 0; + { + ConditionalLock l( getModel() != nullptr, + getModel() ? &getModel()->resourceMutex() : nullptr ); + rowCount = getModel()->rowCount( index ); + } + if ( rowCount ) { + auto& data = getIndexMetadata( index ); + if ( !data.open ) { + data.open = true; + if ( forceUpdate ) + createOrUpdateColumns(); + onOpenTreeModelIndex( index, data.open ); + } + return true; + } + return false; +} + UIWidget* UITreeView::setupCell( UITableCell* widget, UIWidget* rowWidget, const ModelIndex& index ) { widget->setParent( rowWidget ); @@ -701,20 +722,7 @@ ModelIndex UITreeView::selectRowWithPath( std::string path ) { if ( foundIndex == ModelIndex() ) break; - size_t rowCount = 0; - { - ConditionalLock l( getModel() != nullptr, - getModel() ? &getModel()->resourceMutex() : nullptr ); - rowCount = getModel()->rowCount( foundIndex ); - } - if ( rowCount ) { - auto& data = getIndexMetadata( foundIndex ); - if ( !data.open ) { - data.open = true; - createOrUpdateColumns(); - onOpenTreeModelIndex( foundIndex, data.open ); - } - } + tryOpenModelIndex( foundIndex ); parentIndex = foundIndex; @@ -726,13 +734,20 @@ ModelIndex UITreeView::selectRowWithPath( std::string path ) { return {}; } -void UITreeView::setSelection( const ModelIndex& index, bool scrollToSelection ) { +void UITreeView::setSelection( const ModelIndex& index, bool scrollToSelection, + bool openModelIndexTree ) { if ( !getModel() ) return; auto& model = *this->getModel(); - if ( model.isValid( index ) && scrollToSelection ) { + if ( model.isValid( index ) ) { + if ( openModelIndexTree ) + openModelIndexParentTree( index ); + getSelection().set( index ); + if ( !scrollToSelection ) + return; + ModelIndex prevIndex; ModelIndex foundIndex; Float curY = 0; @@ -759,6 +774,26 @@ void UITreeView::setSelection( const ModelIndex& index, bool scrollToSelection ) } } +void UITreeView::openModelIndexParentTree( const ModelIndex& index ) { + if ( !index.hasParent() ) + return; + + std::stack indexes; + ModelIndex parent = index; + while ( parent.hasParent() ) { + indexes.push( parent ); + parent = parent.parent(); + } + + while ( !indexes.empty() ) { + if ( !tryOpenModelIndex( indexes.top() ) ) + return; + indexes.pop(); + } + + createOrUpdateColumns(); +} + bool UITreeView::getFocusOnSelection() const { return mFocusOnSelection; } diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 1f786237e..82dcaaf96 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -519,18 +519,50 @@ App::~App() { eeSAFE_DELETE( mConsole ); } +void App::checkWidgetPick( UITreeView* widgetTree ) { + Input* input = mWindow->getInput(); + if ( input->getClickTrigger() & EE_BUTTON_LMASK ) { + Node* node = mUISceneNode->getEventDispatcher()->getMouseOverNode(); + WidgetTreeModel* model = static_cast( widgetTree->getModel() ); + ModelIndex index( model->getModelIndex( node ) ); + widgetTree->setSelection( index ); + mUISceneNode->setHighlightOver( false ); + mUISceneNode->getEventDispatcher()->setDisableMousePress( false ); + } else { + mUISceneNode->runOnMainThread( [this, widgetTree]() { checkWidgetPick( widgetTree ); } ); + } +} + void App::createWidgetTreeView() { - UIWindow* uiWin = UIWindow::NewOpt( UIWindow::LINEAR_LAYOUT ); + UIWindow* uiWin = UIWindow::New(); uiWin->setMinWindowSize( 600, 400 ); uiWin->setWindowFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_RESIZEABLE | UI_WIN_MAXIMIZE_BUTTON ); - UITreeView* widgetTree = UITreeView::New(); - widgetTree->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); - widgetTree->setParent( uiWin ); + UIWidget* cont = mUISceneNode->loadLayoutFromString( R"xml( + + + + + + + )xml", + uiWin->getContainer() ); + UITreeView* widgetTree = cont->findByType( UI_TYPE_TREEVIEW ); widgetTree->setHeadersVisible( true ); widgetTree->setAutoExpandOnSingleColumn( true ); widgetTree->setExpanderIconSize( mMenuIconSize ); widgetTree->setAutoColumnsWidth( true ); - widgetTree->setModel( WidgetTreeModel::New( mUISceneNode ) ); + auto model = WidgetTreeModel::New( mUISceneNode ); + widgetTree->setModel( model ); + widgetTree->tryOpenModelIndex( model->getRoot() ); + UIPushButton* button = cont->find( "pick_widget" ); + button->addEventListener( Event::MouseClick, [&, widgetTree]( const Event* event ) { + if ( event->asMouseEvent()->getFlags() & EE_BUTTON_LMASK ) { + mUISceneNode->setHighlightOver( true ); + mUISceneNode->getEventDispatcher()->setDisableMousePress( true ); + mUISceneNode->runOnMainThread( + [this, widgetTree]() { checkWidgetPick( widgetTree ); } ); + } + } ); uiWin->center(); } diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index 8f973cfa6..6d681d227 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -345,6 +345,8 @@ class App : public UICodeEditorSplitter::Client { void renameFile( const FileInfo& file ); void initPluginManager(); + + void checkWidgetPick( UITreeView* widgetTree ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index 1e457e0d7..96dcb5b61 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -445,6 +445,7 @@ std::string LinterPlugin::getMatchString( const LinterType& type ) { return "error"; } +// TODO: Implement error lens void LinterPlugin::drawAfterLineText( UICodeEditor* editor, const Int64& index, Vector2f position, const Float& /*fontSize*/, const Float& lineHeight ) { Lock l( mMatchesMutex );