From 2c1811e519bb438ffda309e717157eb6b7d3c601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 10 Apr 2023 18:58:34 -0300 Subject: [PATCH] eepp: Several fixes on widgets. ecode: Build Side Panel WIP. --- docs/articles/cssspecification.md | 20 ++ include/eepp/ui/css/propertydefinition.hpp | 1 + include/eepp/ui/uilistbox.hpp | 6 +- include/eepp/ui/uipushbutton.hpp | 15 +- include/eepp/ui/uitextinput.hpp | 1 + include/eepp/ui/uitreeview.hpp | 2 +- src/eepp/system/process.cpp | 6 +- src/eepp/ui/css/stylesheetspecification.cpp | 7 + src/eepp/ui/uidropdownlist.cpp | 21 ++- src/eepp/ui/uilistbox.cpp | 10 +- src/eepp/ui/uipushbutton.cpp | 91 ++++++++- src/eepp/ui/uitab.cpp | 26 +-- src/eepp/ui/uitableheadercolumn.cpp | 2 +- src/eepp/ui/uitabwidget.cpp | 2 +- src/eepp/ui/uitextinput.cpp | 2 +- src/eepp/ui/uitouchdraggablewidget.cpp | 4 + src/tools/ecode/appconfig.cpp | 13 +- src/tools/ecode/appconfig.hpp | 9 +- src/tools/ecode/ecode.cpp | 43 ++--- src/tools/ecode/ecode.hpp | 20 +- src/tools/ecode/projectbuild.cpp | 172 +++++++++++++++++- src/tools/ecode/projectbuild.hpp | 28 ++- .../ecode/statusbuildoutputcontroller.cpp | 17 ++ .../ecode/statusbuildoutputcontroller.hpp | 2 + 24 files changed, 426 insertions(+), 94 deletions(-) diff --git a/docs/articles/cssspecification.md b/docs/articles/cssspecification.md index 296a33c06..4fd214d9e 100644 --- a/docs/articles/cssspecification.md +++ b/docs/articles/cssspecification.md @@ -856,6 +856,26 @@ the load can't be determined. --- +### inner-widget-orientation + +PushButton can contain 3 widgets: the text (textbox), the icon, and a custom extra +item. And with these 3 items does its own layouting. This property allows configuring the order +in which these items are displayed/sorted inside the button. + +* Applicable to: EE::UI::UIPushButton (PushButton) and any element that extends it: UIMenuItem, UISelectButton (SelectButton), UITableCell , UITableHeaderColumn. +* Data Type: [string-list](#string-list-data-type) +* Value List (Widget is the custom widget): + * `widgeticontextbox`: Widget | Icon | TextBox + * `widgettextboxicon`: Widget | TextBox | Icon + * `icontextboxwidget`: Icon | TextBox | Widget + * `iconwidgettextbox`: Icon | Widget | TextBox + * `textboxiconwidget`: TextBox | Icon | Widget + * `textboxwidgeticon`: TextBox | Widget | Icon + +* Default value: `widgeticontextbox` + +--- + ### layout-gravity The layout gravity defines how the element gravitates againts its parent (when possible). Gravity diff --git a/include/eepp/ui/css/propertydefinition.hpp b/include/eepp/ui/css/propertydefinition.hpp index 86e5ce7a4..00880c69f 100644 --- a/include/eepp/ui/css/propertydefinition.hpp +++ b/include/eepp/ui/css/propertydefinition.hpp @@ -211,6 +211,7 @@ enum class PropertyId : Uint32 { GravityOwner = String::hash( "gravity-owner" ), Href = String::hash( "href" ), Focusable = String::hash( "focusable" ), + InnerWidgetOrientation = String::hash( "inner-widget-orientation" ), }; enum class PropertyType : Uint32 { diff --git a/include/eepp/ui/uilistbox.hpp b/include/eepp/ui/uilistbox.hpp index cc4efa76f..67146e729 100644 --- a/include/eepp/ui/uilistbox.hpp +++ b/include/eepp/ui/uilistbox.hpp @@ -75,7 +75,9 @@ class EE_API UIListBox : public UITouchDraggableWidget { const Uint32& getRowHeight() const; - Uint32 getCount(); + Uint32 getCount() const; + + bool isEmpty() const; void setSelected( Uint32 Index ); @@ -104,6 +106,8 @@ class EE_API UIListBox : public UITouchDraggableWidget { virtual std::vector getPropertiesImplemented() const; + Uint32 getMaxTextWidth() const; + protected: friend class UIListBoxItem; friend class UIItemContainer; diff --git a/include/eepp/ui/uipushbutton.hpp b/include/eepp/ui/uipushbutton.hpp index 408763d53..8b78863f1 100644 --- a/include/eepp/ui/uipushbutton.hpp +++ b/include/eepp/ui/uipushbutton.hpp @@ -7,10 +7,21 @@ namespace EE { namespace UI { -enum class InnerWidgetOrientation { Left, Right, Center }; +enum class InnerWidgetOrientation { + WidgetIconTextBox, + WidgetTextBoxIcon, + IconTextBoxWidget, + IconWidgetTextBox, + TextBoxIconWidget, + TextBoxWidgetIcon, +}; class EE_API UIPushButton : public UIWidget { public: + static InnerWidgetOrientation innerWidgetOrientationFromString( std::string iwo ); + + static std::string innerWidgetOrientationToString( const InnerWidgetOrientation& orientation ); + static UIPushButton* New(); static UIPushButton* NewWithTag( const std::string& tag ); @@ -70,7 +81,7 @@ class EE_API UIPushButton : public UIWidget { UIImage* mIcon; UITextView* mTextBox; Sizei mIconMinSize; - InnerWidgetOrientation mInnerWidgetOrientation{ InnerWidgetOrientation::Right }; + InnerWidgetOrientation mInnerWidgetOrientation{ InnerWidgetOrientation::IconTextBoxWidget }; bool mTextAsFallback{ false }; UIPushButton(); diff --git a/include/eepp/ui/uitextinput.hpp b/include/eepp/ui/uitextinput.hpp index 6e14fe5bb..0a7d09665 100644 --- a/include/eepp/ui/uitextinput.hpp +++ b/include/eepp/ui/uitextinput.hpp @@ -129,6 +129,7 @@ class EE_API UITextInput : public UITextView, public TextDocument::Client { bool mMouseDown; bool mCreateDefaultContextMenuOptions{ true }; bool mEscapePastedText{ false }; + bool mEnabledCreateContextMenu{ true }; Uint32 mMaxLength{ 0 }; KeyBindings mKeyBindings; Clock mLastDoubleClick; diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index 0ec12ba70..de95f0dd3 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -62,7 +62,7 @@ class EE_API UITreeViewCell : public UITableCell { UITableCell( "treeview::cell", newTextViewCb ) { mTextBox->setElementTag( mTag + "::text" ); mIcon->setElementTag( mTag + "::icon" ); - mInnerWidgetOrientation = InnerWidgetOrientation::Left; + mInnerWidgetOrientation = InnerWidgetOrientation::WidgetIconTextBox; auto cb = [&]( const Event* ) { updateLayout(); }; mImage = UIImage::NewWithTag( mTag + "::expander" ); mImage->setScaleType( UIScaleType::FitInside ) diff --git a/src/eepp/system/process.cpp b/src/eepp/system/process.cpp index 7cf5783b4..121f2a412 100644 --- a/src/eepp/system/process.cpp +++ b/src/eepp/system/process.cpp @@ -182,7 +182,11 @@ bool Process::join( int* const returnCodeOut ) { bool Process::kill() { eeASSERT( mProcess != nullptr ); - return PROCESS_PTR->alive ? 0 == subprocess_terminate( PROCESS_PTR ) : false; + if ( PROCESS_PTR->alive ) { + destroy(); + return 0 == subprocess_terminate( PROCESS_PTR ); + } + return false; } bool Process::destroy() { diff --git a/src/eepp/ui/css/stylesheetspecification.cpp b/src/eepp/ui/css/stylesheetspecification.cpp index 5dc798c4a..a7856edbb 100644 --- a/src/eepp/ui/css/stylesheetspecification.cpp +++ b/src/eepp/ui/css/stylesheetspecification.cpp @@ -127,21 +127,25 @@ void StyleSheetSpecification::registerDefaultProperties() { registerProperty( "flags", "" ); registerProperty( "margin-top", "0px" ) .setType( PropertyType::NumberLength ) + .addAlias( "margin_top" ) .addAlias( "layout-margin-top" ) .addAlias( "layout_margintop" ) .setRelativeTarget( PropertyRelativeTarget::ContainingBlockHeight ); registerProperty( "margin-left", "0px" ) .setType( PropertyType::NumberLength ) + .addAlias( "margin_left" ) .addAlias( "layout-margin-left" ) .addAlias( "layout_marginleft" ) .setRelativeTarget( PropertyRelativeTarget::ContainingBlockWidth ); registerProperty( "margin-right", "0px" ) .setType( PropertyType::NumberLength ) + .addAlias( "margin_right" ) .addAlias( "layout-margin-right" ) .addAlias( "layout_marginright" ) .setRelativeTarget( PropertyRelativeTarget::ContainingBlockWidth ); registerProperty( "margin-bottom", "0px" ) .setType( PropertyType::NumberLength ) + .addAlias( "margin_bottom" ) .addAlias( "layout-margin-bottom" ) .addAlias( "layout_marginbottom" ) .setRelativeTarget( PropertyRelativeTarget::ContainingBlockHeight ); @@ -393,6 +397,9 @@ void StyleSheetSpecification::registerDefaultProperties() { registerProperty( "href", "" ).setType( PropertyType::String ); registerProperty( "focusable", "true" ).setType( PropertyType::Bool ); + registerProperty( "inner-widget-orientation", "widgeticontextbox" ) + .setType( PropertyType::String ); + // Shorthands registerShorthand( "margin", { "margin-top", "margin-right", "margin-bottom", "margin-left" }, "box" ); diff --git a/src/eepp/ui/uidropdownlist.cpp b/src/eepp/ui/uidropdownlist.cpp index 18ea6fcc5..7847231fe 100644 --- a/src/eepp/ui/uidropdownlist.cpp +++ b/src/eepp/ui/uidropdownlist.cpp @@ -20,6 +20,7 @@ UIDropDownList* UIDropDownList::New() { UIDropDownList::UIDropDownList( const std::string& tag ) : UITextInput( tag ), mListBox( NULL ), mFriendNode( NULL ) { + mEnabledCreateContextMenu = false; setClipType( ClipType::ContentBox ); setFlags( UI_AUTO_SIZE | UI_AUTO_PADDING ); unsetFlags( UI_TEXT_SELECTION_ENABLED ); @@ -139,15 +140,18 @@ void UIDropDownList::showList() { Float sliderValue = mListBox->getVerticalScrollBar()->getValue(); + Float width = + NULL != mFriendNode ? mFriendNode->getSize().getWidth() : getSize().getWidth(); + mListBox->setSize( - NULL != mFriendNode ? mFriendNode->getSize().getWidth() : getSize().getWidth(), - (Int32)( std::min( mListBox->getCount(), mStyleConfig.MaxNumVisibleItems ) * - mListBox->getRowHeight() ) + - tPadding.Top + tPadding.Bottom + - ( mListBox->getHorizontalScrollBar() && - mListBox->getHorizontalScrollBar()->isVisible() - ? mListBox->getHorizontalScrollBar()->getSize().getHeight() - : 0.f ) ); + width, (Int32)( eemin( mListBox->getCount(), mStyleConfig.MaxNumVisibleItems ) * + mListBox->getRowHeight() ) + + tPadding.Top + tPadding.Bottom + + ( mListBox->getHorizontalScrollBar() && + mListBox->getHorizontalScrollBar()->isVisible() && + PixelDensity::dpToPx( width ) < mListBox->getMaxTextWidth() + ? mListBox->getHorizontalScrollBar()->getSize().getHeight() + : 0.f ) ); mListBox->getVerticalScrollBar()->setValue( sliderValue ); @@ -259,6 +263,7 @@ void UIDropDownList::onItemSelected( const Event* ) { messagePost( &Msg ); sendCommonEvent( Event::OnItemSelected ); + sendCommonEvent( Event::OnValueChange ); } void UIDropDownList::show() { diff --git a/src/eepp/ui/uilistbox.cpp b/src/eepp/ui/uilistbox.cpp index b67f1c2a1..67c7b9d11 100644 --- a/src/eepp/ui/uilistbox.cpp +++ b/src/eepp/ui/uilistbox.cpp @@ -803,10 +803,14 @@ const Uint32& UIListBox::getRowHeight() const { return mRowHeight; } -Uint32 UIListBox::getCount() { +Uint32 UIListBox::getCount() const { return (Uint32)mItems.size(); } +bool UIListBox::isEmpty() const { + return mItems.empty(); +} + void UIListBox::setSelected( const String& Text ) { setSelected( getItemIndex( Text ) ); } @@ -1064,6 +1068,10 @@ std::vector UIListBox::getPropertiesImplemented() const { return props; } +Uint32 UIListBox::getMaxTextWidth() const { + return mMaxTextWidth; +} + bool UIListBox::applyProperty( const StyleSheetProperty& attribute ) { if ( !checkPropertyDefinition( attribute ) ) return false; diff --git a/src/eepp/ui/uipushbutton.cpp b/src/eepp/ui/uipushbutton.cpp index 5459c43e2..c98e5bd20 100644 --- a/src/eepp/ui/uipushbutton.cpp +++ b/src/eepp/ui/uipushbutton.cpp @@ -8,6 +8,42 @@ namespace EE { namespace UI { +InnerWidgetOrientation UIPushButton::innerWidgetOrientationFromString( std::string iwo ) { + String::toLowerInPlace( iwo ); + if ( iwo == "widgeticontextbox" ) + return InnerWidgetOrientation::WidgetIconTextBox; + if ( iwo == "widgettextboxicon" ) + return InnerWidgetOrientation::WidgetTextBoxIcon; + if ( iwo == "icontextboxwidget" ) + return InnerWidgetOrientation::IconTextBoxWidget; + if ( iwo == "iconwidgettextbox" ) + return InnerWidgetOrientation::IconWidgetTextBox; + if ( iwo == "textboxiconwidget" ) + return InnerWidgetOrientation::TextBoxIconWidget; + if ( iwo == "textboxwidgeticon" ) + return InnerWidgetOrientation::TextBoxWidgetIcon; + return InnerWidgetOrientation::WidgetIconTextBox; +} + +std::string +UIPushButton::innerWidgetOrientationToString( const InnerWidgetOrientation& orientation ) { + switch ( orientation ) { + case InnerWidgetOrientation::WidgetIconTextBox: + return "widgeticontextbox"; + case InnerWidgetOrientation::WidgetTextBoxIcon: + return "widgettextboxicon"; + case InnerWidgetOrientation::IconTextBoxWidget: + return "icontextboxwidget"; + case InnerWidgetOrientation::IconWidgetTextBox: + return "iconwidgettextbox"; + case InnerWidgetOrientation::TextBoxIconWidget: + return "textboxiconwidget"; + case InnerWidgetOrientation::TextBoxWidgetIcon: + return "textboxwidgeticon"; + } + return "widgeticontextbox"; +} + UIPushButton* UIPushButton::New() { return eeNew( UIPushButton, () ); } @@ -216,21 +252,35 @@ Vector2f UIPushButton::packLayout( const std::vector& widgets, const UIWidget* UIPushButton::getFirstInnerItem() const { switch ( mInnerWidgetOrientation ) { - case InnerWidgetOrientation::Left: + case InnerWidgetOrientation::WidgetIconTextBox: return getExtraInnerWidget() && getExtraInnerWidget()->isVisible() ? getExtraInnerWidget() : mIcon; - case InnerWidgetOrientation::Center: + case InnerWidgetOrientation::IconWidgetTextBox: return mIcon->isVisible() ? mIcon : ( getExtraInnerWidget() && getExtraInnerWidget()->isVisible() ? getExtraInnerWidget() : mTextBox ); - case InnerWidgetOrientation::Right: + case InnerWidgetOrientation::IconTextBoxWidget: if ( mIcon->isVisible() ) return mIcon; else return mTextBox; + case InnerWidgetOrientation::WidgetTextBoxIcon: + return ( getExtraInnerWidget() && getExtraInnerWidget()->isVisible() ) + ? getExtraInnerWidget() + : mTextBox; + case InnerWidgetOrientation::TextBoxIconWidget: + if ( mTextBox->isVisible() ) + return mTextBox; + if ( mIcon->isVisible() ) + return mIcon; + break; + case InnerWidgetOrientation::TextBoxWidgetIcon: + if ( mTextBox->isVisible() ) + return mTextBox; + break; } return mChild->isWidget() ? mChild->asType() : nullptr; } @@ -239,15 +289,24 @@ Sizef UIPushButton::updateLayout() { Sizef size; Rectf autoPadding = calculatePadding(); switch ( mInnerWidgetOrientation ) { - case InnerWidgetOrientation::Left: + case InnerWidgetOrientation::WidgetIconTextBox: size = packLayout( { getExtraInnerWidget(), mIcon, mTextBox }, autoPadding ); break; - case InnerWidgetOrientation::Center: + case InnerWidgetOrientation::IconWidgetTextBox: size = packLayout( { mIcon, getExtraInnerWidget(), mTextBox }, autoPadding ); break; - case InnerWidgetOrientation::Right: + case InnerWidgetOrientation::IconTextBoxWidget: size = packLayout( { mIcon, mTextBox, getExtraInnerWidget() }, autoPadding ); break; + case InnerWidgetOrientation::WidgetTextBoxIcon: + size = packLayout( { getExtraInnerWidget(), mTextBox, mIcon }, autoPadding ); + break; + case InnerWidgetOrientation::TextBoxIconWidget: + size = packLayout( { mTextBox, mIcon, getExtraInnerWidget() }, autoPadding ); + break; + case InnerWidgetOrientation::TextBoxWidgetIcon: + size = packLayout( { mTextBox, getExtraInnerWidget(), mIcon }, autoPadding ); + break; } return size.ceil(); } @@ -401,15 +460,24 @@ Sizef UIPushButton::getContentSize() const { Sizef size; Rectf autoPadding = calculatePadding(); switch ( mInnerWidgetOrientation ) { - case InnerWidgetOrientation::Left: + case InnerWidgetOrientation::WidgetIconTextBox: size = calcLayoutSize( { getExtraInnerWidget(), mIcon, mTextBox }, autoPadding ); break; - case InnerWidgetOrientation::Center: + case InnerWidgetOrientation::IconWidgetTextBox: size = calcLayoutSize( { mIcon, getExtraInnerWidget(), mTextBox }, autoPadding ); break; - case InnerWidgetOrientation::Right: + case InnerWidgetOrientation::IconTextBoxWidget: size = calcLayoutSize( { mIcon, mTextBox, getExtraInnerWidget() }, autoPadding ); break; + case InnerWidgetOrientation::TextBoxIconWidget: + size = calcLayoutSize( { mIcon, mTextBox, getExtraInnerWidget() }, autoPadding ); + break; + case InnerWidgetOrientation::TextBoxWidgetIcon: + size = calcLayoutSize( { mTextBox, getExtraInnerWidget(), mIcon }, autoPadding ); + break; + case InnerWidgetOrientation::WidgetTextBoxIcon: + size = calcLayoutSize( { getExtraInnerWidget(), mTextBox, mIcon }, autoPadding ); + break; } if ( getSkin() ) size.x += PixelDensity::dpToPxI( getSkin()->getBorderSize().Left + @@ -436,6 +504,8 @@ std::string UIPushButton::getPropertyString( const PropertyDefinition* propertyD return ""; switch ( propertyDef->getPropertyId() ) { + case PropertyId::InnerWidgetOrientation: + return innerWidgetOrientationToString( mInnerWidgetOrientation ); case PropertyId::Text: return getText().toUtf8(); case PropertyId::Icon: @@ -505,6 +575,9 @@ bool UIPushButton::applyProperty( const StyleSheetProperty& attribute ) { } switch ( attribute.getPropertyDefinition()->getPropertyId() ) { + case PropertyId::InnerWidgetOrientation: + setInnerWidgetOrientation( innerWidgetOrientationFromString( attribute.asString() ) ); + break; case PropertyId::Text: if ( NULL != mSceneNode && mSceneNode->isUISceneNode() ) setText( getUISceneNode()->getTranslatorString( attribute.asString() ) ); diff --git a/src/eepp/ui/uitab.cpp b/src/eepp/ui/uitab.cpp index 0fa6778ac..8e518d445 100644 --- a/src/eepp/ui/uitab.cpp +++ b/src/eepp/ui/uitab.cpp @@ -81,21 +81,23 @@ Uint32 UITab::onDrag( const Vector2f& pos, const Uint32&, const Sizef& dragDiff } } - setEnabled( false ); - Node* overFind = getUISceneNode()->overFind( pos ); - setEnabled( true ); + if ( tabW->getAllowDragAndDropTabs() ) { + setEnabled( false ); + Node* overFind = getUISceneNode()->overFind( pos ); + setEnabled( true ); - if ( overFind && overFind->isType( UI_TYPE_WIDGET ) ) { - UIWidget* widget = overFind->asType()->acceptsDropOfWidgetInTree( this ); + if ( overFind && overFind->isType( UI_TYPE_WIDGET ) ) { + UIWidget* widget = overFind->asType()->acceptsDropOfWidgetInTree( this ); - if ( mCurDropWidget ) { - mCurDropWidget->writeNodeFlag( NODE_FLAG_DROPPABLE_HOVERING, 0 ); - mCurDropWidget = nullptr; - } + if ( mCurDropWidget ) { + mCurDropWidget->writeNodeFlag( NODE_FLAG_DROPPABLE_HOVERING, 0 ); + mCurDropWidget = nullptr; + } - if ( widget ) { - mCurDropWidget = widget; - mCurDropWidget->writeNodeFlag( NODE_FLAG_DROPPABLE_HOVERING, 1 ); + if ( widget ) { + mCurDropWidget = widget; + mCurDropWidget->writeNodeFlag( NODE_FLAG_DROPPABLE_HOVERING, 1 ); + } } } diff --git a/src/eepp/ui/uitableheadercolumn.cpp b/src/eepp/ui/uitableheadercolumn.cpp index 0f89294f0..74b5c98ef 100644 --- a/src/eepp/ui/uitableheadercolumn.cpp +++ b/src/eepp/ui/uitableheadercolumn.cpp @@ -8,7 +8,7 @@ UITableHeaderColumn::UITableHeaderColumn( const std::string& parentTag, UIAbstra const size_t& colIndex ) : UIPushButton( parentTag + "::header::column" ), mView( view ), mColIndex( colIndex ) { setDragEnabled( true ); - mInnerWidgetOrientation = InnerWidgetOrientation::Right; + mInnerWidgetOrientation = InnerWidgetOrientation::IconTextBoxWidget; auto cb = [&]( const Event* ) { updateLayout(); }; mImage = UIImage::NewWithTag( mTag + "::arrow" ); mImage->setLayoutSizePolicy( SizePolicy::WrapContent, SizePolicy::WrapContent ) diff --git a/src/eepp/ui/uitabwidget.cpp b/src/eepp/ui/uitabwidget.cpp index 70c76165a..5173a3827 100644 --- a/src/eepp/ui/uitabwidget.cpp +++ b/src/eepp/ui/uitabwidget.cpp @@ -901,7 +901,7 @@ Uint32 UITabWidget::onMessage( const NodeMessage* msg ) { const NodeDropMessage* dropMsg = static_cast( msg ); if ( dropMsg->getDroppedNode()->isType( UI_TYPE_TAB ) ) { UITab* tab = dropMsg->getDroppedNode()->asType(); - if ( tab->getTabWidget() != this ) { + if ( tab->getTabWidget() != this && tab->getTabWidget()->getAllowDragAndDropTabs() ) { tab->getTabWidget()->removeTab( tab, false, false, false ); add( tab ); setTabSelected( tab ); diff --git a/src/eepp/ui/uitextinput.cpp b/src/eepp/ui/uitextinput.cpp index fbee4210a..68a0a8cec 100644 --- a/src/eepp/ui/uitextinput.cpp +++ b/src/eepp/ui/uitextinput.cpp @@ -314,7 +314,7 @@ Uint32 UITextInput::onMouseUp( const Vector2i& position, const Uint32& flags ) { mMouseDown = false; getUISceneNode()->getWindow()->getInput()->captureMouse( false ); } - } else if ( ( flags & EE_BUTTON_RMASK ) ) { + } else if ( ( flags & EE_BUTTON_RMASK ) && mEnabledCreateContextMenu ) { onCreateContextMenu( position, flags ); } return UITextView::onMouseUp( position, flags ); diff --git a/src/eepp/ui/uitouchdraggablewidget.cpp b/src/eepp/ui/uitouchdraggablewidget.cpp index 95c8a4e10..4615f4852 100644 --- a/src/eepp/ui/uitouchdraggablewidget.cpp +++ b/src/eepp/ui/uitouchdraggablewidget.cpp @@ -137,6 +137,10 @@ Uint32 UITouchDraggableWidget::onMessage( const NodeMessage* msg ) { mTouchDragPoint = getEventDispatcher()->getMousePosf(); mTouchDragAcceleration = Vector2f( 0, 0 ); return 1; + } else if ( msg->getMsg() == NodeMessage::MouseUp && ( msg->getFlags() & EE_BUTTON_LMASK ) && isTouchDragging() && isTouchOverAllowedChilds() ) { + setTouchDragging( false ); + getEventDispatcher()->setNodeDragging( nullptr ); + return 1; } return 0; } diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index 3b9e0a0eb..d66a9f3cb 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -348,8 +348,8 @@ json saveNode( Node* node ) { } void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, - const std::string& configPath, - const ProjectDocumentConfig& docConfig ) { + const std::string& configPath, const ProjectDocumentConfig& docConfig, + const ProjectBuildConfiguration& buildConfig ) { FileSystem::dirAddSlashAtEnd( projectFolder ); std::vector editors = editorSplitter->getAllEditors(); std::vector paths; @@ -376,6 +376,8 @@ void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* ed TextDocument::lineEndingToString( docConfig.doc.lineEndings ) ); ini.setValueI( "document", "tab_width", docConfig.doc.tabWidth ); ini.setValueI( "document", "line_breaking_column", docConfig.doc.lineBreakingColumn ); + ini.setValue( "build", "build_name", buildConfig.buildName ); + ini.setValue( "build", "build_type", buildConfig.buildType ); ini.setValue( "nodes", "documents", saveNode( editorSplitter->getBaseLayout()->getFirstChild() ).dump() ); ini.deleteKey( "files" ); @@ -510,6 +512,13 @@ void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* ed docConfig.doc.lineBreakingColumn = eemax( 0, ini.getValueI( "document", "line_breaking_column", 100 ) ); + if ( app->getProjectBuildManager() ) { + ProjectBuildConfiguration prjCfg; + prjCfg.buildName = ini.getValue( "build", "build_name", "" ); + prjCfg.buildType = ini.getValue( "build", "build_type", "" ); + app->getProjectBuildManager()->setConfig( prjCfg ); + } + if ( ini.keyValueExists( "nodes", "documents" ) ) { json j; try { diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index 6504c0eda..63d652591 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -109,6 +109,12 @@ struct ProjectDocumentConfig { ProjectDocumentConfig( const DocumentConfig& doc ) { this->doc = doc; } }; +struct ProjectBuildConfiguration { + ProjectBuildConfiguration() {} + std::string buildName; + std::string buildType; +}; + class NewTerminalOrientation { public: enum Orientation { Same, Vertical, Horizontal }; @@ -175,7 +181,8 @@ class AppConfig { const GlobalSearchBarConfig& globalSearchBarConfig, PluginManager* pluginManager ); void saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, - const std::string& configPath, const ProjectDocumentConfig& docConfig ); + const std::string& configPath, const ProjectDocumentConfig& docConfig, + const ProjectBuildConfiguration& buildConfig ); void loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, const std::string& configPath, ProjectDocumentConfig& docConfig, diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 2a5525c97..fce6953c8 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -81,7 +81,9 @@ bool App::onCloseRequestCallback( EE::Window::Window* ) { .unescape() ); msgBox->addEventListener( Event::OnConfirm, [&]( const Event* ) { if ( !mCurrentProject.empty() ) - mConfig.saveProject( mCurrentProject, mSplitter, mConfigPath, mProjectDocConfig ); + mConfig.saveProject( mCurrentProject, mSplitter, mConfigPath, mProjectDocConfig, + mProjectBuildManager ? mProjectBuildManager->getConfig() + : ProjectBuildConfiguration() ); saveConfig(); mWindow->close(); } ); @@ -92,7 +94,9 @@ bool App::onCloseRequestCallback( EE::Window::Window* ) { return false; } else { if ( !mCurrentProject.empty() ) - mConfig.saveProject( mCurrentProject, mSplitter, mConfigPath, mProjectDocConfig ); + mConfig.saveProject( mCurrentProject, mSplitter, mConfigPath, mProjectDocConfig, + mProjectBuildManager ? mProjectBuildManager->getConfig() + : ProjectBuildConfiguration() ); saveConfig(); return true; } @@ -752,6 +756,10 @@ ProjectBuildManager* App::getProjectBuildManager() const { return mProjectBuildManager.get(); } +UITabWidget* App::getSidePanel() const { + return mSidePanel; +} + void App::switchSidePanel() { mConfig.ui.showSidePanel = !mConfig.ui.showSidePanel; mSettings->getWindowMenu() @@ -1788,7 +1796,9 @@ void App::closeEditors() { mSplitter->removeTabWithOwnedWidgetId( "welcome_ecode" ); mStatusBar->setVisible( mConfig.ui.showStatusBar ); - mConfig.saveProject( mCurrentProject, mSplitter, mConfigPath, mProjectDocConfig ); + mConfig.saveProject( mCurrentProject, mSplitter, mConfigPath, mProjectDocConfig, + mProjectBuildManager ? mProjectBuildManager->getConfig() + : ProjectBuildConfiguration() ); std::vector editors = mSplitter->getAllEditors(); while ( !editors.empty() ) { UICodeEditor* editor = editors[0]; @@ -2864,11 +2874,11 @@ void App::loadFolder( const std::string& path ) { mCurrentProject = rpath; mPluginManager->setWorkspaceFolder( rpath ); - mProjectBuildManager = std::make_unique( rpath, mThreadPool ); - loadDirTree( rpath ); Clock projClock; + mProjectBuildManager = + std::make_unique( rpath, mThreadPool, mSidePanel, this ); mConfig.loadProject( rpath, mSplitter, mConfigPath, mProjectDocConfig, mThreadPool, this ); Log::info( "Load project took: %.2f ms", projClock.getElapsedTime().asMilliseconds() ); @@ -3432,7 +3442,9 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe { "cursor-pointer", 0xec09 }, { "drive", 0xedf8 }, { "refresh", 0xf064 }, - { "hearth-pulse", 0xee10 } }; + { "hearth-pulse", 0xee10 }, + { "add", 0xea12 }, + { "hammer", 0xedee } }; for ( const auto& icon : icons ) iconTheme->add( UIGlyphIcon::New( icon.first, iconFont, icon.second ) ); @@ -3825,8 +3837,6 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { { "convert-lang-output" }, "" ); args::Flag disableFileLogs( parser, "disable-file-logs", "Disables writing logs to a log file", { "disable-file-logs" } ); - args::ValueFlag testBuild( parser, "test-build-path", "Test project build", - { "test-build-path" } ); std::vector args; try { @@ -3848,23 +3858,6 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { return EXIT_FAILURE; } - if ( !testBuild.Get().empty() ) { - ProjectBuildManager bm( testBuild.Get(), {} ); - auto i18n = []( const std::string&, const String& def ) -> String { return def; }; - auto res = bm.generateBuildCommands( "ecode", i18n, "debug" ); - if ( res.isValid() ) { - for ( const auto& cmd : res.cmds ) { - for ( const auto& env : cmd.envs ) - std::cout << "export " << env.first << "=" << env.second << "\n"; - std::cout << cmd.workingDir << " " << cmd.cmd << " " << cmd.args << "\n"; - } - } else { - std::cout << res.errorMsg.toUtf8() << "\n"; - } - - return EXIT_SUCCESS; - } - if ( convertLangPath && !convertLangPath.Get().empty() ) { Sys::windowAttachConsole(); IOStreamFile sfile( convertLangPath.Get() ); diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index ab0b40913..40f16a3ab 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -235,22 +235,8 @@ class App : public UICodeEditorSplitter::Client { t.setCommand( "toggle-locatebar", [&] { mUniversalLocator->toggleLocateBar(); } ); t.setCommand( "open-command-palette", [&] { mUniversalLocator->showCommandPalette(); } ); t.setCommand( "project-build-start", [&] { - if ( mProjectBuildManager && !mProjectBuildManager->isBuilding() && - !mProjectBuildManager->getBuilds().empty() ) { - std::string os = String::toLower( Sys::getPlatform() ); - const ProjectBuild* build = nullptr; - for ( const auto& buildIt : mProjectBuildManager->getBuilds() ) { - if ( buildIt.second.isOSSupported( os ) ) { - build = &buildIt.second; - } - } - - if ( build ) { - mStatusBuildOutputController->run( - build->getName(), - !build->buildTypes().empty() ? *build->buildTypes().begin() : "", - mProjectBuildManager->getOutputParser( build->getName() ) ); - } + if ( mProjectBuildManager && mStatusBuildOutputController ) { + mProjectBuildManager->buildCurrentConfig( mStatusBuildOutputController.get() ); } } ); t.setCommand( "project-build-cancel", [&] { @@ -404,6 +390,8 @@ class App : public UICodeEditorSplitter::Client { ProjectBuildManager* getProjectBuildManager() const; + UITabWidget* getSidePanel() const; + protected: std::vector mArgs; EE::Window::Window* mWindow{ nullptr }; diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index 4b7163cee..6479309a8 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -1,14 +1,20 @@ #include "projectbuild.hpp" +#include "ecode.hpp" #include "scopedop.hpp" +#include "statusbuildoutputcontroller.hpp" #include +#include #include #include #include #include +#include +#include +#include #include using json = nlohmann::json; -using namespace EE; +using namespace EE::Scene; /** @return The process environment variables */ static std::unordered_map getEnvironmentVariables() { @@ -73,8 +79,9 @@ bool ProjectBuild::isOSSupported( const std::string& os ) const { } ProjectBuildManager::ProjectBuildManager( const std::string& projectRoot, - std::shared_ptr pool ) : - mProjectRoot( projectRoot ), mThreadPool( pool ) { + std::shared_ptr pool, UITabWidget* sidePanel, + App* app ) : + mProjectRoot( projectRoot ), mThreadPool( pool ), mSidePanel( sidePanel ), mApp( app ) { FileSystem::dirAddSlashAtEnd( mProjectRoot ); if ( mThreadPool ) { @@ -85,6 +92,9 @@ ProjectBuildManager::ProjectBuildManager( const std::string& projectRoot, } ProjectBuildManager::~ProjectBuildManager() { + if ( mUISceneNode && !SceneManager::instance()->isShuttingDown() && mSidePanel && mTab ) { + mSidePanel->removeTab( mTab ); + } mShuttingDown = true; mCancelBuild = true; while ( mLoading ) @@ -250,6 +260,10 @@ bool ProjectBuildManager::load() { mBuilds.insert( { build.key(), std::move( b ) } ); } + if ( mSidePanel ) { + mSidePanel->runOnMainThread( [this]() { buildSidePanelTab(); } ); + } + mLoaded = true; return true; } @@ -301,6 +315,34 @@ ProjectBuildOutputParser ProjectBuildManager::getOutputParser( const std::string void ProjectBuildManager::cancelBuild() { mCancelBuild = true; + if ( mProcess ) { + mProcess->destroy(); + mProcess->kill(); + } +} + +ProjectBuildConfiguration ProjectBuildManager::getConfig() const { + return mConfig; +} + +void ProjectBuildManager::setConfig( const ProjectBuildConfiguration& config ) { + mConfig = config; +} + +void ProjectBuildManager::buildCurrentConfig( StatusBuildOutputController* sboc ) { + if ( sboc && !isBuilding() && !getBuilds().empty() ) { + std::string os = String::toLower( Sys::getPlatform() ); + const ProjectBuild* build = nullptr; + for ( const auto& buildIt : getBuilds() ) { + if ( buildIt.second.getName() == mConfig.buildName ) { + build = &buildIt.second; + } + } + + if ( build ) { + sboc->run( build->getName(), mConfig.buildType, getOutputParser( build->getName() ) ); + } + } } void ProjectBuildManager::runBuild( const std::string& buildName, const std::string& buildType, @@ -340,7 +382,7 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str int c = 0; for ( const auto& cmd : res.cmds ) { int progress = c > 0 ? c / (Float)res.cmds.size() : 0; - Process process; + mProcess = std::make_unique(); auto options = Process::SearchUserPath | Process::NoWindow | Process::CombinedStdoutStderr; std::unordered_map env; if ( !cmd.config.clearSysEnv ) { @@ -353,7 +395,7 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str } else { env = cmd.envs; } - if ( process.create( cmd.cmd, cmd.args, options, env, cmd.workingDir ) ) { + if ( mProcess->create( cmd.cmd, cmd.args, options, env, cmd.workingDir ) ) { if ( progressFn ) progressFn( progress, Sys::getDateTimeStr() + ": " + @@ -365,14 +407,14 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str unsigned bytesRead = 0; int returnCode; do { - bytesRead = process.readStdOut( buffer ); + bytesRead = mProcess->readStdOut( buffer ); std::string data( buffer.substr( 0, bytesRead ) ); if ( progressFn ) progressFn( progress, std::move( data ) ); - } while ( bytesRead != 0 && process.isAlive() && !mShuttingDown && !mCancelBuild ); + } while ( bytesRead != 0 && mProcess->isAlive() && !mShuttingDown && !mCancelBuild ); if ( mShuttingDown || mCancelBuild ) { - process.kill(); + mProcess->kill(); mCancelBuild = false; printElapsed(); if ( doneFn ) @@ -380,8 +422,8 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str return; } - process.join( &returnCode ); - process.destroy(); + mProcess->join( &returnCode ); + mProcess->destroy(); if ( returnCode != EXIT_SUCCESS ) { if ( progressFn ) { @@ -421,4 +463,114 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str doneFn( EXIT_SUCCESS ); } +void ProjectBuildManager::buildSidePanelTab() { + mUISceneNode = mSidePanel->getUISceneNode(); + UIIcon* icon = mUISceneNode->findIcon( "symbol-property" ); + UIWidget* node = mUISceneNode->loadLayoutFromString( + R"html( + + + + + + + + + + + + + + + + )html" ); + mTab = mSidePanel->add( mUISceneNode->getTranslatorStringFromKey( "build", "Build" ), node, + icon ? icon->getSize( PixelDensity::dpToPx( 12 ) ) : nullptr ); + mTab->setTextAsFallback( true ); + + updateSidePanelTab(); +} + +void ProjectBuildManager::updateSidePanelTab() { + UIWidget* buildTab = mTab->getOwnedWidget()->find( "build_tab" ); + UIDropDownList* buildList = buildTab->find( "build_list" ); + UIPushButton* buildButton = buildTab->find( "build_button" ); + + buildList->getListBox()->clear(); + + String first = mConfig.buildName; + std::vector buildNamesItems; + for ( const auto& build : mBuilds ) { + buildNamesItems.push_back( build.first ); + if ( first.empty() ) + first = build.first; + } + + buildList->getListBox()->addListBoxItems( buildNamesItems ); + + if ( !first.empty() && buildList->getListBox()->getItemIndex( first ) != eeINDEX_NOT_FOUND ) { + buildList->getListBox()->setSelected( first ); + mConfig.buildName = first; + } else if ( !buildList->getListBox()->isEmpty() ) { + buildList->getListBox()->setSelected( 0L ); + mConfig.buildName = buildList->getListBox()->getItemSelectedText(); + } + + buildList->setEnabled( !buildList->getListBox()->isEmpty() ); + + UIDropDownList* buildTypeList = buildTab->find( "build_type_list" ); + + buildTypeList->getListBox()->clear(); + + first = buildList->getListBox()->getItemSelectedText(); + if ( !first.empty() ) { + auto foundIt = mBuilds.find( first ); + if ( foundIt != mBuilds.end() ) { + const auto& buildTypes = foundIt->second.buildTypes(); + std::vector buildTypesItems; + for ( const auto& buildType : buildTypes ) + buildTypesItems.emplace_back( buildType ); + buildTypeList->getListBox()->addListBoxItems( buildTypesItems ); + if ( buildTypeList->getListBox()->getItemIndex( mConfig.buildType ) != + eeINDEX_NOT_FOUND ) { + buildTypeList->getListBox()->setSelected( mConfig.buildType ); + mConfig.buildType = first; + } else if ( !buildTypeList->getListBox()->isEmpty() ) { + buildTypeList->getListBox()->setSelected( 0 ); + mConfig.buildType = buildTypeList->getListBox()->getItemSelectedText(); + } + } + } + buildTypeList->setEnabled( !buildTypeList->getListBox()->isEmpty() ); + + buildList->removeEventsOfType( Event::OnItemSelected ); + buildList->addEventListener( Event::OnItemSelected, [this, buildList]( const Event* ) { + mConfig.buildName = buildList->getListBox()->getItemSelectedText(); + } ); + + buildTypeList->removeEventsOfType( Event::OnItemSelected ); + buildTypeList->addEventListener( Event::OnItemSelected, [this, buildTypeList]( const Event* ) { + mConfig.buildType = buildTypeList->getListBox()->getItemSelectedText(); + } ); + + buildButton->addMouseClickListener( + [this]( const Event* ) { + if ( isBuilding() ) { + cancelBuild(); + } else { + buildCurrentConfig( mApp->getStatusBuildOutputController() ); + } + }, + MouseButton::EE_BUTTON_LEFT ); +} + } // namespace ecode diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index afbf47b14..544f410e6 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -1,7 +1,10 @@ #ifndef ECODE_PROJECTBUILD_HPP #define ECODE_PROJECTBUILD_HPP +#include "appconfig.hpp" +#include #include +#include #include #include #include @@ -11,9 +14,13 @@ using namespace EE; using namespace EE::System; +using namespace EE::UI; namespace ecode { +class App; +class StatusBuildOutputController; + /** reference: { "ecode": { @@ -133,7 +140,7 @@ class ProjectBuild { std::string mName; std::string mProjectRoot; - std::unordered_set mOS; + std::set mOS; std::set mBuildTypes; ProjectBuildSteps mBuild; ProjectBuildSteps mClean; @@ -177,7 +184,8 @@ using ProjectBuildi18nFn = class ProjectBuildManager { public: - ProjectBuildManager( const std::string& projectRoot, std::shared_ptr pool ); + ProjectBuildManager( const std::string& projectRoot, std::shared_ptr pool, + UITabWidget* sidePanel, App* app ); ~ProjectBuildManager(); @@ -206,11 +214,23 @@ class ProjectBuildManager { void cancelBuild(); + ProjectBuildConfiguration getConfig() const; + + void setConfig( const ProjectBuildConfiguration& config ); + + void buildCurrentConfig( StatusBuildOutputController* sboc ); + protected: std::string mProjectRoot; std::string mProjectFile; ProjectBuildMap mBuilds; + ProjectBuildConfiguration mConfig; std::shared_ptr mThreadPool; + UITabWidget* mSidePanel{ nullptr }; + UISceneNode* mUISceneNode{ nullptr }; + UITab* mTab{ nullptr }; + App* mApp{ nullptr }; + std::unique_ptr mProcess; bool mLoaded{ false }; bool mLoading{ false }; bool mBuilding{ false }; @@ -223,6 +243,10 @@ class ProjectBuildManager { const ProjectBuildDoneFn& doneFn = {} ); bool load(); + + void buildSidePanelTab(); + + void updateSidePanelTab(); }; } // namespace ecode diff --git a/src/tools/ecode/statusbuildoutputcontroller.cpp b/src/tools/ecode/statusbuildoutputcontroller.cpp index d9540c84f..494af3c43 100644 --- a/src/tools/ecode/statusbuildoutputcontroller.cpp +++ b/src/tools/ecode/statusbuildoutputcontroller.cpp @@ -72,6 +72,15 @@ static std::string getProjectOutputParserTypeToString( const ProjectOutputParser return "notice"; } +UIPushButton* StatusBuildOutputController::getBuildButton( App* app ) { + if ( app->getSidePanel() ) { + UIWidget* tab = app->getSidePanel()->find( "build_tab" ); + if ( tab ) + return tab->find( "build_button" ); + } + return nullptr; +} + void StatusBuildOutputController::run( const std::string& buildName, const std::string& buildType, const ProjectBuildOutputParser& outputParser ) { if ( !mApp->getProjectBuildManager() ) @@ -103,6 +112,10 @@ void StatusBuildOutputController::run( const std::string& buildName, const std:: mContainer->getDocument().setSyntaxDefinition( synDef ); mContainer->getVScrollBar()->setValue( 1.f ); + UIPushButton* buildButton = getBuildButton( mApp ); + if ( buildButton ) + buildButton->setText( mApp->i18n( "cancel_build", "Cancel Build" ) ); + auto res = pbm->run( buildName, [this]( const auto& key, const auto& def ) { return mApp->i18n( key, def ); }, buildType, @@ -131,6 +144,10 @@ void StatusBuildOutputController::run( const std::string& buildName, const std:: if ( scrollToBottom ) mContainer->setScrollY( mContainer->getMaxScroll().y ); } ); + + UIPushButton* buildButton = nullptr; + if ( ( buildButton = getBuildButton( mApp ) ) ) + buildButton->setText( mApp->i18n( "build", "Build" ) ); } ); if ( !res.isValid() ) { diff --git a/src/tools/ecode/statusbuildoutputcontroller.hpp b/src/tools/ecode/statusbuildoutputcontroller.hpp index 59b762756..62c7bfa49 100644 --- a/src/tools/ecode/statusbuildoutputcontroller.hpp +++ b/src/tools/ecode/statusbuildoutputcontroller.hpp @@ -38,6 +38,8 @@ class StatusBuildOutputController { UICodeEditor* mContainer{ nullptr }; UICodeEditor* createContainer(); + + UIPushButton* getBuildButton( App* app ); }; } // namespace ecode