diff --git a/qmltermwidget/lib/TerminalView.cpp b/qmltermwidget/lib/TerminalView.cpp index bc3bbe5..b7d0bf1 100644 --- a/qmltermwidget/lib/TerminalView.cpp +++ b/qmltermwidget/lib/TerminalView.cpp @@ -80,7 +80,7 @@ TerminalView::TerminalView() updatePointerCursor(); _blinkTimer.onTimeout = [this] { blinkEvent(); }; - _blinkCursorTimer.onTimeout = [this] { blinkCursorEvent(); }; + _blinkCursorTimer.onTimeout = [this] { blinkCursorTimeout(); }; _tripleClickTimer.setSingleShot(true); _tripleClickTimer.onTimeout = [this] { _possibleTripleClick = false; }; _bellTimer.setSingleShot(true); @@ -270,7 +270,7 @@ void TerminalView::setBlinkingCursor(bool blink) { _hasBlinkingCursor = blink; - if (blink && !_blinkCursorTimer.isActive() && hasFocus()) + if (blink && !_blinkCursorTimer.isActive() && hasKeyboard()) _blinkCursorTimer.start(std::chrono::milliseconds(CURSOR_BLINK_INTERVAL)); if (!blink && _blinkCursorTimer.isActive()) { @@ -306,9 +306,28 @@ void TerminalView::blinkCursorEvent() requestPaint(); } +void TerminalView::blinkCursorTimeout() +{ + // Without the keyboard the cursor shows in its unfocused state; the next + // paint with it starts blinking again. + if (!hasKeyboard()) { + _blinkCursorTimer.stop(); + if (_cursorBlinking) + blinkCursorEvent(); + return; + } + blinkCursorEvent(); +} + +bool TerminalView::hasKeyboard() const +{ + const ui::Window* window = this->window(); + return hasFocus() && window->hasKeyboardFocus(); +} + void TerminalView::focusIn(ui::FocusReason) { - if (_hasBlinkingCursor) + if (_hasBlinkingCursor && hasKeyboard()) _blinkCursorTimer.start(std::chrono::milliseconds(CURSOR_BLINK_INTERVAL)); requestPaint(); @@ -2087,6 +2106,15 @@ static void addOtherLineChar(FramePainter& frame, double left, double top, int w // Mirrors TerminalGpuNode::build(), which mirrored TerminalDisplay::paint(). void TerminalView::paint(ui::Painter& painter) { + // The window repaints its focus view when the keyboard comes or goes. + if (hasKeyboard()) { + if (_hasBlinkingCursor && !_blinkCursorTimer.isActive()) + _blinkCursorTimer.start(std::chrono::milliseconds(CURSOR_BLINK_INTERVAL)); + } else if (_blinkCursorTimer.isActive()) { + _blinkCursorTimer.stop(); + _cursorBlinking = false; + } + if (_image.empty() || geometry().width <= 0 || geometry().height <= 0) return; diff --git a/qmltermwidget/lib/TerminalView.h b/qmltermwidget/lib/TerminalView.h index b5063bc..bbb0125 100644 --- a/qmltermwidget/lib/TerminalView.h +++ b/qmltermwidget/lib/TerminalView.h @@ -100,6 +100,7 @@ public: void setTerminalFont(const std::string& family, double pointSize); void setBlinkingCursor(bool blink); + bool isCursorBlinking() const { return _blinkCursorTimer.isActive(); } void setTerminalCursorShape(int shape); void setBellMode(int mode) { _bellMode = mode; } void setConfirmMultilinePaste(bool confirm) { _confirmMultilinePaste = confirm; } @@ -229,6 +230,8 @@ private: int drawnLines() const; void blinkEvent(); void blinkCursorEvent(); + void blinkCursorTimeout(); + bool hasKeyboard() const; void restartCursorBlink(); void bell(); void swapColorTable(); diff --git a/tests/window_test.cpp b/tests/window_test.cpp index 18aad12..265b63b 100644 --- a/tests/window_test.cpp +++ b/tests/window_test.cpp @@ -21,6 +21,7 @@ #include "TerminalFont.h" #include "TerminalSession.h" +#include "TerminalView.h" #include "tools.h" #include "terminalpage.h" @@ -153,6 +154,48 @@ TEST_F(WindowTest, CloseOtherTabsAfterTheKeptTabEnded) EXPECT_TRUE(busy->isVisible()); } +TEST_F(WindowTest, CursorBlinksOnlyWithTheKeyboard) +{ + Windows windows(m_settings); + TerminalWindow* window = windows.create(shell()); + ui::Window& surface = window->window(); + Konsole::TerminalView* terminal = window->currentPage()->terminal(); + ASSERT_TRUE(terminal->hasFocus()); + + ui::TestInput::setKeyboardFocus(surface, true); + surface.grab(); + EXPECT_TRUE(terminal->isCursorBlinking()); + + ui::TestInput::setKeyboardFocus(surface, false); + surface.grab(); + EXPECT_FALSE(terminal->isCursorBlinking()); + + ui::TestInput::setKeyboardFocus(surface, true); + surface.grab(); + EXPECT_TRUE(terminal->isCursorBlinking()); + + // Without a frame drawn, the next blink stops the timer. + ui::TestInput::setKeyboardFocus(surface, false); + EXPECT_TRUE(waitFor([&] { return !terminal->isCursorBlinking(); })); +} + +TEST_F(WindowTest, CursorWithoutBlinkingNeverStartsTheTimer) +{ + m_settings.setBlinkingCursor(false); + Windows windows(m_settings); + TerminalWindow* window = windows.create(shell()); + ui::Window& surface = window->window(); + Konsole::TerminalView* terminal = window->currentPage()->terminal(); + + ui::TestInput::setKeyboardFocus(surface, true); + surface.grab(); + EXPECT_FALSE(terminal->isCursorBlinking()); + + ui::TestInput::setKeyboardFocus(surface, false); + surface.grab(); + EXPECT_FALSE(terminal->isCursorBlinking()); +} + namespace { class Canvas : public ui::View