Skip to content

Commit

Permalink
Allow to split editors views by dragging tabs into the tab widget cor…
Browse files Browse the repository at this point in the history
…ners.
  • Loading branch information
SpartanJ committed Dec 30, 2024
1 parent a447da4 commit 793e87a
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 51 deletions.
2 changes: 1 addition & 1 deletion include/eepp/scene/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ class EE_API Node : public Transformable {

void childRemove( Node* node );

Rectf getScreenBounds();
Rectf getScreenBounds() const;

void setInternalPosition( const Vector2f& Pos );

Expand Down
10 changes: 10 additions & 0 deletions include/eepp/ui/splitdirection.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef EE_UI_SPLIT_DIRECTION_HPP
#define EE_UI_SPLIT_DIRECTION_HPP

namespace EE { namespace UI {

enum class SplitDirection { Left, Right, Top, Bottom };

}} // namespace EE::UI

#endif // EE_UI_SPLIT_DIRECTION_HPP
17 changes: 15 additions & 2 deletions include/eepp/ui/tools/uicodeeditorsplitter.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef EE_UI_TOOLS_UICODEEDITORSPLITTER_HPP
#define EE_UI_TOOLS_UICODEEDITORSPLITTER_HPP

#include <eepp/ui/splitdirection.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include <eepp/ui/uimessagebox.hpp>
#include <eepp/ui/uiscenenode.hpp>
Expand All @@ -19,8 +20,6 @@ class EE_API UICodeEditorSplitter {

static const std::map<KeyBindings::Shortcut, std::string> getLocalDefaultKeybindings();

enum class SplitDirection { Left, Right, Top, Bottom };

class EE_API Client {
public:
virtual ~Client() {};
Expand Down Expand Up @@ -355,6 +354,12 @@ class EE_API UICodeEditorSplitter {

void setOnTabWidgetCreateCb( std::function<void( UITabWidget* )> cb );

bool getVisualSplitting() const;
void setVisualSplitting( bool visualSplitting );

Float getVisualSplitEdgePercent() const;
void setVisualSplitEdgePercent( Float visualSplitEdgePercent );

protected:
UISceneNode* mUISceneNode{ nullptr };
std::shared_ptr<ThreadPool> mThreadPool;
Expand All @@ -367,6 +372,7 @@ class EE_API UICodeEditorSplitter {
Client* mClient;
bool mHideTabBarOnSingleTab{ true };
bool mFirstCodeEditor{ true };
bool mVisualSplitting{ true };
UICodeEditor* mAboutToAddEditor{ nullptr };
UIMessageBox* mTryCloseMsgBox{ nullptr };
Mutex mTabWidgetMutex;
Expand All @@ -378,6 +384,7 @@ class EE_API UICodeEditorSplitter {
std::vector<NavigationRecord> mNavigationHistory;
size_t mNavigationHistoryPos{ std::numeric_limits<size_t>::max() };
std::function<void( UITabWidget* )> mOnTabWidgetCreateCb;
Float mVisualSplitEdgePercent{ 0.1 };

UICodeEditorSplitter( UICodeEditorSplitter::Client* client, UISceneNode* sceneNode,
std::shared_ptr<ThreadPool> threadPool,
Expand All @@ -387,6 +394,12 @@ class EE_API UICodeEditorSplitter {
virtual void onTabClosed( const TabEvent* tabEvent );

void closeAllTabs( std::vector<UITab*> tabs, UITabWidget::FocusTabBehavior focusTabBehavior );

UITabWidget* createTabWidget( Node* parent );

UITabWidget* splitTabWidget( SplitDirection, UITabWidget* );

void updateTabWidgetVisualSplitting();
};

}}} // namespace EE::UI::Tools
Expand Down
2 changes: 2 additions & 0 deletions include/eepp/ui/uinode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ class EE_API UINode : public Node {
void smartClipStart( const ClipType& reqClipType );

void smartClipEnd( const ClipType& reqClipType );

Color getDroppableHoveringColor();
};

}} // namespace EE::UI
Expand Down
14 changes: 13 additions & 1 deletion include/eepp/ui/uitabwidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
#define EE_UI_UITABWIDGET_HPP

#include <deque>
#include <eepp/ui/splitdirection.hpp>
#include <eepp/ui/uitab.hpp>
#include <eepp/ui/uiwidget.hpp>
#include <optional>

namespace EE { namespace UI {

Expand All @@ -26,6 +28,8 @@ class EE_API TabEvent : public Event {

class EE_API UITabWidget : public UIWidget {
public:
using SplitFunctionCb = std::function<UITabWidget*( SplitDirection, UITabWidget* )>;

enum class FocusTabBehavior { Closest, FocusOrder, Default };

class StyleConfig {
Expand Down Expand Up @@ -157,7 +161,7 @@ class EE_API UITabWidget : public UIWidget {

void setAllowRearrangeTabs( bool allowRearrangeTabs );

bool getAllowDragAndDropTabs() const;
bool allowDragAndDropTabs() const;

void setAllowDragAndDropTabs( bool allowDragAndDropTabs );

Expand Down Expand Up @@ -187,6 +191,8 @@ class EE_API UITabWidget : public UIWidget {

void swapTabs( UITab* left, UITab* right );

void setSplitFunction( SplitFunctionCb cb, Float splitEdgePercent = 0.1 );

protected:
friend class UITab;

Expand All @@ -209,6 +215,8 @@ class EE_API UITabWidget : public UIWidget {
FocusTabBehavior mFocusTabBehavior{ FocusTabBehavior::Closest };
std::deque<UITab*> mFocusHistory;
UIPopUpMenu* mCurrentMenu{ nullptr };
SplitFunctionCb mSplitFn;
Float mSplitEdgePercent{ 0.1 };

void onThemeLoaded();

Expand All @@ -229,6 +237,8 @@ class EE_API UITabWidget : public UIWidget {

virtual Uint32 onMessage( const NodeMessage* msg );

virtual void drawDroppableHovering();

void setContainerSize();

void posTabs();
Expand All @@ -254,6 +264,8 @@ class EE_API UITabWidget : public UIWidget {
void insertFocusHistory( UITab* tab );

void eraseFocusHistory( UITab* tab );

std::optional<SplitDirection> getDropDirection() const;
};

}} // namespace EE::UI
Expand Down
1 change: 1 addition & 0 deletions projects/linux/ee.files
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@
../../include/eepp/ui/models/persistentmodelindex.hpp
../../include/eepp/ui/models/sortingproxymodel.hpp
../../include/eepp/ui/models/widgettreemodel.hpp
../../include/eepp/ui/splitdirection.hpp
../../include/eepp/ui/tools/textureatlaseditor.hpp
../../include/eepp/ui/tools/uicodeeditorsplitter.hpp
../../include/eepp/ui/tools/uicolorpicker.hpp
Expand Down
2 changes: 1 addition & 1 deletion src/eepp/scene/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ void Node::onSizeChange() {
invalidateDraw();
}

Rectf Node::getScreenBounds() {
Rectf Node::getScreenBounds() const {
return Rectf( Vector2f( mScreenPosi.x, mScreenPosi.y ),
Sizef( (Float)(int)mSize.getWidth(), (Float)(int)mSize.getHeight() ) );
}
Expand Down
107 changes: 100 additions & 7 deletions src/eepp/ui/tools/uicodeeditorsplitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,11 +626,7 @@ void UICodeEditorSplitter::removeUnusedTab( UITabWidget* tabWidget, bool destroy
}
}

UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent, bool openCurEditor ) {
eeASSERT( curWidgetExists() );
if ( nullptr == mBaseLayout )
mBaseLayout = parent;
UICodeEditor* prevCurEditor = mCurEditor;
UITabWidget* UICodeEditorSplitter::createTabWidget( Node* parent ) {
UITabWidget* tabWidget = UITabWidget::New();
tabWidget->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent );
tabWidget->setParent( parent );
Expand All @@ -641,6 +637,13 @@ UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent, bool
tabWidget->setAllowSwitchTabsInEmptySpaces( true );
tabWidget->setEnabledCreateContextMenu( true );
tabWidget->setFocusTabBehavior( UITabWidget::FocusTabBehavior::FocusOrder );
if ( mVisualSplitting ) {
tabWidget->setSplitFunction(
[this]( SplitDirection dir, UITabWidget* widget ) -> UITabWidget* {
return splitTabWidget( dir, widget );
},
mVisualSplitEdgePercent );
}
tabWidget->addEventListener( Event::OnTabSelected, [this]( const Event* event ) {
UITabWidget* tabWidget = event->getNode()->asType<UITabWidget>();
if ( tabWidget->getTabSelected()->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) {
Expand All @@ -663,6 +666,17 @@ UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent, bool
} );
if ( mOnTabWidgetCreateCb )
mOnTabWidgetCreateCb( tabWidget );
Lock l( mTabWidgetMutex );
mTabWidgets.push_back( tabWidget );
return tabWidget;
}

UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent, bool openCurEditor ) {
eeASSERT( curWidgetExists() );
if ( nullptr == mBaseLayout )
mBaseLayout = parent;
UICodeEditor* prevCurEditor = mCurEditor;
UITabWidget* tabWidget = createTabWidget( parent );
auto editorData = createCodeEditorInTabWidget( tabWidget );
if ( editorData.first == nullptr || editorData.second == nullptr ) {
if ( !mTabWidgets.empty() && mTabWidgets[0]->getTabCount() > 0 ) {
Expand All @@ -684,8 +698,6 @@ UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent, bool
editorData.first->setTooltipText( path );
}
mAboutToAddEditor = nullptr;
Lock l( mTabWidgetMutex );
mTabWidgets.push_back( tabWidget );
return tabWidget;
}

Expand Down Expand Up @@ -1205,6 +1217,51 @@ UISplitter* UICodeEditorSplitter::split( const SplitDirection& direction, UIWidg
return splitter;
}

UITabWidget* UICodeEditorSplitter::splitTabWidget( SplitDirection direction,
UITabWidget* tabWidget ) {
if ( !tabWidget )
return nullptr;
UIOrientation orientation =
direction == SplitDirection::Left || direction == SplitDirection::Right
? UIOrientation::Horizontal
: UIOrientation::Vertical;
Node* parent = tabWidget->getParent();
UISplitter* parentSplitter = nullptr;
bool wasFirst = true;

if ( parent->isType( UI_TYPE_SPLITTER ) ) {
parentSplitter = parent->asType<UISplitter>();
wasFirst = parentSplitter->getFirstWidget() == tabWidget;
if ( !parentSplitter->isFull() ) {
parentSplitter->setOrientation( orientation );
UITabWidget* newTabWidget = createTabWidget( parentSplitter );
if ( direction == SplitDirection::Left || direction == SplitDirection::Top )
parentSplitter->swap();
return newTabWidget;
}
}

UISplitter* splitter = UISplitter::New();
splitter->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent );
splitter->setOrientation( orientation );
tabWidget->detach();
splitter->setParent( parent );
tabWidget->setParent( splitter );
UITabWidget* newTabWidget = createTabWidget( splitter );
if ( direction == SplitDirection::Left || direction == SplitDirection::Top )
splitter->swap();

if ( parentSplitter ) {
if ( wasFirst && parentSplitter->getFirstWidget() != splitter ) {
parentSplitter->swap();
} else if ( !wasFirst && parentSplitter->getLastWidget() != splitter ) {
parentSplitter->swap();
}
}

return newTabWidget;
}

void UICodeEditorSplitter::switchToTab( Int32 index ) {
UITabWidget* tabWidget = tabWidgetFromWidget( mCurWidget );
if ( tabWidget ) {
Expand Down Expand Up @@ -1665,4 +1722,40 @@ void UICodeEditorSplitter::setOnTabWidgetCreateCb( std::function<void( UITabWidg
mOnTabWidgetCreateCb = std::move( cb );
}

bool UICodeEditorSplitter::getVisualSplitting() const {
return mVisualSplitting;
}

void UICodeEditorSplitter::setVisualSplitting( bool visualSplitting ) {
if ( mVisualSplitting != visualSplitting ) {
mVisualSplitting = visualSplitting;
updateTabWidgetVisualSplitting();
}
}

Float UICodeEditorSplitter::getVisualSplitEdgePercent() const {
return mVisualSplitEdgePercent;
}

void UICodeEditorSplitter::setVisualSplitEdgePercent( Float visualSplitEdgePercent ) {
if ( mVisualSplitEdgePercent != visualSplitEdgePercent ) {
mVisualSplitEdgePercent = visualSplitEdgePercent;
updateTabWidgetVisualSplitting();
}
}

void UICodeEditorSplitter::updateTabWidgetVisualSplitting() {
for ( UITabWidget* tabWidget : mTabWidgets ) {
if ( mVisualSplitting ) {
tabWidget->setSplitFunction(
[this]( SplitDirection dir, UITabWidget* widget ) -> UITabWidget* {
return splitTabWidget( dir, widget );
},
mVisualSplitEdgePercent );
} else {
tabWidget->setSplitFunction( nullptr, mVisualSplitEdgePercent );
}
}
}

}}} // namespace EE::UI::Tools
35 changes: 19 additions & 16 deletions src/eepp/ui/uinode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,25 +567,10 @@ void UINode::drawOverNode() {
}

void UINode::drawDroppableHovering() {
const PropertyDefinition* def =
StyleSheetSpecification::instance()->getProperty( "droppable-hovering-color" );
Color color = Color::fromString( def->getDefaultValue() );
if ( isWidget() ) {
UIWidget* widget = asType<UIWidget>();
std::string colorString = widget->getPropertyString( def );
if ( !colorString.empty() ) {
color = Color::fromString( colorString );
} else {
colorString = mUISceneNode->getRoot()->getPropertyString( def );
if ( !colorString.empty() )
color = Color::fromString( colorString );
}
}

Primitives P;
P.setFillMode( DRAW_FILL );
P.setBlendMode( getBlendMode() );
P.setColor( color );
P.setColor( getDroppableHoveringColor() );
P.setLineWidth( PixelDensity::dpToPxI( 1 ) );
P.drawRectangle( getScreenBounds() );
}
Expand Down Expand Up @@ -1053,6 +1038,24 @@ void UINode::smartClipEnd( const ClipType& reqClipType ) {
smartClipEnd( reqClipType, isMeOrParentTreeScaledOrRotatedOrFrameBuffer() );
}

Color UINode::getDroppableHoveringColor() {
const PropertyDefinition* def =
StyleSheetSpecification::instance()->getProperty( "droppable-hovering-color" );
Color color = Color::fromString( def->getDefaultValue() );
if ( isWidget() ) {
UIWidget* widget = asType<UIWidget>();
std::string colorString = widget->getPropertyString( def );
if ( !colorString.empty() ) {
color = Color::fromString( colorString );
} else {
colorString = mUISceneNode->getRoot()->getPropertyString( def );
if ( !colorString.empty() )
color = Color::fromString( colorString );
}
}
return color;
}

void UINode::nodeDraw() {
if ( mVisible ) {
if ( mNodeFlags & NODE_FLAG_POSITION_DIRTY )
Expand Down
6 changes: 3 additions & 3 deletions src/eepp/ui/uitab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Uint32 UITab::onDrag( const Vector2f& pos, const Uint32&, const Sizef& dragDiff
tabW->swapTabs( tab, this );
}

if ( tabW->getAllowDragAndDropTabs() && !( mFlags & UI_DRAG_VERTICAL ) ) {
if ( tabW->allowDragAndDropTabs() && !( mFlags & UI_DRAG_VERTICAL ) ) {
mDragTotalDiff += mDragPoint.y - pos.y;
if ( eeabs( mDragTotalDiff ) >= tabW->getTabVerticalDragResistance() ) {
setFlags( UI_DRAG_VERTICAL );
Expand All @@ -90,7 +90,7 @@ Uint32 UITab::onDrag( const Vector2f& pos, const Uint32&, const Sizef& dragDiff
}
}

if ( tabW->getAllowDragAndDropTabs() ) {
if ( tabW->allowDragAndDropTabs() ) {
setEnabled( false );
Node* overFind = getUISceneNode()->overFind( pos );
setEnabled( true );
Expand Down Expand Up @@ -435,7 +435,7 @@ void UITab::updateTab() {
} else {
UIPushButton::setText( mText );
}
setDragEnabled( tTabW->getAllowRearrangeTabs() || tTabW->getAllowDragAndDropTabs() );
setDragEnabled( tTabW->getAllowRearrangeTabs() || tTabW->allowDragAndDropTabs() );
onAutoSize();
}
}
Expand Down
Loading

0 comments on commit 793e87a

Please sign in to comment.