Added support to move Tabs from one UITabWidget to another using the onDrop API.

This commit is contained in:
Martín Lucas Golini
2020-06-18 05:33:21 -03:00
parent d904cea482
commit 6885e34572
14 changed files with 144 additions and 38 deletions

View File

@@ -1,10 +1,6 @@
# TODO - Short and mid term plans.
## UITabWidget
* Add support to move Tabs from one `UITabWidget` to another using the onDrop API.
## UIIconTheme and UIIconThemeManager
Implement icon themes separated from the `UITheme` and customizable from a CSS file.

View File

@@ -1564,6 +1564,16 @@ Enables/disables manually rearraging the tabs in the tab bar.
---
### tabbar-allow-drag-and-drop-tabs
Enables/disables the hability to move any tab from a TabWidget to another.
* Applicable to: EE::UI::UITabWidget (TabWidget)
* Data Type: [boolean](#boolean-data-type)
* Default value: `false`
---
### tab-closable
Enables/disables tabs to be closable with the middle mouse button click or by clicking on the close

View File

@@ -52,6 +52,7 @@ class EE_API Event {
MsgBoxConfirmClick,
MsgBoxCancelClick,
OnTabSelected,
OnTabAdded,
OnTabClosed,
OnTabNavigate,
OnClose, // Warning: Only some nodes will report this event.

View File

@@ -371,7 +371,7 @@ class EE_API Node : public Transformable {
bool isChild( Node* child ) const;
bool inParentTreeOf( Node* Child ) const;
bool inParentTreeOf( Node* child ) const;
void setLoadingState( bool loading );

View File

@@ -190,6 +190,7 @@ enum class PropertyId : Uint32 {
BorderBottomRightRadius = String::hash( "border-bottom-right-radius" ),
TabBarHideOnSingleTab = String::hash( "tabbar-hide-on-single-tab" ),
TabBarAllowRearrange = String::hash( "tabbar-allow-rearrange" ),
TabBarAllowDragAndDrop = String::hash( "tabbar-allow-drag-and-drop-tabs" ),
};
enum class PropertyType : Uint32 {

View File

@@ -43,6 +43,8 @@ class EE_API UITab : public UISelectButton {
String mText;
std::string mOwnedName;
UIWidget* mCloseButton;
Float mDragTotalDiff;
UITabWidget* mTabWidget;
Uint32 onDrag( const Vector2f& position, const Uint32& flags, const Sizef& dragDiff );

View File

@@ -141,6 +141,14 @@ class EE_API UITabWidget : public UIWidget {
void setAllowRearrangeTabs( bool allowRearrangeTabs );
bool getAllowDragAndDropTabs() const;
void setAllowDragAndDropTabs( bool allowDragAndDropTabs );
const Float& getTabVerticalDragResistance() const;
void setTabVerticalDragResistance( const Float& tabVerticalDragResistance );
protected:
friend class UITab;
@@ -153,17 +161,25 @@ class EE_API UITabWidget : public UIWidget {
TabTryCloseCallback mTabTryCloseCallback;
bool mHideTabBarOnSingleTab;
bool mAllowRearrangeTabs;
bool mAllowDragAndDropTabs;
Float mTabVerticalDragResistance;
void onThemeLoaded();
UITab* createTab( const String& text, UINode* nodeOwned, Drawable* icon );
void removeTab( const Uint32& index, bool destroyOwnedNode, bool destroyTab = true );
void removeTab( UITab* tab, bool destroyOwnedNode, bool destroyTab );
virtual void onSizeChange();
virtual void onChildCountChange( Node* child, const bool& removed );
virtual void onPaddingChange();
virtual Uint32 onMessage( const NodeMessage* msg );
void setContainerSize();
void posTabs();

View File

@@ -773,16 +773,13 @@ bool Node::isChild( Node* child ) const {
return false;
}
bool Node::inParentTreeOf( Node* Child ) const {
Node* ParentLoop = Child->mParentNode;
while ( NULL != ParentLoop ) {
if ( ParentLoop == this )
bool Node::inParentTreeOf( Node* child ) const {
Node* node = child->mParentNode;
while ( NULL != node ) {
if ( node == this )
return true;
ParentLoop = ParentLoop->mParentNode;
node = node->mParentNode;
}
return false;
}

View File

@@ -23,4 +23,8 @@ const Uint32& NodeMessage::getFlags() const {
NodeDropMessage::NodeDropMessage( Node* node, const Uint32& msg, Node* droppedNode ) :
NodeMessage( node, msg ), mDroppedNode( droppedNode ) {}
Node* NodeDropMessage::getDroppedNode() const {
return mDroppedNode;
}
}} // namespace EE::Scene

View File

@@ -362,6 +362,7 @@ void StyleSheetSpecification::registerDefaultProperties() {
registerProperty( "tabbar-hide-on-single-tab", "false" );
registerProperty( "tabbar-allow-rearrange", "false" );
registerProperty( "tabbar-allow-drag-and-drop-tabs", "false" );
// Shorthands
registerShorthand( "margin", {"margin-top", "margin-right", "margin-bottom", "margin-left"},

View File

@@ -442,11 +442,7 @@ Uint32 UINode::onCalculateDrag( const Vector2f& position, const Uint32& flags )
if ( onDrag( pos, flags, dragDiff ) ) {
setPixelsPosition( mPosition - dragDiff );
mDragPoint = pos;
onPositionChange();
eventDispatcher->setNodeDragging( this );
}
}
@@ -1137,9 +1133,9 @@ void UINode::setDragging( const bool& dragging ) {
mEnabled = false;
Node* found = getUISceneNode()->overFind( getEventDispatcher()->getMousePosf() );
if ( found && found->isUINode() ) {
found->asType<UINode>()->onDrop( this );
NodeDropMessage msg( found, NodeMessage::Drop, this );
found->messagePost( &msg );
found->asType<UINode>()->onDrop( this );
}
mEnabled = enabled;
}

View File

@@ -9,7 +9,8 @@ UITab* UITab::New() {
return eeNew( UITab, () );
}
UITab::UITab() : UISelectButton( "tab" ), mOwnedWidget( NULL ) {
UITab::UITab() :
UISelectButton( "tab" ), mOwnedWidget( NULL ), mDragTotalDiff( 0.f ), mTabWidget( NULL ) {
mTextBox->setElementTag( mTag + "::text" );
mIcon->setElementTag( mTag + "::icon" );
auto cb = [&]( const Event* ) { onSizeChange(); };
@@ -37,12 +38,13 @@ UITabWidget* UITab::getTabWidget() {
if ( NULL != getParent() && NULL != getParent()->getParent() &&
getParent()->getParent()->isType( UI_TYPE_TABWIDGET ) ) {
return getParent()->getParent()->asType<UITabWidget>();
} else if ( mTabWidget ) {
return mTabWidget;
}
return NULL;
}
Uint32 UITab::onDrag( const Vector2f&, const Uint32&, const Sizef& dragDiff ) {
Uint32 UITab::onDrag( const Vector2f& pos, const Uint32&, const Sizef& dragDiff ) {
UITabWidget* tabW = getTabWidget();
if ( !tabW )
return 0;
@@ -65,11 +67,27 @@ Uint32 UITab::onDrag( const Vector2f&, const Uint32&, const Sizef& dragDiff ) {
}
}
}
if ( tabW->getAllowDragAndDropTabs() && !( mFlags & UI_DRAG_VERTICAL ) ) {
mDragTotalDiff += ( Float )( mDragPoint.y - pos.y );
if ( eeabs( mDragTotalDiff ) >= tabW->getTabVerticalDragResistance() ) {
setFlags( UI_DRAG_VERTICAL );
setPixelsPosition( mPosition.x, mPosition.y - mDragTotalDiff );
mDragPoint = pos;
mTabWidget = getTabWidget();
Vector2f posDiff( pos );
worldToNode( posDiff );
setParent( getUISceneNode()->getRoot() );
setPixelsPosition( pos - posDiff );
}
}
return 1;
}
Uint32 UITab::onDragStart( const Vector2i& position, const Uint32& flags ) {
UITabWidget* tabW = getTabWidget();
mTabWidget = NULL;
mDragTotalDiff = 0;
unsetFlags( UI_DRAG_VERTICAL );
if ( tabW ) {
for ( size_t i = 0; i < tabW->getTabCount(); i++ ) {
UITab* tab = tabW->getTab( i );
@@ -83,6 +101,10 @@ Uint32 UITab::onDragStart( const Vector2i& position, const Uint32& flags ) {
Uint32 UITab::onDragStop( const Vector2i& position, const Uint32& flags ) {
UITabWidget* tabW = getTabWidget();
if ( mTabWidget ) {
setParent( tabW->getTabBar() );
mTabWidget = NULL;
}
if ( tabW ) {
for ( size_t i = 0; i < tabW->getTabCount(); i++ ) {
UITab* tab = tabW->getTab( i );
@@ -303,7 +325,7 @@ void UITab::updateTab() {
} else {
UIPushButton::setText( mText );
}
setDragEnabled( tTabW->getAllowRearrangeTabs() );
setDragEnabled( tTabW->getAllowRearrangeTabs() || tTabW->getAllowDragAndDropTabs() );
onAutoSize();
}
}

View File

@@ -20,7 +20,9 @@ UITabWidget::UITabWidget() :
mTabSelected( NULL ),
mTabSelectedIndex( eeINDEX_NOT_FOUND ),
mHideTabBarOnSingleTab( false ),
mAllowRearrangeTabs( false ) {
mAllowRearrangeTabs( false ),
mAllowDragAndDropTabs( false ),
mTabVerticalDragResistance( PixelDensity::dpToPx( 64 ) ) {
setHorizontalAlign( UI_HALIGN_CENTER );
mTabBar = UIWidget::NewWithTag( "tabwidget::tabbar" );
@@ -218,6 +220,9 @@ bool UITabWidget::applyProperty( const StyleSheetProperty& attribute ) {
case PropertyId::TabBarAllowRearrange:
setAllowRearrangeTabs( attribute.asBool() );
break;
case PropertyId::TabBarAllowDragAndDrop:
setAllowDragAndDropTabs( attribute.asBool() );
break;
default:
return UIWidget::applyProperty( attribute );
}
@@ -424,6 +429,9 @@ UITabWidget* UITabWidget::add( UITab* tab ) {
orderTabs();
}
TabEvent tabEvent( this, tab, getTabIndex( tab ), Event::OnTabAdded );
sendEvent( &tabEvent );
return this;
}
@@ -459,13 +467,19 @@ Uint32 UITabWidget::getTabCount() const {
}
void UITabWidget::removeTab( const Uint32& index, bool destroyOwnedNode ) {
removeTab( index, destroyOwnedNode, true );
}
void UITabWidget::removeTab( const Uint32& index, bool destroyOwnedNode, bool destroyTab ) {
eeASSERT( index < mTabs.size() );
UITab* tab = mTabs[index];
tab->close();
tab->setVisible( false );
tab->setEnabled( false );
if ( destroyTab ) {
tab->close();
tab->setVisible( false );
tab->setEnabled( false );
}
if ( destroyOwnedNode ) {
tab->getOwnedWidget()->close();
tab->getOwnedWidget()->setVisible( false );
@@ -499,7 +513,11 @@ void UITabWidget::removeTab( const Uint32& index, bool destroyOwnedNode ) {
}
void UITabWidget::removeTab( UITab* tab, bool destroyOwnedNode ) {
removeTab( getTabIndex( tab ), destroyOwnedNode );
removeTab( getTabIndex( tab ), destroyOwnedNode, true );
}
void UITabWidget::removeTab( UITab* tab, bool destroyOwnedNode, bool destroyTab ) {
removeTab( getTabIndex( tab ), destroyOwnedNode, destroyTab );
}
void UITabWidget::removeAllTabs( bool destroyOwnedNode ) {
@@ -628,6 +646,22 @@ void UITabWidget::setAllowRearrangeTabs( bool allowRearrangeTabs ) {
}
}
bool UITabWidget::getAllowDragAndDropTabs() const {
return mAllowDragAndDropTabs;
}
void UITabWidget::setAllowDragAndDropTabs( bool allowDragAndDropTabs ) {
mAllowDragAndDropTabs = allowDragAndDropTabs;
}
const Float& UITabWidget::getTabVerticalDragResistance() const {
return mTabVerticalDragResistance;
}
void UITabWidget::setTabVerticalDragResistance( const Float& tabVerticalDragResistance ) {
mTabVerticalDragResistance = tabVerticalDragResistance;
}
void UITabWidget::refreshOwnedWidget( UITab* tab ) {
if ( NULL != tab && NULL != tab->getOwnedWidget() ) {
tab->getOwnedWidget()->setParent( mNodeContainer );
@@ -695,17 +729,24 @@ void UITabWidget::onSizeChange() {
void UITabWidget::onChildCountChange( Node* child, const bool& removed ) {
if ( !removed && child != mTabBar && child != mNodeContainer ) {
if ( child->isType( UI_TYPE_TAB ) ) {
UITab* Tab = static_cast<UITab*>( child );
// This must be a tab that was dragging.
if ( std::find( mTabs.begin(), mTabs.end(), child->asType<UITab>() ) != mTabs.end() )
return;
Tab->setParent( mTabBar );
UITab* tab = static_cast<UITab*>( child );
mTabs.push_back( Tab );
tab->setParent( mTabBar );
mTabs.push_back( tab );
if ( NULL == mTabSelected ) {
setTabSelected( Tab );
setTabSelected( tab );
} else {
orderTabs();
}
TabEvent tabEvent( this, tab, getTabIndex( tab ), Event::OnTabAdded );
sendEvent( &tabEvent );
} else {
child->setParent( mNodeContainer );
child->setVisible( false );
@@ -722,6 +763,21 @@ void UITabWidget::onPaddingChange() {
UIWidget::onPaddingChange();
}
Uint32 UITabWidget::onMessage( const NodeMessage* msg ) {
if ( msg->getMsg() == NodeMessage::Drop && mAllowDragAndDropTabs ) {
const NodeDropMessage* dropMsg = static_cast<const NodeDropMessage*>( msg );
if ( dropMsg->getDroppedNode()->isType( UI_TYPE_TAB ) ) {
UITab* tab = dropMsg->getDroppedNode()->asType<UITab>();
if ( tab->getTabWidget() != this ) {
tab->getTabWidget()->removeTab( tab, false, false );
add( tab );
return 1;
}
}
}
return 0;
}
void UITabWidget::applyThemeToTabs() {
if ( mStyleConfig.TabsEdgesDiffSkins ) {
for ( Uint32 i = 0; i < mTabs.size(); i++ ) {

View File

@@ -451,6 +451,7 @@ UITabWidget* App::createEditorWithTabWidget( Node* parent ) {
tabWidget->setTabsClosable( true );
tabWidget->setHideTabBarOnSingleTab( true );
tabWidget->setAllowRearrangeTabs( true );
tabWidget->setAllowDragAndDropTabs( true );
tabWidget->addEventListener( Event::OnTabSelected, [&]( const Event* event ) {
UITabWidget* tabWidget = event->getNode()->asType<UITabWidget>();
mCurEditor = tabWidget->getTabSelected()->getOwnedWidget()->asType<UICodeEditor>();
@@ -463,6 +464,16 @@ UITabWidget* App::createEditorWithTabWidget( Node* parent ) {
tabWidget->addEventListener( Event::OnTabClosed, [&]( const Event* event ) {
onTabClosed( static_cast<const TabEvent*>( event ) );
} );
tabWidget->addEventListener( Event::OnTabAdded, [&]( const Event* event ) {
UITabWidget* tabWidget = event->getNode()->asType<UITabWidget>();
if ( tabWidget->getTabCount() == 2 && tabWidget->getTab( 0 )
->getOwnedWidget()
->asType<UICodeEditor>()
->getDocument()
.isEmpty() ) {
tabWidget->removeTab( (Uint32)0 );
}
} );
createCodeEditorInTabWidget( tabWidget );
mTabWidgets.push_back( tabWidget );
return tabWidget;
@@ -508,13 +519,6 @@ void App::openFileDialog() {
UITab* addedTab = d.first;
loadFileFromPath( event->getNode()->asType<UIFileDialog>()->getFullPath(), d.second );
tabWidget->setTabSelected( addedTab );
UITab* firstTab = tabWidget->getTab( 0 );
if ( addedTab != firstTab ) {
UICodeEditor* editor = (UICodeEditor*)firstTab->getOwnedWidget();
if ( editor->getDocument().isEmpty() ) {
tabWidget->removeTab( firstTab );
}
}
} );
dialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) {
if ( mCurEditor && !SceneManager::instance()->isShootingDown() )