Commit 9ee21b9c authored by Mariusz Glebocki's avatar Mariusz Glebocki Committed by Kurt Hindenburg
Browse files

Report TerminalDisplay focus when any of its children is focused

Focused TerminalDisplay is used as a source of tab and window name,
for activity monitoring, splits headers highlighting, etc.
With this change TerminalDisplay is considered focused even when actual
focus is e.g. in its search box or popup menu.
parent 316a5101
......@@ -266,14 +266,9 @@ public Q_SLOTS:
void receiveData(const char *text, int length);
/**
* Sends information about the focus lost event to the terminal.
* Sends information about the focus event to the terminal.
*/
virtual void focusLost() = 0;
/**
* Sends information about the focus gained event to the terminal.
*/
virtual void focusGained() = 0;
virtual void focusChanged(bool focused) = 0;
Q_SIGNALS:
......
......@@ -329,8 +329,7 @@ void Session::addView(TerminalDisplay* widget)
connect(widget, &Konsole::TerminalDisplay::destroyed, this, &Konsole::Session::viewDestroyed);
connect(widget, &Konsole::TerminalDisplay::focusLost, _emulation, &Konsole::Emulation::focusLost);
connect(widget, &Konsole::TerminalDisplay::focusGained, _emulation, &Konsole::Emulation::focusGained);
connect(widget, &Konsole::TerminalDisplay::compositeFocusChanged, _emulation, &Konsole::Emulation::focusChanged);
connect(_emulation, &Konsole::Emulation::setCursorStyleRequest, widget, &Konsole::TerminalDisplay::setCursorStyle);
connect(_emulation, &Konsole::Emulation::resetCursorStyleRequest, widget, &Konsole::TerminalDisplay::resetCursorStyle);
......
......@@ -148,8 +148,8 @@ SessionController::SessionController(Session* session , TerminalDisplay* view, Q
setIdentifier(++_lastControllerId);
sessionAttributeChanged();
view->installEventFilter(this);
view->setSessionController(this);
connect(_view, &TerminalDisplay::compositeFocusChanged, this, &SessionController::viewFocusChangeHandler);
_view->setSessionController(this);
// install filter on the view to highlight URLs and files
updateFilterList(SessionManager::instance()->sessionProfile(_session));
......@@ -203,8 +203,10 @@ SessionController::SessionController(Session* session , TerminalDisplay* view, Q
_interactionTimer->setSingleShot(true);
_interactionTimer->setInterval(500);
connect(_interactionTimer, &QTimer::timeout, this, &Konsole::SessionController::snapshot);
connect(_view.data(), &Konsole::TerminalDisplay::focusGained, this, &Konsole::SessionController::interactionHandler);
connect(_view.data(), &Konsole::TerminalDisplay::keyPressedSignal, this, &Konsole::SessionController::interactionHandler);
connect(_view.data(), &Konsole::TerminalDisplay::compositeFocusChanged,
this, [this](bool focused) { if (focused) { interactionHandler(); }});
connect(_view.data(), &Konsole::TerminalDisplay::keyPressedSignal,
this, &Konsole::SessionController::interactionHandler);
// take a snapshot of the session state periodically in the background
auto backgroundTimer = new QTimer(_session);
......@@ -271,6 +273,30 @@ void SessionController::trackOutput(QKeyEvent* event)
_view->screenWindow()->setTrackOutput(true);
}
void SessionController::viewFocusChangeHandler(bool focused)
{
if (focused) {
// notify the world that the view associated with this session has been focused
// used by the view manager to update the title of the MainWindow widget containing the view
emit viewFocused(this);
// when the view is focused, set bell events from the associated session to be delivered
// by the focused view
// first, disconnect any other views which are listening for bell signals from the session
disconnect(_session.data(), &Konsole::Session::bellRequest, nullptr, nullptr);
// second, connect the newly focused view to listen for the session's bell signal
connect(_session.data(), &Konsole::Session::bellRequest, _view.data(), &Konsole::TerminalDisplay::bell);
if ((_copyInputToAllTabsAction != nullptr) && _copyInputToAllTabsAction->isChecked()) {
// A session with "Copy To All Tabs" has come into focus:
// Ensure that newly created sessions are included in _copyToGroup!
copyInputToAllTabs();
}
}
}
void SessionController::interactionHandler()
{
// This flag is used to make sure those special icons indicating interest
......@@ -503,30 +529,6 @@ void SessionController::toggleReadOnly()
}
}
bool SessionController::eventFilter(QObject* watched , QEvent* event)
{
if (event->type() == QEvent::FocusIn && watched == _view) {
// notify the world that the view associated with this session has been focused
// used by the view manager to update the title of the MainWindow widget containing the view
emit focused(this);
// when the view is focused, set bell events from the associated session to be delivered
// by the focused view
// first, disconnect any other views which are listening for bell signals from the session
disconnect(_session.data(), &Konsole::Session::bellRequest, nullptr, nullptr);
// second, connect the newly focused view to listen for the session's bell signal
connect(_session.data(), &Konsole::Session::bellRequest, _view.data(), &Konsole::TerminalDisplay::bell);
if ((_copyInputToAllTabsAction != nullptr) && _copyInputToAllTabsAction->isChecked()) {
// A session with "Copy To All Tabs" has come into focus:
// Ensure that newly created sessions are included in _copyToGroup!
copyInputToAllTabs();
}
}
return Konsole::ViewProperties::eventFilter(watched, event);
}
void SessionController::removeSearchFilter()
{
if (_searchFilter == nullptr) {
......
......@@ -141,9 +141,6 @@ public:
bool confirmClose() const override;
virtual bool confirmForceClose() const;
// Reimplemented to watch for events happening to the view
bool eventFilter(QObject *watched, QEvent *event) override;
/** Returns the set of all controllers that exist. */
static QSet<SessionController *> allControllers()
{
......@@ -160,7 +157,7 @@ Q_SIGNALS:
* This can be used by other classes to plug the controller's actions into a window's
* menus.
*/
void focused(SessionController *controller);
void viewFocused(SessionController *controller);
void rawTitleChanged();
......@@ -266,6 +263,7 @@ private Q_SLOTS:
void updateFilterList(Profile::Ptr profile); // Called when the profile has changed, so we might need to change the list of filters
void viewFocusChangeHandler(bool focused);
void interactionHandler();
void snapshot(); // called periodically as the user types
// to take a snapshot of the state of the
......
......@@ -555,6 +555,16 @@ TerminalDisplay::TerminalDisplay(QWidget* parent)
_verticalLayout->setContentsMargins(0, 0, 0, 0);
setLayout(_verticalLayout);
new AutoScrollHandler(this);
// Keep this last
auto focusWatcher = new CompositeWidgetFocusWatcher(this, this);
connect(focusWatcher, &CompositeWidgetFocusWatcher::compositeFocusChanged,
this, [this](bool focused) {_hasCompositeFocus = focused;});
connect(focusWatcher, &CompositeWidgetFocusWatcher::compositeFocusChanged,
this, &TerminalDisplay::compositeFocusChanged);
connect(focusWatcher, &CompositeWidgetFocusWatcher::compositeFocusChanged,
_headerBar, &TerminalHeaderBar::setFocusIndicatorState);
#ifndef QT_NO_ACCESSIBILITY
QAccessible::installFactory(Konsole::accessibleInterfaceFactory);
#endif
......@@ -1826,8 +1836,6 @@ void TerminalDisplay::focusOutEvent(QFocusEvent*)
Q_ASSERT(!_textBlinking);
_showUrlHint = false;
_headerBar->terminalFocusOut();
emit focusLost();
}
void TerminalDisplay::focusInEvent(QFocusEvent*)
......@@ -1841,8 +1849,6 @@ void TerminalDisplay::focusInEvent(QFocusEvent*)
if (_allowBlinkingText && _hasTextBlinker) {
_blinkTextTimer->start();
}
_headerBar->terminalFocusIn();
emit focusGained();
}
void TerminalDisplay::blinkTextEvent()
......@@ -1853,7 +1859,6 @@ void TerminalDisplay::blinkTextEvent()
// TODO: Optimize to only repaint the areas of the widget where there is
// blinking text rather than repainting the whole widget.
_headerBar->terminalFocusOut();
update();
}
......@@ -4026,3 +4031,49 @@ void TerminalDisplay::applyProfile(const Profile::Ptr &profile)
_mouseWheelZoom = profile->mouseWheelZoomEnabled();
setAlternateScrolling(profile->property<bool>(Profile::AlternateScrolling));
}
//
// CompositeWidgetFocusWatcher
//
CompositeWidgetFocusWatcher::CompositeWidgetFocusWatcher(QWidget *compositeWidget, QObject *parent)
: QObject(parent)
, _compositeWidget(compositeWidget)
{
registerWidgetAndChildren(compositeWidget);
}
bool CompositeWidgetFocusWatcher::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched);
auto *focusEvent = static_cast<QFocusEvent *>(event);
switch(event->type()) {
case QEvent::FocusIn:
emit compositeFocusChanged(true);
break;
case QEvent::FocusOut:
if(focusEvent->reason() != Qt::PopupFocusReason) {
emit compositeFocusChanged(false);
}
break;
default:
break;
}
return false;
}
void CompositeWidgetFocusWatcher::registerWidgetAndChildren(QWidget *widget)
{
Q_ASSERT(widget != nullptr);
if (widget->focusPolicy() != Qt::NoFocus) {
widget->installEventFilter(this);
}
for (auto *child: widget->children()) {
auto *childWidget = qobject_cast<QWidget *>(child);
if (childWidget) {
registerWidgetAndChildren(childWidget);
}
}
}
......@@ -371,6 +371,11 @@ public:
/** See setAlternateScrolling() */
bool alternateScrolling() const;
bool hasCompositeFocus() const
{
return _hasCompositeFocus;
}
public Q_SLOTS:
/**
* Scrolls current ScreenWindow
......@@ -546,8 +551,7 @@ Q_SIGNALS:
void sendStringToEmu(const QByteArray &local8BitString);
void focusLost();
void focusGained();
void compositeFocusChanged(bool focused);
protected:
// events
......@@ -880,6 +884,8 @@ private:
bool _drawOverlay;
Qt::Edge _overlayEdge;
bool _hasCompositeFocus;
};
class AutoScrollHandler : public QObject
......@@ -899,6 +905,29 @@ private:
int _timerId;
};
// Watches compositeWidget and all its focusable children,
// and emits focusChanged() signal when either compositeWidget's
// or a child's focus changed.
// Limitation: children added after the object was created
// will not be registered.
class CompositeWidgetFocusWatcher : public QObject
{
Q_OBJECT
public:
explicit CompositeWidgetFocusWatcher(QWidget *compositeWidget, QObject *parent);
bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;
Q_SIGNALS:
void compositeFocusChanged(bool focused);
private:
void registerWidgetAndChildren(QWidget *widget);
QWidget *_compositeWidget;
};
}
#endif // TERMINALDISPLAY_H
......@@ -78,10 +78,9 @@ TerminalHeaderBar::TerminalHeaderBar(QWidget *parent)
setLayout(m_boxLayout);
setAutoFillBackground(true);
terminalFocusOut();
setFocusIndicatorState(false);
connect(m_toggleExpandedMode, &QToolButton::clicked,
this, &TerminalHeaderBar::requestToggleExpansion);
}
// Hack untill I can detangle the creation of the TerminalViews
......@@ -103,6 +102,12 @@ void TerminalHeaderBar::finishHeaderSetup(ViewProperties *properties)
connect(m_closeBtn, &QToolButton::clicked, controller, &SessionController::closeSession);
}
void TerminalHeaderBar::setFocusIndicatorState(bool focused)
{
m_terminalIsFocused = focused;
update();
}
void TerminalHeaderBar::paintEvent(QPaintEvent *paintEvent)
{
/* Try to get the widget that's 10px above this one.
......@@ -165,16 +170,4 @@ void TerminalHeaderBar::mouseReleaseEvent(QMouseEvent* ev)
Q_UNUSED(ev)
}
void TerminalHeaderBar::terminalFocusIn()
{
m_terminalIsFocused = true;
update();
}
void TerminalHeaderBar::terminalFocusOut()
{
m_terminalIsFocused = false;
update();
}
}
......@@ -39,8 +39,8 @@ public:
explicit TerminalHeaderBar(QWidget *parent = nullptr);
void finishHeaderSetup(ViewProperties *properties);
void terminalFocusIn();
void terminalFocusOut();
public Q_SLOTS:
void setFocusIndicatorState(bool focused);
protected:
void paintEvent(QPaintEvent* paintEvent) override;
......
......@@ -363,7 +363,7 @@ void TabbedViewContainer::splitView(TerminalDisplay *view, Qt::Orientation orien
void TabbedViewContainer::connectTerminalDisplay(TerminalDisplay *display)
{
auto item = display->sessionController();
connect(item, &Konsole::SessionController::focused, this,
connect(item, &Konsole::SessionController::viewFocused, this,
&Konsole::TabbedViewContainer::currentSessionControllerChanged);
connect(item, &Konsole::ViewProperties::titleChanged, this,
......@@ -379,7 +379,7 @@ void TabbedViewContainer::connectTerminalDisplay(TerminalDisplay *display)
void TabbedViewContainer::disconnectTerminalDisplay(TerminalDisplay *display)
{
auto item = display->sessionController();
disconnect(item, &Konsole::SessionController::focused, this,
disconnect(item, &Konsole::SessionController::viewFocused, this,
&Konsole::TabbedViewContainer::currentSessionControllerChanged);
disconnect(item, &Konsole::ViewProperties::titleChanged, this,
......
......@@ -525,7 +525,7 @@ void ViewManager::focusAnotherTerminal(ViewSplitter *toplevelSplitter)
}
}
void ViewManager::viewActivated(TerminalDisplay *view)
void ViewManager::activateView(TerminalDisplay *view)
{
Q_ASSERT(view != nullptr);
......@@ -586,7 +586,7 @@ SessionController *ViewManager::createController(Session *session, TerminalDispl
// create a new controller for the session, and ensure that this view manager
// is notified when the view gains the focus
auto controller = new SessionController(session, view, this);
connect(controller, &Konsole::SessionController::focused, this,
connect(controller, &Konsole::SessionController::viewFocused, this,
&Konsole::ViewManager::controllerChanged);
connect(session, &Konsole::Session::destroyed, controller,
&Konsole::SessionController::deleteLater);
......@@ -705,7 +705,7 @@ TabbedViewContainer *ViewManager::createContainer()
connect(container, &Konsole::TabbedViewContainer::viewRemoved, this,
&Konsole::ViewManager::viewDestroyed);
connect(container, &Konsole::TabbedViewContainer::activeViewChanged, this,
&Konsole::ViewManager::viewActivated);
&Konsole::ViewManager::activateView);
return container;
}
......
......@@ -333,7 +333,7 @@ private Q_SLOTS:
// called when the active view in a ViewContainer changes, so
// that we can plug the appropriate actions into the UI
void viewActivated(TerminalDisplay *view);
void activateView(TerminalDisplay *view);
void focusUp();
void focusDown();
......
......@@ -1098,30 +1098,16 @@ void Vt102Emulation::sendMouseEvent(int cb, int cx, int cy, int eventType)
}
/**
* The focus lost event can be used by Vim (or other terminal applications)
* to recognize that the konsole window has lost focus.
* The focus change event can be used by Vim (or other terminal applications)
* to recognize that the konsole window has changed focus.
* The escape sequence is also used by iTerm2.
* Vim needs the following plugin to be installed to convert the escape
* sequence into the FocusLost autocmd: https://github.com/sjl/vitality.vim
* sequence into the FocusLost/FocusGained autocmd:
* https://github.com/sjl/vitality.vim
*/
void Vt102Emulation::focusLost()
{
if (_reportFocusEvents) {
sendString("\033[O");
}
}
/**
* The focus gained event can be used by Vim (or other terminal applications)
* to recognize that the konsole window has gained focus again.
* The escape sequence is also used by iTerm2.
* Vim needs the following plugin to be installed to convert the escape
* sequence into the FocusGained autocmd: https://github.com/sjl/vitality.vim
*/
void Vt102Emulation::focusGained()
{
void Vt102Emulation::focusChanged(bool focused) {
if (_reportFocusEvents) {
sendString("\033[I");
sendString(focused ? "\033[I" : "\033[O");
}
}
......
......@@ -93,8 +93,7 @@ public Q_SLOTS:
void sendText(const QString &text) override;
void sendKeyEvent(QKeyEvent *) override;
void sendMouseEvent(int buttons, int column, int line, int eventType) override;
void focusLost() override;
void focusGained() override;
void focusChanged(bool focused) override;
protected:
// reimplemented from Emulation
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment