#include "statusdebuggercontroller.hpp" #include "../../widgetcommandexecuter.hpp" #include "../plugincontextprovider.hpp" #include "eepp/ui/uiwidgetcreator.hpp" #include namespace ecode { class UIBreakpointsTableCell : public UITableCell { public: static UIBreakpointsTableCell* New( const std::string& tag, const BreakpointsModel* model, ModelIndex curIndex ) { return eeNew( UIBreakpointsTableCell, ( tag, model, curIndex ) ); } UIBreakpointsTableCell( const std::string& tag, const BreakpointsModel* model, ModelIndex curIndex ) : UITableCell( tag, getCheckBoxFn( model, curIndex ) ) {} std::function getCheckBoxFn( const BreakpointsModel* model, ModelIndex index ) { return [index, model, this]( UIPushButton* ) -> UITextView* { UICheckBox* chk = UICheckBox::New(); bool enabled = model ->data( model->index( index.row(), BreakpointsModel::Enabled ), ModelRole::Data ) .asBool(); chk->setChecked( enabled ); chk->setCheckMode( UICheckBox::Button ); chk->on( Event::OnValueChange, [this, chk]( const Event* ) { auto parent = static_cast( getParent()->getParent() ); auto model = parent->getModel(); auto index = getCurIndex(); bool checked = chk->isChecked(); if ( !parent->onBreakpointEnabledChange ) return; std::string filePath( model ->data( model->index( index.row(), BreakpointsModel::SourcePath ), ModelRole::Data ) .asCStr() ); int line( model ->data( model->index( index.row(), BreakpointsModel::Line ), ModelRole::Data ) .asInt() ); parent->onBreakpointEnabledChange( filePath, line, checked ); } ); return chk; }; } virtual void updateCell( Model* model ) { if ( !mTextBox->isType( UI_TYPE_CHECKBOX ) ) return; auto bpModel = static_cast( model ); auto cur = bpModel->get( getCurIndex() ); if ( cur.first.empty() ) return; mTextBox->asType()->setChecked( cur.second.enabled ); } }; UIWidget* UIBreakpointsTableView::createCell( UIWidget* rowWidget, const ModelIndex& index ) { if ( index.column() == BreakpointsModel::Enabled ) { UIBreakpointsTableCell* widget = UIBreakpointsTableCell::New( mTag + "::cell", (const BreakpointsModel*)getModel(), index ); widget->getTextView()->setEnabled( true ); widget->setDontAutoHideEmptyTextBox( true ); return setupCell( widget, rowWidget, index ); } else if ( index.column() == BreakpointsModel::Remove ) { auto cell = UITableView::createCell( rowWidget, index ); cell->onClick( [index, this]( auto ) { auto model = getModel(); if ( onBreakpointRemove ) { std::string filePath( model ->data( model->index( index.row(), BreakpointsModel::SourcePath ), ModelRole::Data ) .asCStr() ); int line( model ->data( model->index( index.row(), BreakpointsModel::Line ), ModelRole::Data ) .asInt() ); onBreakpointRemove( filePath, line ); } } ); return cell; } return UITableView::createCell( rowWidget, index ); } const std::map StatusDebuggerController::getLocalDefaultKeybindings() { return { { { KEY_TAB, KeyMod::getDefaultModifier() }, "next-tab" }, { { KEY_TAB, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "previous-tab" }, { { KEY_1, KeyMod::getDefaultModifier() }, "switch-to-tab-1" }, { { KEY_2, KeyMod::getDefaultModifier() }, "switch-to-tab-2" }, { { KEY_3, KeyMod::getDefaultModifier() }, "switch-to-tab-3" }, { { KEY_4, KeyMod::getDefaultModifier() }, "switch-to-tab-4" }, { { KEY_5, KeyMod::getDefaultModifier() }, "switch-to-tab-5" }, }; } StatusDebuggerController::StatusDebuggerController( UISplitter* mainSplitter, UISceneNode* uiSceneNode, PluginContextProvider* pluginContext ) : StatusBarElement( mainSplitter, uiSceneNode, pluginContext ) {} UIWidget* StatusDebuggerController::getWidget() { return mContainer; } UIWidget* StatusDebuggerController::createWidget() { if ( nullptr == mContainer ) { createContainer(); if ( onWidgetCreated ) onWidgetCreated( this, mContainer ); } return mContainer; } UITableView* StatusDebuggerController::getUIThreads() { return mUIThreads; } UITableView* StatusDebuggerController::getUIStack() { return mUIStack; } UIBreakpointsTableView* StatusDebuggerController::getUIBreakpoints() { return mUIBreakpoints; } UITreeView* StatusDebuggerController::getUIVariables() const { return mUIVariables; } UITreeView* StatusDebuggerController::getUIExpressions() const { return mUIExpressions; } UITabWidget* StatusDebuggerController::getUITabWidget() const { return mUITabWidget; } UICodeEditor* StatusDebuggerController::getUIConsole() const { return mUIConsole; } void StatusDebuggerController::insertConsoleBuffer( std::string&& buffer ) { auto console = getUIConsole(); if ( console == nullptr ) return; console->runOnMainThread( [console, this, buffer = std::move( buffer )]() { console->getDocument().insert( 0, console->getDocument().endOfDoc(), buffer ); if ( getConsoleScrollLocked() ) console->setScrollY( console->getMaxScroll().y ); } ); } void StatusDebuggerController::setDebuggingState( State state ) { if ( !mContainer ) return; mUISceneNode->runOnMainThread( [this, state] { mUIButStart->setVisible( state == State::NotStarted ) ->setEnabled( state == State::NotStarted ); mUIButStop->setVisible( state != State::NotStarted ) ->setEnabled( state != State::NotStarted ); mUIButContinue->setVisible( state != State::NotStarted ) ->setEnabled( state == State::Paused ); mUIButPause->setVisible( state != State::NotStarted ) ->setEnabled( state == State::Running ); mUIButStepOver->setVisible( state != State::NotStarted ) ->setEnabled( state == State::Paused ); mUIButStepInto->setVisible( state != State::NotStarted ) ->setEnabled( state == State::Paused ); mUIButStepOut->setVisible( state != State::NotStarted ) ->setEnabled( state == State::Paused ); } ); } void StatusDebuggerController::clearConsoleBuffer() { if ( mUIConsole == nullptr ) return; mUIConsole->runOnMainThread( [this] { mUIConsole->getDocument().reset(); mUIConsole->invalidateLongestLineWidth(); mUIConsole->setScrollY( mUIConsole->getMaxScroll().y ); } ); } void StatusDebuggerController::createContainer() { if ( mContainer ) return; const auto XML = R"xml( )xml"; UIWidgetCreator::registerWidget( "BreakpointsTableView", UIBreakpointsTableView::New ); if ( mMainSplitter->getLastWidget() != nullptr ) { mMainSplitter->getLastWidget()->setVisible( false ); mMainSplitter->getLastWidget()->setParent( mUISceneNode ); } mContainer = mContext->getUISceneNode() ->loadLayoutFromString( XML, mMainSplitter, String::hash( "status_debugger_controller" ) ) ->asType(); mContext->getStatusBar()->registerStatusBarPanel( mContainer, mContainer ); mContainer->bind( "app_debugger_tab_widget", mUITabWidget ); mContainer->bind( "debugger_threads_and_stack", mUIThreadsSplitter ); mContainer->bind( "debugger_threads", mUIThreads ); mContainer->bind( "debugger_stack", mUIStack ); mContainer->bind( "debugger_breakpoints", mUIBreakpoints ); mContainer->bind( "debugger_variables", mUIVariables ); mContainer->bind( "debugger_expressions", mUIExpressions ); mContainer->bind( "debugger_console", mUIConsole ); mContainer->bind( "app_debugger_start", mUIButStart ); mContainer->bind( "app_debugger_stop", mUIButStop ); mContainer->bind( "app_debugger_pause", mUIButPause ); mContainer->bind( "app_debugger_continue", mUIButContinue ); mContainer->bind( "app_debugger_step_over", mUIButStepOver ); mContainer->bind( "app_debugger_step_into", mUIButStepInto ); mContainer->bind( "app_debugger_step_out", mUIButStepOut ); mContainer->setCommand( "next-tab", [this] { if ( mUITabWidget ) mUITabWidget->focusNextTab(); } ); mContainer->setCommand( "previous-tab", [this] { if ( mUITabWidget ) mUITabWidget->focusPreviousTab(); } ); for ( int i = 1; i <= 5; i++ ) { mContainer->setCommand( String::format( "switch-to-tab-%d", i ), [this, i] { if ( mUITabWidget ) mUITabWidget->setTabSelected( eeclamp( i - 1, 0, mUITabWidget->getTabCount() - 1 ) ); } ); } mContainer->on( Event::KeyDown, [this]( const Event* event ) { auto ke = event->asKeyEvent(); if ( ke->getSanitizedMod() == 0 && ke->getKeyCode() == EE::Window::KEY_ESCAPE && mSplitter->getCurEditor() ) { mSplitter->getCurEditor()->setFocus(); } } ); mContainer->getKeyBindings().addKeybinds( getLocalDefaultKeybindings() ); setDebuggingState( State::NotStarted ); mUIButStart->onClick( [this]( auto ) { mContext->runCommand( "debugger-continue-interrupt" ); } ); mUIButContinue->onClick( [this]( auto ) { mContext->runCommand( "debugger-continue-interrupt" ); } ); mUIButPause->onClick( [this]( auto ) { mContext->runCommand( "debugger-continue-interrupt" ); } ); mUIButStop->onClick( [this]( auto ) { mContext->runCommand( "debugger-stop" ); } ); mUIButStepOver->onClick( [this]( auto ) { mContext->runCommand( "debugger-step-over" ); } ); mUIButStepInto->onClick( [this]( auto ) { mContext->runCommand( "debugger-step-into" ); } ); mUIButStepOut->onClick( [this]( auto ) { mContext->runCommand( "debugger-step-out" ); } ); mUIThreads->setAutoExpandOnSingleColumn( true ); mUIStack->setAutoColumnsWidth( true ); mUIStack->setFitAllColumnsToWidget( true ); mUIStack->setMainColumn( 1 ); mUIVariables->setAutoColumnsWidth( true ); mUIVariables->setFitAllColumnsToWidget( true ); mUIBreakpoints->setAutoColumnsWidth( true ); mUIBreakpoints->setFitAllColumnsToWidget( true ); mUIBreakpoints->setMainColumn( 1 ); mUIExpressions->setAutoColumnsWidth( true ); mUIExpressions->setFitAllColumnsToWidget( true ); mUIExpressions->setMainColumn( 1 ); mUIConsole->setLocked( true ); mUIConsole->setLineBreakingColumn( 0 ); mUIConsole->setShowLineNumber( false ); mUIConsole->getDocument().reset(); mUIConsole->setScrollY( mUIConsole->getMaxScroll().y ); mUIConsole->on( Event::OnScrollChange, [this]( auto ) { mScrollLocked = mUIConsole->getMaxScroll().y == mUIConsole->getScroll().y; } ); } } // namespace ecode