From e5a67727c44cda116648e233ea2186b942352ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 19 Dec 2025 14:20:17 -0300 Subject: [PATCH] Terminal: Add auto-scroll when selecting and mouse cursor moves out of vertical view range. Fix tab owned widget focus. Fix overlapping shortcuts and terminal shortcuts in terminal. --- include/eepp/scene/node.hpp | 3 +- src/eepp/scene/eventdispatcher.cpp | 3 +- src/eepp/ui/uitab.cpp | 1 + .../eterm/terminal/terminaldisplay.hpp | 5 +- .../src/eterm/terminal/terminaldisplay.cpp | 63 ++++++++++++++++++- .../src/eterm/terminal/terminalemulator.cpp | 8 +-- src/modules/eterm/src/eterm/ui/uiterminal.cpp | 15 +++-- 7 files changed, 82 insertions(+), 16 deletions(-) diff --git a/include/eepp/scene/node.hpp b/include/eepp/scene/node.hpp index 97aa9ffb8..fbcc95f6f 100644 --- a/include/eepp/scene/node.hpp +++ b/include/eepp/scene/node.hpp @@ -63,7 +63,8 @@ enum NodeFlags { NODE_FLAG_LOADING = ( 1 << 27 ), NODE_FLAG_CLOSING_CHILDREN = ( 1 << 28 ), - NODE_FLAG_FREE_USE = ( 1 << 29 ) + NODE_FLAG_DISABLE_CLICK_FOCUS = ( 1 << 29 ), + NODE_FLAG_FREE_USE = ( 1 << 30 ) }; class EE_API Node : public Transformable { diff --git a/src/eepp/scene/eventdispatcher.cpp b/src/eepp/scene/eventdispatcher.cpp index 2b25daaac..729f078ee 100644 --- a/src/eepp/scene/eventdispatcher.cpp +++ b/src/eepp/scene/eventdispatcher.cpp @@ -135,7 +135,8 @@ void EventDispatcher::update( const Time& time ) { if ( NULL != mFocusNode && mDownNode == mOverNode && ( mInput->getPressTrigger() & ( EE_BUTTON_LMASK | EE_BUTTON_RMASK ) ) && - ( !nodeWasDragging || mMousePos == mLastMousePos ) ) { + ( !nodeWasDragging || mMousePos == mLastMousePos ) && + !( mOverNode->getNodeFlags() & NODE_FLAG_DISABLE_CLICK_FOCUS ) ) { setFocusNode( mOverNode, NodeFocusReason::Click ); } } else if ( NULL != mOverNode && mInput->getReleaseTrigger() && diff --git a/src/eepp/ui/uitab.cpp b/src/eepp/ui/uitab.cpp index 9a779a315..3e970ddb2 100644 --- a/src/eepp/ui/uitab.cpp +++ b/src/eepp/ui/uitab.cpp @@ -29,6 +29,7 @@ UITab::UITab() : mCloseButton->on( Event::OnVisibleChange, cb ); applyDefaultTheme(); unsetFlags( UI_DRAG_VERTICAL ); + mNodeFlags |= NODE_FLAG_DISABLE_CLICK_FOCUS; } UITab::~UITab() {} diff --git a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp index 4b1320970..c16f2a60c 100644 --- a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp +++ b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp @@ -173,7 +173,7 @@ class TerminalDisplay : public ITerminalDisplay { virtual void drawCursor( int cx, int cy, TerminalGlyph g, int ox, int oy, TerminalGlyph og ); virtual void drawEnd(); - virtual bool update(); + virtual bool update( bool isMouseOverMe = true ); void executeFile( const std::string& cmd ); @@ -200,6 +200,8 @@ class TerminalDisplay : public ITerminalDisplay { virtual void onKeyDown( const Keycode& keyCode, const Uint32& chr, const Uint32& mod, const Scancode& scancode ); + bool isRegisteredShortcut( const Keycode& keyCode, const Uint32& mod ) const; + Font* getFont() const; void setFont( Font* font ); @@ -319,6 +321,7 @@ class TerminalDisplay : public ITerminalDisplay { Uint32 mQuadVertex{ 6 }; Primitives mPrimitives; Vector2u mCurGridPos; + Clock mLastAutoScroll; std::string mProgram; std::vector mArgs; diff --git a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp index dd2ad3764..99580e415 100644 --- a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp @@ -628,7 +628,7 @@ void TerminalDisplay::setKeepAlive( bool keepAlive ) { mKeepAlive = keepAlive; } -bool TerminalDisplay::update() { +bool TerminalDisplay::update( bool isMouseOverMe ) { bool ret = true; if ( mFocus && isBlinkingCursor() && mClock.getElapsedTime().asSeconds() > 0.7 ) { mMode ^= MODE_BLINK; @@ -641,6 +641,14 @@ bool TerminalDisplay::update() { if ( histi != mTerminal->getHistorySize() ) sendEvent( { EventType::HISTORY_LENGTH_CHANGE } ); } + if ( mAlreadyClickedLButton ) { + if ( !( mWindow->getInput()->getPressTrigger() & EE_BUTTON_LMASK ) ) { + mWindow->getInput()->captureMouse( false ); + } else if ( !isMouseOverMe ) { + onMouseMove( mWindow->getInput()->getMousePos(), + mWindow->getInput()->getPressTrigger() ); + } + } return ret; } @@ -826,6 +834,22 @@ void TerminalDisplay::onMouseMove( const Vector2i& pos, const Uint32& flags ) { bool shiftPressed = ( mWindow->getInput()->getModState() & KEYMOD_SHIFT ) != 0; bool isCapturingMouse = isAppCapturingMouse() && !shiftPressed; + if ( !isAltScr() && !isCapturingMouse && ( flags & EE_BUTTON_LMASK ) && + mAlreadyClickedLButton ) { + Vector2f relPos = { pos.x - mPosition.x - mPadding.Left, + pos.y - mPosition.y - mPadding.Top }; + + if ( mLastAutoScroll.getElapsedTime() >= Milliseconds( 16 ) ) { + if ( relPos.y < 0 ) { + action( TerminalShortcutAction::SCROLLUP_ROW ); + mLastAutoScroll.restart(); + } else if ( relPos.y > mSize.getHeight() ) { + action( TerminalShortcutAction::SCROLLDOWN_ROW ); + mLastAutoScroll.restart(); + } + } + } + if ( !isCapturingMouse && ( flags & EE_BUTTON_LMASK ) && ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_EMPTY || mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_READY ) ) { @@ -856,6 +880,7 @@ void TerminalDisplay::onMouseDown( const Vector2i& pos, const Uint32& flags ) { mTerminal->selstart( gridPos.x, gridPos.y, 0 ); mDraggingSel = true; invalidateLines(); + mWindow->getInput()->captureMouse( true ); } } else if ( flags & EE_BUTTON_MMASK ) { if ( !mAlreadyClickedMButton ) { @@ -891,8 +916,10 @@ void TerminalDisplay::onMouseUp( const Vector2i& pos, const Uint32& flags ) { Uint32 smod = sanitizeMod( mWindow->getInput()->getModState() ); - if ( flags & EE_BUTTON_LMASK ) + if ( flags & EE_BUTTON_LMASK ) { mAlreadyClickedLButton = false; + mWindow->getInput()->captureMouse( false ); + } if ( flags & EE_BUTTON_MMASK ) mAlreadyClickedMButton = false; @@ -1586,6 +1613,30 @@ void TerminalDisplay::onTextEditing( const String&, const Int32&, const Int32& ) updateIMELocation(); } +bool TerminalDisplay::isRegisteredShortcut( const Keycode& keyCode, const Uint32& mod ) const { + Uint32 smod = sanitizeMod( mod ); + auto scIt = terminalKeyMap.Shortcuts().find( keyCode ); + if ( scIt != terminalKeyMap.Shortcuts().end() ) { + for ( auto& k : scIt->second ) { + if ( k.mask == KEYMOD_CTRL_SHIFT_ALT_META || k.mask == smod ) { + if ( IS_SET( MODE_APPKEYPAD ) ? k.appkey < 0 : k.appkey > 0 ) + continue; + + if ( IS_SET( MODE_NUMLOCK ) && k.appkey == 2 ) + continue; + + if ( IS_SET( MODE_APPCURSOR ) ? k.appcursor < 0 : k.appcursor > 0 ) + continue; + + if ( !k.altscrn || ( k.altscrn == ( mEmulator->tisaltscr() ? 1 : -1 ) ) ) { + return true; + } + } + } + } + return false; +} + void TerminalDisplay::onKeyDown( const Keycode& keyCode, const Uint32& /*chr*/, const Uint32& mod, const Scancode& scancode ) { if ( mWindow->getIME().isEditing() ) @@ -1614,7 +1665,7 @@ void TerminalDisplay::onKeyDown( const Keycode& keyCode, const Uint32& /*chr*/, } if ( mod & KEYMOD_CTRL ) { - // I really dont like this, as it depends on the undelying backend implementation (SDL in + // I really dont like this, as it depends on the underlying backend implementation (SDL in // this case) if ( ( scancode >= SCANCODE_A && scancode <= SCANCODE_0 ) || SCANCODE_LEFTBRACKET == scancode || SCANCODE_RIGHTBRACKET == scancode ) { @@ -1748,6 +1799,12 @@ void TerminalDisplay::setFocus( bool focus ) { if ( focus == mFocus ) return; + + // focus loss + if ( mFocus && !focus && mAlreadyClickedMButton ) { + mWindow->getInput()->captureMouse( false ); + } + mFocus = focus; bool modeFocus = mMode & MODE_FOCUSED; if ( mFocus != modeFocus ) { diff --git a/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp b/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp index d78a1d8d4..621b6958e 100644 --- a/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp @@ -1638,9 +1638,9 @@ void TerminalEmulator::csihandle( void ) { switch ( mCsiescseq.mode[1] ) { case '4': /* Extended underline styles ESC[>4;Nm */ // Extended underline styles - fallback to standard underline - DEFAULT( mCsiescseq.arg[0], 1 ); + /* DEFAULT( mCsiescseq.arg[0], 1 ); switch ( mCsiescseq.arg[0] ) { - /* case 0: // No underline - fallback to ESC[24m + case 0: // No underline - fallback to ESC[24m { int fallback_args[] = { 24 }; // Reset underline tsetattr( fallback_args, 1 ); @@ -1653,10 +1653,10 @@ void TerminalEmulator::csihandle( void ) { { int fallback_args[] = { 4 }; // Standard underline tsetattr( fallback_args, 1 ); - } break; */ + } break; default: goto unknown; - } + } */ break; default: goto unknown; diff --git a/src/modules/eterm/src/eterm/ui/uiterminal.cpp b/src/modules/eterm/src/eterm/ui/uiterminal.cpp index db1f41f82..ad25355f2 100644 --- a/src/modules/eterm/src/eterm/ui/uiterminal.cpp +++ b/src/modules/eterm/src/eterm/ui/uiterminal.cpp @@ -315,7 +315,7 @@ const std::shared_ptr& UITerminal::getTerm() const { void UITerminal::scheduledUpdate( const Time& ) { if ( !mTerm ) return; - mTerm->update(); + mTerm->update( isMouseOverMeOrChildren() ); if ( mTerm->isDirty() && isVisible() ) invalidateDraw(); @@ -451,11 +451,14 @@ Uint32 UITerminal::onKeyDown( const KeyEvent& event ) { if ( mUISceneNode->getUIEventDispatcher()->justGainedFocus() ) return 0; - std::string cmd = mKeyBindings.getCommandFromKeyBind( { event.getKeyCode(), event.getMod() } ); - if ( !cmd.empty() ) { - if ( !mExclusiveMode || cmd == getExclusiveModeToggleCommandName() ) - execute( cmd ); - return 1; + if ( !mTerm->isRegisteredShortcut( event.getKeyCode(), event.getMod() ) ) { + std::string cmd = + mKeyBindings.getCommandFromKeyBind( { event.getKeyCode(), event.getMod() } ); + if ( !cmd.empty() ) { + if ( !mExclusiveMode || cmd == getExclusiveModeToggleCommandName() ) + execute( cmd ); + return 1; + } } mTerm->onKeyDown( event.getKeyCode(), event.getChar(), event.getMod(), event.getScancode() );