eepp: Several fixes on widgets.

ecode: Build Side Panel WIP.
This commit is contained in:
Martín Lucas Golini
2023-04-10 18:58:34 -03:00
parent 5632caa5ff
commit 2c1811e519
24 changed files with 426 additions and 94 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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<PropertyId> getPropertiesImplemented() const;
Uint32 getMaxTextWidth() const;
protected:
friend class UIListBoxItem;
friend class UIItemContainer<UIListBox>;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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 )

View File

@@ -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() {

View File

@@ -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" );

View File

@@ -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() {

View File

@@ -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<PropertyId> UIListBox::getPropertiesImplemented() const {
return props;
}
Uint32 UIListBox::getMaxTextWidth() const {
return mMaxTextWidth;
}
bool UIListBox::applyProperty( const StyleSheetProperty& attribute ) {
if ( !checkPropertyDefinition( attribute ) )
return false;

View File

@@ -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<UIWidget*>& 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<UIWidget>() : 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() ) );

View File

@@ -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<UIWidget>()->acceptsDropOfWidgetInTree( this );
if ( overFind && overFind->isType( UI_TYPE_WIDGET ) ) {
UIWidget* widget = overFind->asType<UIWidget>()->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 );
}
}
}

View File

@@ -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 )

View File

@@ -901,7 +901,7 @@ Uint32 UITabWidget::onMessage( const NodeMessage* msg ) {
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 ) {
if ( tab->getTabWidget() != this && tab->getTabWidget()->getAllowDragAndDropTabs() ) {
tab->getTabWidget()->removeTab( tab, false, false, false );
add( tab );
setTabSelected( tab );

View File

@@ -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 );

View File

@@ -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;
}

View File

@@ -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<UICodeEditor*> editors = editorSplitter->getAllEditors();
std::vector<ProjectPath> 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 {

View File

@@ -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,

View File

@@ -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<UICodeEditor*> 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<ProjectBuildManager>( rpath, mThreadPool );
loadDirTree( rpath );
Clock projClock;
mProjectBuildManager =
std::make_unique<ProjectBuildManager>( 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<std::string> testBuild( parser, "test-build-path", "Test project build",
{ "test-build-path" } );
std::vector<std::string> 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() );

View File

@@ -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<std::string> mArgs;
EE::Window::Window* mWindow{ nullptr };

View File

@@ -1,14 +1,20 @@
#include "projectbuild.hpp"
#include "ecode.hpp"
#include "scopedop.hpp"
#include "statusbuildoutputcontroller.hpp"
#include <eepp/core/string.hpp>
#include <eepp/scene/scenemanager.hpp>
#include <eepp/system/clock.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/log.hpp>
#include <eepp/system/process.hpp>
#include <eepp/ui/uidropdownlist.hpp>
#include <eepp/ui/uiiconthememanager.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace EE;
using namespace EE::Scene;
/** @return The process environment variables */
static std::unordered_map<std::string, std::string> getEnvironmentVariables() {
@@ -73,8 +79,9 @@ bool ProjectBuild::isOSSupported( const std::string& os ) const {
}
ProjectBuildManager::ProjectBuildManager( const std::string& projectRoot,
std::shared_ptr<ThreadPool> pool ) :
mProjectRoot( projectRoot ), mThreadPool( pool ) {
std::shared_ptr<ThreadPool> 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<Process>();
auto options = Process::SearchUserPath | Process::NoWindow | Process::CombinedStdoutStderr;
std::unordered_map<std::string, std::string> 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(
<style>
#build_tab {
background-color: var(--list-back);
}
</style>
<ScrollView id="build_tab" lw="mp" lh="mp">
<vbox lw="mp" lh="wc" padding="4dp">
<TextView text="@string(build_settings, Build Settings)" font-size="15dp" />
<TextView text="@string(build_configuration, Build Configuration)" />
<DropDownList id="build_list" layout_width="mp" layout_height="wrap_content" margin-bottom="4dp" />
<!--
<hbox lw="mp" lh="wc">
<PushButton lw="0" lw8="0.5" id="build_edit" text="@string(edit_build, Edit Build)" margin-right="2dp" />
<PushButton lw="0" lw8="0.5" id="build_add" text="@string(add_build, Add Build)" margin-left="2dp" />
</hbox>
-->
<TextView text="@string(build_target, Build Target)" margin-top="8dp" />
<hbox lw="mp" lh="wc">
<DropDownList id="build_type_list" layout_width="0" layout_weight="1" layout_height="wrap_content" />
<!-- <PushButton id="build_type_add" text="@string(add_build, Add Build)" text-as-fallback="true" icon="icon(add, 12dp)" margin-left="2dp"/> -->
</hbox>
<PushButton id="build_button" lw="mp" lh="wc" text="@string(build, Build)" margin-top="8dp" icon="icon(hammer, 12dp)" />
</vbox>
</ScrollView>
)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<UIWidget>( "build_tab" );
UIDropDownList* buildList = buildTab->find<UIDropDownList>( "build_list" );
UIPushButton* buildButton = buildTab->find<UIPushButton>( "build_button" );
buildList->getListBox()->clear();
String first = mConfig.buildName;
std::vector<String> 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<UIDropDownList>( "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<String> 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

View File

@@ -1,7 +1,10 @@
#ifndef ECODE_PROJECTBUILD_HPP
#define ECODE_PROJECTBUILD_HPP
#include "appconfig.hpp"
#include <eepp/system/process.hpp>
#include <eepp/system/threadpool.hpp>
#include <eepp/ui/uitabwidget.hpp>
#include <memory>
#include <set>
#include <string>
@@ -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<std::string> mOS;
std::set<std::string> mOS;
std::set<std::string> mBuildTypes;
ProjectBuildSteps mBuild;
ProjectBuildSteps mClean;
@@ -177,7 +184,8 @@ using ProjectBuildi18nFn =
class ProjectBuildManager {
public:
ProjectBuildManager( const std::string& projectRoot, std::shared_ptr<ThreadPool> pool );
ProjectBuildManager( const std::string& projectRoot, std::shared_ptr<ThreadPool> 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<ThreadPool> mThreadPool;
UITabWidget* mSidePanel{ nullptr };
UISceneNode* mUISceneNode{ nullptr };
UITab* mTab{ nullptr };
App* mApp{ nullptr };
std::unique_ptr<Process> 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

View File

@@ -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<UIWidget>( "build_tab" );
if ( tab )
return tab->find<UIPushButton>( "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() ) {

View File

@@ -38,6 +38,8 @@ class StatusBuildOutputController {
UICodeEditor* mContainer{ nullptr };
UICodeEditor* createContainer();
UIPushButton* getBuildButton( App* app );
};
} // namespace ecode