#include "uibuildsettings.hpp" #include #include #include #include #include #include using namespace EE::UI::Models; namespace ecode { class OutputParserModel final : public Model { public: static std::shared_ptr create( std::vector& data, std::function i18n ) { return std::make_shared( data, i18n ); } explicit OutputParserModel( std::vector& data, std::function i18n ) : mData( data ), i18n( i18n ) { mColumnNames.push_back( i18n( "type", "Type" ) ); mColumnNames.push_back( i18n( "pattern", "Pattern" ) ); } virtual ~OutputParserModel() {} virtual size_t rowCount( const ModelIndex& ) const { return mData.size(); } virtual size_t columnCount( const ModelIndex& ) const { return 2; } virtual std::string columnName( const size_t& index ) const { eeASSERT( index < 2 ); return mColumnNames[index]; } virtual void setColumnName( const size_t& index, const std::string& name ) { eeASSERT( index < 2 ); mColumnNames[index] = name; } virtual ModelIndex index( int row, int column, const ModelIndex& parent = ModelIndex() ) const { if ( row >= (int)rowCount( parent ) || column >= (int)columnCount( parent ) ) return {}; return Model::index( row, column, parent ); } virtual Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const { if ( role == ModelRole::Display ) { switch ( index.column() ) { case 0: { std::string val = ProjectBuildOutputParserConfig::typeToString( mData[index.row()].type ); return Variant( i18n( val, String::capitalize( val ) ) ); } case 1: default: return Variant( mData[index.row()].pattern ); } } return {}; } private: std::vector& mData; std::function i18n; std::vector mColumnNames; }; class UICustomOutputParserWindow : public UIWindow { public: static UICustomOutputParserWindow* New( ProjectBuildOutputParserConfig& cfg ) { return eeNew( UICustomOutputParserWindow, ( cfg ) ); } explicit UICustomOutputParserWindow( ProjectBuildOutputParserConfig& cfg ) : UIWindow( SIMPLE_LAYOUT, { UI_WIN_CLOSE_BUTTON | UI_WIN_USE_DEFAULT_BUTTONS_ACTIONS | UI_WIN_SHARE_ALPHA_WITH_CHILDREN | UI_WIN_MODAL } ), mTmpCfg( cfg ), mCfg( cfg ) { static const auto CUSTOM_OUTPUT_PARSER_XML = R"xml( @string("error", "Error") @string("warning", "Warning") @string("notice", "Notice") )xml"; mLayoutCont = getUISceneNode()->loadLayoutFromString( CUSTOM_OUTPUT_PARSER_XML, mContainer ); setTitle( i18n( "custom_output_parser", "Custom Output Parser" ) ); auto patternInput = find( "custom_parser_pattern" ); mDataBindHolder += UIDataBindString::New( &mTmpCfg.pattern, patternInput ); UIDropDownList* cpTypeddl = find( "custom_parser_type" ); UIDataBind::Converter projectOutputParserTypesConverter( []( const UIDataBind* databind, ProjectOutputParserTypes& val, const std::string& str ) -> bool { auto v = StyleSheetProperty( databind->getPropertyDefinition(), str ).asString(); Uint32 idx; if ( String::fromString( idx, v ) && idx >= 0 && idx <= 2 ) { val = (ProjectOutputParserTypes)idx; return true; } return false; }, [cpTypeddl]( const UIDataBind*, std::string& str, const ProjectOutputParserTypes& val ) -> bool { str = cpTypeddl->getListBox()->getItem( (Uint32)val )->getText(); return true; } ); mDataBindHolder += UIDataBind::New( &mTmpCfg.type, cpTypeddl, projectOutputParserTypesConverter, "selected-index" ); mDataBindHolder += UIDataBind::New( &mTmpCfg.patternOrder.file, find( "file_name_pos" ) ); mDataBindHolder += UIDataBind::New( &mTmpCfg.patternOrder.line, find( "line_number_pos" ) ); mDataBindHolder += UIDataBind::New( &mTmpCfg.patternOrder.col, find( "column_pos" ) ); mDataBindHolder += UIDataBind::New( &mTmpCfg.patternOrder.message, find( "message_pos" ) ); auto butOK = find( "but_ok" ); butOK->setEnabled( !patternInput->getText().empty() ); patternInput->on( Event::OnTextChanged, [butOK, patternInput]( auto ) { butOK->setEnabled( !patternInput->getText().empty() ); } ); butOK->onClick( [this]( auto ) { mCfg.pattern = mTmpCfg.pattern; mCfg.patternOrder = mTmpCfg.patternOrder; mCfg.type = mTmpCfg.type; sendCommonEvent( Event::OnConfirm ); closeWindow(); } ); find( "but_cancel" )->onClick( [this]( auto ) { closeWindow(); } ); } virtual ~UICustomOutputParserWindow() {} protected: UIWidget* mLayoutCont{ nullptr }; ProjectBuildOutputParserConfig mTmpCfg; ProjectBuildOutputParserConfig& mCfg; UIDataBindHolder mDataBindHolder; virtual void onWindowReady() { forcedApplyStyle(); Sizef size( mLayoutCont->getSize() ); setMinWindowSize( size ); center(); if ( mShowWhenReady ) { mShowWhenReady = false; show(); } sendCommonEvent( Event::OnWindowReady ); } }; class UIBuildStep : public UILinearLayout { public: enum class StepType { Build, Clean, Run }; static UIBuildStep* New( StepType stepType, UIBuildSettings* buildSettings, size_t stepNum, ProjectBuildStep* buildStep ) { return eeNew( UIBuildStep, ( stepType, buildSettings, stepNum, buildStep ) ); } void clearBindings() { mDataBindHolder.clear(); } void updateStep( size_t stepNum, ProjectBuildStep* buildStep ) { clearBindings(); removeClass( String::toString( (Uint64)mStepNum ) ); mStepNum = stepNum; mStep = buildStep; addClass( String::toString( (Uint64)mStepNum ) ); forEachChild( [buildStep]( Node* node ) { node->setEnabled( buildStep != nullptr ); } ); if ( buildStep == nullptr ) return; auto stepName = findByClass( "step_name" ); if ( isBuildOrClean() ) { stepName->setText( String::format( mBuildSettings->getUISceneNode() ->i18n( "build_step_num", "Step %u: %s" ) .toUtf8() .c_str(), mStepNum + 1, mStep->cmd.c_str() ) ); } else { stepName->setText( getUISceneNode()->i18n( "executable_to_run", "Executable to Run" ) ); } mDataBindHolder += UIDataBindBool::New( &mStep->enabled, findByClass( "enabled_checkbox" ) ); mDataBindHolder += UIDataBindString::New( &mStep->cmd, findByClass( "input_cmd" ) ); mDataBindHolder += UIDataBindString::New( &mStep->args, findByClass( "input_args" ) ); mDataBindHolder += UIDataBindString::New( &mStep->workingDir, findByClass( "input_working_dir" ) ); mDataBindHolder += UIDataBindBool::New( &mStep->runInTerminal, findByClass( "run_in_terminal" ) ); mDataBindHolder += UIDataBindBool::New( &mStep->reusePreviousTerminal, findByClass( "reuse_previous_terminal" ) ); mDataBindHolder += UIDataBindBool::New( &mStep->useStatusBarTerminal, findByClass( "use_statusbar_terminal" ) ); mDataBindHolder += UIDataBindBool::New( &mStep->stripAnsiCodes, findByClass( "strip_ansi_codes" ) ); } protected: UIBuildStep( StepType stepType, UIBuildSettings* buildSettings, size_t stepNum, ProjectBuildStep* buildStep ) : UILinearLayout( "buildstep", UIOrientation::Vertical ), mStepType( stepType ), mBuildSettings( buildSettings ), mStepNum( stepNum ), mStep( buildStep ) { setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); addClass( "build_step" ); addClass( String::toString( (Uint64)stepNum ) ); static const auto BUILD_STEP_XML = R"xml( )xml"; getUISceneNode()->loadLayoutFromString( BUILD_STEP_XML, this ); if ( !isBuildOrClean() ) { findByClass( "enabled_checkbox" )->setVisible( false ); auto runInTerminal = findByClass( "run_in_terminal" )->asType(); auto reusePreviousTerminal = findByClass( "reuse_previous_terminal" )->asType(); auto useStatusBarTerminal = findByClass( "use_statusbar_terminal" )->asType(); auto stripAnsiCodes = findByClass( "strip_ansi_codes" )->asType(); runInTerminal->setVisible( true ); runInTerminal->setChecked( buildStep->runInTerminal ); runInTerminal->on( Event::OnValueChange, [reusePreviousTerminal, runInTerminal, useStatusBarTerminal, stripAnsiCodes]( auto ) { reusePreviousTerminal->setEnabled( runInTerminal->isChecked() ); useStatusBarTerminal->setEnabled( runInTerminal->isChecked() ); stripAnsiCodes->setEnabled( !runInTerminal->isChecked() ); if ( !runInTerminal->isChecked() ) { reusePreviousTerminal->setChecked( false ); useStatusBarTerminal->setChecked( false ); } } ); reusePreviousTerminal->setVisible( true ); reusePreviousTerminal->setEnabled( buildStep->runInTerminal ); reusePreviousTerminal->setChecked( buildStep->reusePreviousTerminal ); useStatusBarTerminal->setVisible( true ); useStatusBarTerminal->setEnabled( buildStep->runInTerminal ); useStatusBarTerminal->setChecked( buildStep->useStatusBarTerminal ); stripAnsiCodes->setVisible( true ); stripAnsiCodes->setEnabled( !buildStep->runInTerminal ); stripAnsiCodes->setChecked( buildStep->stripAnsiCodes ); } findByClass( "details_but" )->onClick( [this]( const MouseEvent* event ) { auto me = event->getNode()->asType(); findByClass( "details" )->setVisible( me->hasClass( "contracted" ) ); me->toggleClass( "contracted" ); } ); auto moveDown = findByClass( "move_down" ); if ( isBuildOrClean() ) { moveDown->onClick( [this]( auto ) { mBuildSettings->moveStepDown( mStepNum, mStepType == StepType::Clean ); } ); } else { moveDown->setVisible( false ); } auto moveUp = findByClass( "move_up" ); if ( isBuildOrClean() ) { moveUp->onClick( [this]( auto ) { mBuildSettings->moveStepUp( mStepNum, mStepType == StepType::Clean ); } ); } else { moveUp->setVisible( false ); } auto removeItem = findByClass( "remove_item" ); if ( isBuildOrClean() ) { removeItem->onClick( [this]( auto ) { mBuildSettings->deleteStep( mStepNum, mStepType == StepType::Clean ); } ); } else { removeItem->setVisible( false ); } updateStep( mStepNum, mStep ); } bool isBuildOrClean() { return mStepType == StepType::Build || mStepType == StepType::Clean; } StepType mStepType{ true }; UIBuildSettings* mBuildSettings{ nullptr }; size_t mStepNum{ 0 }; ProjectBuildStep* mStep; UIDataBindHolder mDataBindHolder; }; static const auto SETTINGS_PANEL_XML = R"xml( generic )xml"; UIBuildSettings* UIBuildSettings::New( ProjectBuild& build, ProjectBuildConfiguration& config, bool isNew, const std::function onBuildNameChange ) { return eeNew( UIBuildSettings, ( build, config, isNew, onBuildNameChange ) ); } UIBuildSettings::~UIBuildSettings() { for ( const auto& cbs : mCbs ) { for ( const auto& cb : cbs.second ) cbs.first->removeEventListener( cb ); } if ( !mCanceled ) sendCommonEvent( Event::OnConfirm ); } UIBuildSettings::UIBuildSettings( ProjectBuild& build, ProjectBuildConfiguration& config, bool isNew, const std::function onBuildNameChange ) : mBuild( build ), mConfig( config ), mOldName( mBuild.getName() ), mIsNew( isNew ), mNewNameFn( onBuildNameChange ) { addClass( "build_settings" ); mUISceneNode->loadLayoutFromString( SETTINGS_PANEL_XML, this, String::hash( "build_settings" ) ); auto buildNameInput = find( "build_name" ); mDataBindHolder += UIDataBindString::New( &mBuild.mName, buildNameInput ); auto panelBuildNameDDL = getUISceneNode() ->getRoot() ->querySelector( "#build_tab_view #build_list" ) ->asType(); buildNameInput->on( Event::OnValueChange, [this, panelBuildNameDDL]( auto ) { refreshTab(); if ( panelBuildNameDDL ) { auto idx = panelBuildNameDDL->getListBox()->getItemIndex( mOldName ); if ( idx != eeINDEX_NOT_FOUND ) panelBuildNameDDL->getListBox()->setItemText( idx, mBuild.getName() ); } if ( mOldName == mConfig.buildName ) mConfig.buildName = mBuild.getName(); if ( mNewNameFn ) mNewNameFn( mOldName, mBuild.getName() ); mOldName = mBuild.getName(); } ); auto oses = find( "os_select" )->querySelectorAll( "CheckBox" ); for ( const auto os : oses ) { if ( mBuild.mOS.find( os->getId() ) != mBuild.mOS.end() ) os->asType()->setChecked( true ); os->on( Event::OnValueChange, [this]( const Event* ) { updateOS(); } ); } if ( mBuild.mBuild.empty() ) mBuild.mBuild.push_back( std::make_unique() ); if ( mBuild.mClean.empty() ) mBuild.mClean.push_back( std::make_unique() ); auto buildStepsParent = find( "build_steps_cont" ); for ( size_t step = 0; step < mBuild.mBuild.size(); ++step ) { auto bs = UIBuildStep::New( UIBuildStep::StepType::Build, this, step, mBuild.mBuild[step].get() ); bs->setParent( buildStepsParent ); } find( "add_build_step" )->onClick( [this, buildStepsParent]( const Event* ) { mBuild.mBuild.push_back( std::make_unique() ); auto step = mBuild.mBuild.size() - 1; UIBuildStep::New( UIBuildStep::StepType::Build, this, step, mBuild.mBuild[step].get() ) ->setParent( buildStepsParent ); } ); auto buildCleanStepsParent = find( "build_clean_steps_cont" ); for ( size_t step = 0; step < mBuild.mClean.size(); ++step ) { UIBuildStep::New( UIBuildStep::StepType::Clean, this, step, mBuild.mClean[step].get() ) ->setParent( buildCleanStepsParent ); } find( "add_clean_step" )->onClick( [this, buildCleanStepsParent]( const Event* ) { mBuild.mClean.push_back( std::make_unique() ); auto step = mBuild.mClean.size() - 1; UIBuildStep::New( UIBuildStep::StepType::Clean, this, step, mBuild.mClean[step].get() ) ->setParent( buildCleanStepsParent ); } ); auto buildTypeDropDown = find( "build_type_list" ); auto panelBuildTypeDDL = getUISceneNode() ->getRoot() ->querySelector( "#build_tab_view #build_type_list" ) ->asType(); std::vector buildTypes; for ( const auto& type : mBuild.mBuildTypes ) buildTypes.push_back( type ); buildTypeDropDown->getListBox()->addListBoxItems( buildTypes ); buildTypeDropDown->getListBox()->setSelected( mConfig.buildType ); buildTypeDropDown->on( Event::OnItemSelected, [this, buildTypeDropDown, panelBuildTypeDDL]( const Event* ) { mConfig.buildType = buildTypeDropDown->getListBox()->getItemSelectedText().toUtf8(); if ( panelBuildTypeDDL ) panelBuildTypeDDL->getListBox()->setSelected( mConfig.buildType ); } ); if ( panelBuildTypeDDL ) { mCbs[panelBuildTypeDDL].push_back( panelBuildTypeDDL->on( Event::OnItemSelected, [this, buildTypeDropDown, panelBuildTypeDDL]( const Event* ) { mConfig.buildType = panelBuildTypeDDL->getListBox()->getItemSelectedText().toUtf8(); if ( buildTypeDropDown ) buildTypeDropDown->getListBox()->setSelected( mConfig.buildType ); } ) ); mCbs[panelBuildTypeDDL].push_back( panelBuildTypeDDL->on( Event::OnClose, [this, panelBuildTypeDDL]( auto ) { mCbs.erase( panelBuildTypeDDL ); } ) ); } auto advTitle = querySelector( ".settings_panel > .advanced_options > .title" ); advTitle->onClick( [this, advTitle]( const MouseEvent* event ) { if ( getEventDispatcher()->getMouseDownNode() == advTitle ) { auto img = event->getNode()->findByType( UI_TYPE_IMAGE )->asType(); findByClass( "inner_box" )->toggleClass( "visible" ); img->toggleClass( "expanded" ); } } ); mDataBindHolder += UIDataBindBool::New( &mBuild.mConfig.clearSysEnv, find( "clear_sys_env" ) ); mDataBindHolder += UIDataBindBool::New( &mBuild.mConfig.stripAnsiCodes, find( "output_parsers_strip_ansi_codes" ) ); bindTable( "table_envs", "env", mBuild.mEnvs ); bindTable( "table_vars", "var", mBuild.mVars ); find( "build_del" )->onClick( [this]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, i18n( "confirm_build_delete", "Are you sure you want to delete the build configuration?" ) ); msgBox->setTitle( i18n( "build_settings", "Build Settings" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) { mCanceled = true; sendTextEvent( Event::OnClear, mBuild.getName() ); msgBox->closeWindow(); if ( mTab ) mTab->removeTab(); } ); } ); find( "build_clone" )->onClick( [this]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, i18n( "build_cloned_name", "New Cloned Build Name:" ) ); msgBox->setTitle( i18n( "build_settings", "Build Settings" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->on( Event::OnWindowReady, [this, msgBox]( auto ) { msgBox->getTextInput()->setText( mBuild.getName() ); msgBox->getTextInput()->getDocument().selectAll(); } ); msgBox->on( Event::OnConfirm, [msgBox, this]( const Event* ) { const auto& newBuildName = msgBox->getTextInput()->getText(); sendTextEvent( Event::OnCopy, newBuildName ); msgBox->closeWindow(); } ); } ); find( "build_type_add" )->onClick( [this, buildTypeDropDown, panelBuildTypeDDL]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, i18n( "build_type_name", "Build Type Name:" ) ); msgBox->setTitle( i18n( "build_settings", "Build Settings" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->on( Event::OnConfirm, [this, msgBox, buildTypeDropDown, panelBuildTypeDDL]( const Event* ) { const auto& buildType = msgBox->getTextInput()->getText(); mBuild.mBuildTypes.insert( buildType.toUtf8() ); buildTypeDropDown->getListBox()->addListBoxItem( buildType ); buildTypeDropDown->getListBox()->setSelected( buildType ); if ( panelBuildTypeDDL ) { panelBuildTypeDDL->getListBox()->addListBoxItem( buildType ); panelBuildTypeDDL->getListBox()->setSelected( buildType ); panelBuildTypeDDL->setEnabled( true ); } msgBox->closeWindow(); } ); } ); find( "build_type_del" )->onClick( [this, buildTypeDropDown, panelBuildTypeDDL]( auto ) { const auto& txt = buildTypeDropDown->getListBox()->getItemSelectedText(); UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, String::format( i18n( "build_type_name_del", "Delete Build Type: %s?" ).toUtf8().c_str(), txt.toUtf8().c_str() ) ); msgBox->setTitle( i18n( "build_settings", "Build Settings" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->on( Event::OnConfirm, [this, msgBox, buildTypeDropDown, panelBuildTypeDDL, txt]( const Event* ) { mBuild.mBuildTypes.erase( txt.toUtf8() ); buildTypeDropDown->getListBox()->removeListBoxItem( txt ); if ( panelBuildTypeDDL ) { panelBuildTypeDDL->getListBox()->removeListBoxItem( txt ); if ( panelBuildTypeDDL->getListBox()->isEmpty() ) panelBuildTypeDDL->setEnabled( false ); } msgBox->closeWindow(); } ); } ); if ( isNew && mBuild.mOutputParser.getPreset().empty() && !ProjectBuildOutputParser::getPresets().empty() ) { mBuild.mOutputParser.mPreset = ProjectBuildOutputParser::getPresets().begin()->first; mBuild.mOutputParser.mPresetConfig = ProjectBuildOutputParser::getPresets().begin()->second.getConfig(); } auto outputParserPresetsDDL = find( "output_parsers_presets_list" ); outputParserPresetsDDL->getListBox()->setSelected( mBuild.mOutputParser.mPreset ); outputParserPresetsDDL->on( Event::OnItemSelected, [this]( const Event* event ) { std::string txt( event->getNode() ->asType() ->getListBox() ->getItemSelectedText() .toUtf8() ); mBuild.mOutputParser.mPreset = txt; if ( ProjectBuildOutputParser::existsPreset( txt ) ) { mBuild.mOutputParser.mPresetConfig = ProjectBuildOutputParser::getPresets()[mBuild.mOutputParser.mPreset].mConfig; } } ); UITableView* tableOP = find( "table_output_parsers" ); tableOP->setMainColumn( 1 ); tableOP->setAutoColumnsWidth( true ); tableOP->setFitAllColumnsToWidget( true ); auto modelOP = OutputParserModel::create( mBuild.mOutputParser.mConfig, [this]( auto s, auto s2 ) { return i18n( s, s2 ); } ); tableOP->setModel( modelOP ); find( "custom_op_add" )->onClick( [this, modelOP]( auto ) { mTmpOpCfg = {}; auto ret = UICustomOutputParserWindow::New( mTmpOpCfg ); ret->showWhenReady(); ret->on( Event::OnConfirm, [this, modelOP]( auto ) { mBuild.mOutputParser.mConfig.push_back( mTmpOpCfg ); modelOP->invalidate(); } ); } ); find( "custom_op_edit" )->onClick( [this, tableOP, modelOP]( auto ) { if ( !tableOP->getSelection().isEmpty() && tableOP->getSelection().first().row() >= 0 && tableOP->getSelection().first().row() < (int)mBuild.mOutputParser.mConfig.size() ) { auto ret = UICustomOutputParserWindow::New( mBuild.mOutputParser.mConfig[tableOP->getSelection().first().row()] ); ret->showWhenReady(); ret->on( Event::OnConfirm, [modelOP]( auto ) { modelOP->invalidate(); } ); } } ); find( "custom_op_del" )->onClick( [this, tableOP, modelOP]( auto ) { if ( !tableOP->getSelection().isEmpty() && tableOP->getSelection().first().row() >= 0 && tableOP->getSelection().first().row() < (int)mBuild.mOutputParser.mConfig.size() ) { mBuild.mOutputParser.mConfig.erase( mBuild.mOutputParser.mConfig.begin() + tableOP->getSelection().first().row() ); modelOP->invalidate(); } } ); runSetup(); } void UIBuildSettings::updateOS() { mBuild.mOS.clear(); auto oses = find( "os_select" )->querySelectorAll( "CheckBox" ); for ( const auto os : oses ) { if ( os->asType()->isChecked() ) mBuild.mOS.insert( os->getId() ); } } void UIBuildSettings::setTab( UITab* tab ) { if ( tab != mTab ) { mTab = tab; refreshTab(); } } UITab* UIBuildSettings::getTab() const { return mTab; } void UIBuildSettings::refreshTab() { if ( !mTab ) return; mTab->setText( String::format( ( i18n( "build_settings", "Build Settings" ) + ": %s" ).toUtf8().c_str(), mBuild.mName.c_str() ) ); std::string hashName = String::toString( String::hash( mIsNew ? "new_name" : mBuild.mName ) ); mTab->setId( "build_settings_" + hashName ); } void UIBuildSettings::bindTable( const std::string& name, const std::string& key, ProjectBuildKeyVal& data ) { UITableView* table = find( name ); auto model = ItemPairListModel::create( data ); const auto createInputDelegate = [table]( const ModelIndex& ) -> ModelEditingDelegate* { auto delegate = StringModelEditingDelegate::New(); delegate->onWillBeginEditing = [delegate, table]() { delegate->getWidget()->asType()->on( Event::OnFocusLoss, [delegate, table]( auto ) { delegate->onCommit(); table->recalculateColumnsWidth(); } ); }; return delegate; }; model->setColumnName( 0, i18n( key + "_name", "Name" ) ); model->setColumnName( 1, i18n( key + "_value", "Value" ) ); model->setIsEditable( true ); table->setMainColumn( 1 ); table->setAutoColumnsWidth( true ); table->setFitAllColumnsToWidget( true ); table->setModel( model ); table->setEditable( true ); table->setSelectionType( UIAbstractView::SelectionType::Cell ); table->setEditTriggers( UIAbstractView::EditTrigger::DoubleClicked | UIAbstractTableView::EditTrigger::EditKeyPressed ); table->onCreateEditingDelegate = createInputDelegate; find( "custom_" + key + "_add" )->onClick( [this, model, &data]( auto ) { data.push_back( { i18n( "new_name", "New Name" ), i18n( "new_value", "New Value" ) } ); model->invalidate(); } ); find( "custom_" + key + "_del" )->onClick( [model, table, &data]( auto ) { if ( !table->getSelection().isEmpty() ) { data.erase( data.begin() + table->getSelection().first().row() ); model->invalidate(); } } ); } void UIBuildSettings::moveStepUp( size_t stepNum, bool isClean ) { moveStepDir( stepNum, isClean, -1 ); } void UIBuildSettings::moveStepDown( size_t stepNum, bool isClean ) { moveStepDir( stepNum, isClean, 1 ); } void UIBuildSettings::moveStepDir( size_t stepNum, bool isClean, int dir ) { ProjectBuildSteps& steps = isClean ? mBuild.mClean : mBuild.mBuild; UIWidget* cont = isClean ? find( "build_clean_steps_cont" ) : find( "build_steps_cont" ); int newStep = (int)stepNum + dir; std::swap( steps[stepNum], steps[newStep] ); auto bs1 = cont->findByClass( String::toString( (Uint64)stepNum ) ); auto bs2 = cont->findByClass( String::toString( newStep ) ); bs1->updateStep( stepNum, steps[stepNum].get() ); bs2->updateStep( newStep, steps[newStep].get() ); } void UIBuildSettings::deleteStep( size_t stepNum, bool isClean ) { ProjectBuildSteps& steps = isClean ? mBuild.mClean : mBuild.mBuild; UIWidget* cont = isClean ? find( "build_clean_steps_cont" ) : find( "build_steps_cont" ); for ( auto step = stepNum; step < steps.size(); step++ ) cont->findByClass( String::toString( (Uint64)step ) )->clearBindings(); steps.erase( steps.begin() + stepNum ); cont->findByClass( String::toString( (Uint64)stepNum ) )->close(); for ( auto step = stepNum + 1; step <= steps.size(); step++ ) cont->findByClass( String::toString( (Uint64)step ) ) ->updateStep( step - 1, steps[step - 1].get() ); } void UIBuildSettings::runSetup() { if ( mBuild.mRun.empty() ) { mBuild.mRun.push_back( std::make_unique() ); mBuild.mRun.back()->name = i18n( "custom_executable", "Custom Executable" ); } UIBuildStep::New( UIBuildStep::StepType::Run, this, 0, mBuild.mRun[runIndex()].get() ) ->setParent( find( "run_cont" ) ); UIDropDownList* runList = find( "run_list" )->asType(); auto panelRunListDDL = getUISceneNode() ->getRoot() ->querySelector( "#build_tab_view #run_config_list" ) ->asType(); auto runName = mConfig.runName; runUpdate( true, runList, panelRunListDDL ); mConfig.runName = std::move( runName ); runList->getListBox()->setSelected( runIndex() ); if ( panelRunListDDL ) panelRunListDDL->getListBox()->setSelected( runIndex() ); runSelect( runIndex() ); runList->on( Event::OnItemSelected, [this, runList, panelRunListDDL]( auto ) { mConfig.runName = runList->getListBox()->getItemSelectedText().toUtf8(); runSelect( runIndex() ); if ( panelRunListDDL ) panelRunListDDL->getListBox()->setSelected( mConfig.runName ); } ); if ( panelRunListDDL ) { mCbs[panelRunListDDL].push_back( panelRunListDDL->on( Event::OnItemSelected, [this, runList, panelRunListDDL]( const Event* ) { mConfig.runName = panelRunListDDL->getListBox()->getItemSelectedText().toUtf8(); if ( runList ) runList->getListBox()->setSelected( mConfig.runName ); } ) ); mCbs[panelRunListDDL].push_back( panelRunListDDL->on( Event::OnClose, [this, panelRunListDDL]( auto ) { mCbs.erase( panelRunListDDL ); } ) ); } find( "run_add" )->onClick( [this, runList, panelRunListDDL]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, i18n( "run_configuration_name", "Run configuration name:" ) ); msgBox->setTitle( i18n( "run_settings", "Run Settings" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->on( Event::OnConfirm, [this, msgBox, runList, panelRunListDDL]( auto ) { bool unique = true; auto newName = msgBox->getTextInput()->getText().toUtf8(); for ( const auto& run : mBuild.mRun ) { if ( newName == run->name ) { unique = false; break; } } if ( unique ) { mBuild.mRun.push_back( std::make_unique() ); mBuild.mRun.back()->name = newName; runList->getListBox()->addListBoxItem( newName ); runList->getListBox()->setSelected( newName ); if ( panelRunListDDL ) { panelRunListDDL->getListBox()->addListBoxItem( newName ); panelRunListDDL->getListBox()->setSelected( newName ); } runUpdate( false, runList, panelRunListDDL ); } else { UIMessageBox* uniqueAlert = UIMessageBox::New( UIMessageBox::OK, i18n( "run_configuration_name_must_be_unique", "Run configuration name must be unique!" ) ); uniqueAlert->setTitle( i18n( "run_settings", "Run Settings" ) ); uniqueAlert->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); uniqueAlert->showWhenReady(); } msgBox->close(); } ); } ); find( "run_remove" )->onClick( [this, runList, panelRunListDDL]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, i18n( "run_configuration_delete_confirm", "Are you sure you want to delete the currently selected run configuration?" ) ); msgBox->setTitle( i18n( "run_configuration", "Run Configuration" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->on( Event::OnConfirm, [this, runList, panelRunListDDL]( auto ) { runRemove( false, runList, panelRunListDDL ); } ); } ); find( "run_remove_all" )->onClick( [this, runList, panelRunListDDL]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, i18n( "run_configuration_delete_all_confirm", "Are you sure you want to delete all the run configurations?" ) ); msgBox->setTitle( i18n( "run_configuration", "Run Configuration" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->on( Event::OnConfirm, [this, runList, panelRunListDDL]( auto ) { runRemove( true, runList, panelRunListDDL ); } ); } ); find( "run_rename" )->onClick( [this, runList, panelRunListDDL]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, i18n( "run_configuration_rename", "Rename configuration name:" ) ); msgBox->setTitle( i18n( "run_settings", "Run Settings" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->getTextInput()->setText( runList->getText() ); msgBox->getTextInput()->getDocument().selectAll(); msgBox->showWhenReady(); auto selectedIndex = runList->getListBox()->getItemSelectedIndex(); msgBox->on( Event::OnConfirm, [this, msgBox, selectedIndex, runList, panelRunListDDL]( auto ) { bool unique = true; auto newName = msgBox->getTextInput()->getText().toUtf8(); for ( size_t i = 0; i < mBuild.mRun.size(); i++ ) { const auto& run = mBuild.mRun[i]; if ( newName == run->name && i != selectedIndex ) { unique = false; break; } } if ( unique && selectedIndex < mBuild.mRun.size() ) { if ( mBuild.mRun[selectedIndex]->name == mConfig.runName ) mConfig.runName = newName; mBuild.mRun[selectedIndex]->name = newName; runList->getListBox()->setItemText( selectedIndex, newName ); if ( panelRunListDDL ) panelRunListDDL->getListBox()->setItemText( selectedIndex, newName ); } else { UIMessageBox* uniqueAlert = UIMessageBox::New( UIMessageBox::OK, i18n( "run_configuration_name_must_be_unique", "Run configuration name must be unique!" ) ); uniqueAlert->setTitle( i18n( "run_settings", "Run Settings" ) ); uniqueAlert->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); uniqueAlert->showWhenReady(); } msgBox->close(); } ); } ); find( "run_clone" )->onClick( [this, runList, panelRunListDDL]( auto ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT, i18n( "run_configuration_name", "Run configuration name:" ) ); msgBox->setTitle( i18n( "run_settings", "Run Settings" ) ); msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->getTextInput()->setText( runList->getText() ); msgBox->getTextInput()->getDocument().selectAll(); msgBox->showWhenReady(); msgBox->on( Event::OnConfirm, [this, msgBox, runList, panelRunListDDL]( auto ) { bool unique = true; auto newName = msgBox->getTextInput()->getText().toUtf8(); for ( const auto& run : mBuild.mRun ) { if ( newName == run->name ) { unique = false; break; } } if ( unique ) { auto selectedIndex = runList->getListBox()->getItemSelectedIndex(); if ( selectedIndex < mBuild.mRun.size() ) { mBuild.mRun.push_back( std::make_unique( *mBuild.mRun[selectedIndex].get() ) ); } else { mBuild.mRun.push_back( std::make_unique() ); } mBuild.mRun.back()->name = newName; runList->getListBox()->addListBoxItem( newName ); runList->getListBox()->setSelected( newName ); if ( panelRunListDDL ) { panelRunListDDL->getListBox()->addListBoxItem( newName ); panelRunListDDL->getListBox()->setSelected( newName ); } } else { UIMessageBox* uniqueAlert = UIMessageBox::New( UIMessageBox::OK, i18n( "run_configuration_name_must_be_unique", "Run configuration name must be unique!" ) ); uniqueAlert->setTitle( i18n( "run_settings", "Run Settings" ) ); uniqueAlert->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); uniqueAlert->showWhenReady(); } msgBox->close(); } ); } ); } void UIBuildSettings::runSelect( Uint32 index ) { UIWidget* cont = find( "run_cont" ); auto bs = cont->findByClass( String::toString( 0 ) ); if ( index < mBuild.mRun.size() ) { bs->updateStep( 0, mBuild.mRun[index].get() ); } else { if ( mBuild.mRun.empty() ) { mBuild.mRun.push_back( std::make_unique() ); mBuild.mRun.back()->name = i18n( "custom_executable", "Custom Executable" ); } bs->updateStep( 0, mBuild.mRun[0].get() ); } } void UIBuildSettings::runRemove( bool all, UIDropDownList* runList, UIDropDownList* panelRunListDDL ) { if ( runList->getListBox()->isEmpty() ) return; if ( all ) { runList->getListBox()->clear(); if ( panelRunListDDL ) panelRunListDDL->getListBox()->clear(); mBuild.mRun.clear(); } else { auto name = runList->getListBox()->getItemSelectedText(); mBuild.mRun.erase( mBuild.mRun.begin() + runList->getListBox()->getItemSelectedIndex() ); runList->getListBox()->removeListBoxItem( name ); if ( panelRunListDDL ) panelRunListDDL->getListBox()->removeListBoxItem( name ); } runUpdate( false, runList, panelRunListDDL ); if ( all ) runSelect(); } void UIBuildSettings::runUpdate( bool recreateList, UIDropDownList* runList, UIDropDownList* panelRunListDDL ) { if ( recreateList ) { runList->getListBox()->clear(); if ( panelRunListDDL ) panelRunListDDL->getListBox()->clear(); size_t i = 1; for ( const auto& run : mBuild.mRun ) { auto name = run->name.empty() ? String::format( i18n( "custom_executable_num", "Custom Executable %d" ).toUtf8(), i ) : run->name; runList->getListBox()->addListBoxItem( name ); if ( panelRunListDDL ) panelRunListDDL->getListBox()->addListBoxItem( name ); i++; } } bool runButEnabled = !runList->getListBox()->isEmpty(); runList->setEnabled( runButEnabled ); if ( panelRunListDDL ) panelRunListDDL->setEnabled( runButEnabled ); find( "run_cont" )->setEnabled( runButEnabled )->setVisible( runButEnabled ); find( "run_remove" )->setEnabled( runButEnabled ); find( "run_remove_all" )->setEnabled( runButEnabled ); find( "run_rename" )->setEnabled( runButEnabled ); find( "run_clone" )->setEnabled( runButEnabled ); } Uint32 UIBuildSettings::runIndex() const { for ( size_t i = 0; i < mBuild.mRun.size(); i++ ) { if ( mBuild.mRun[i]->name == mConfig.runName ) return i; } return 0; } } // namespace ecode