When an arrow key is not accepted look for adjacent views
When no qml items manage the arrow key event, the root item will manage it looking to give focus to a view in the given direction derived from the arrow key BUG:455783
This commit is contained in:
parent
369194ab72
commit
027ca22908
8 changed files with 182 additions and 15 deletions
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtGraphicalEffects 1.12
|
||||
import org.kde.kwin 3.0 as KWinComponents
|
||||
|
@ -100,12 +101,36 @@ Rectangle {
|
|||
switchTo(desktopId);
|
||||
} else if (event.key == Qt.Key_Up) {
|
||||
event.accepted = selectNext(WindowHeap.Direction.Up);
|
||||
if (!event.accepted) {
|
||||
let view = effect.getView(Qt.TopEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
} else if (event.key == Qt.Key_Down) {
|
||||
event.accepted = selectNext(WindowHeap.Direction.Down);
|
||||
if (!event.accepted) {
|
||||
let view = effect.getView(Qt.BottomEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
} else if (event.key == Qt.Key_Left) {
|
||||
event.accepted = selectNext(WindowHeap.Direction.Left);
|
||||
if (!event.accepted) {
|
||||
let view = effect.getView(Qt.LeftEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
} else if (event.key == Qt.Key_Right) {
|
||||
event.accepted = selectNext(WindowHeap.Direction.Right);
|
||||
if (!event.accepted) {
|
||||
let view = effect.getView(Qt.RightEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
} else if (event.key == Qt.Key_Return || event.key == Qt.Key_Space) {
|
||||
for (let i = 0; i < gridRepeater.count; i++) {
|
||||
if (gridRepeater.itemAt(i).focus) {
|
||||
|
@ -229,6 +254,15 @@ Rectangle {
|
|||
height: container.height
|
||||
|
||||
clientModel: stackModel
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border {
|
||||
color: PlasmaCore.Theme.highlightColor
|
||||
width: 1 / grid.scale
|
||||
}
|
||||
visible: parent.activeFocus
|
||||
}
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: {
|
||||
|
|
|
@ -42,6 +42,7 @@ FocusScope {
|
|||
|
||||
Keys.priority: Keys.AfterItem
|
||||
Keys.forwardTo: searchField
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
heap.forceActiveFocus();
|
||||
if (heap.count === 1) {
|
||||
|
@ -49,6 +50,31 @@ FocusScope {
|
|||
}
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
let view = effect.getView(Qt.LeftEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
Keys.onRightPressed: {
|
||||
let view = effect.getView(Qt.RightEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
Keys.onUpPressed: {
|
||||
let view = effect.getView(Qt.TopEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
Keys.onDownPressed: {
|
||||
let view = effect.getView(Qt.BottomEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
|
||||
KWinComponents.DesktopBackgroundItem {
|
||||
id: backgroundItem
|
||||
activity: KWinComponents.Workspace.currentActivity
|
||||
|
|
|
@ -290,7 +290,7 @@ Item {
|
|||
imagePath: "widgets/viewitem"
|
||||
prefix: "hover"
|
||||
z: -1
|
||||
visible: !thumb.windowHeap.dragActive && (hoverHandler.hovered || selected)
|
||||
visible: !thumb.windowHeap.dragActive && (hoverHandler.hovered || selected) && Window.window.activeFocusItem
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
|
|
|
@ -38,6 +38,30 @@ Item {
|
|||
|
||||
Keys.priority: Keys.AfterItem
|
||||
Keys.forwardTo: searchField
|
||||
Keys.onLeftPressed: {
|
||||
let view = effect.getView(Qt.LeftEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
Keys.onRightPressed: {
|
||||
let view = effect.getView(Qt.RightEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
Keys.onUpPressed: {
|
||||
let view = effect.getView(Qt.TopEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
Keys.onDownPressed: {
|
||||
let view = effect.getView(Qt.BottomEdge)
|
||||
if (view) {
|
||||
effect.activateView(view)
|
||||
}
|
||||
}
|
||||
|
||||
KWinComponents.DesktopBackgroundItem {
|
||||
activity: KWinComponents.Workspace.currentActivity
|
||||
|
|
|
@ -220,17 +220,89 @@ QuickSceneView *QuickSceneEffect::viewAt(const QPoint &pos) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
QuickSceneView *QuickSceneEffect::activeView() const
|
||||
{
|
||||
auto it = std::find_if(d->views.constBegin(), d->views.constEnd(), [](QuickSceneView *v) {
|
||||
return v->window()->activeFocusItem();
|
||||
});
|
||||
|
||||
QuickSceneView *screenView = nullptr;
|
||||
|
||||
if (it == d->views.constEnd()) {
|
||||
screenView = d->views.value(effects->activeScreen());
|
||||
} else {
|
||||
screenView = (*it);
|
||||
}
|
||||
|
||||
return screenView;
|
||||
}
|
||||
|
||||
KWin::QuickSceneView *QuickSceneEffect::getView(Qt::Edge edge)
|
||||
{
|
||||
auto screenView = activeView();
|
||||
|
||||
QuickSceneView *candidate = nullptr;
|
||||
|
||||
for (auto *v : d->views) {
|
||||
switch (edge) {
|
||||
case Qt::LeftEdge:
|
||||
if (v->geometry().left() < screenView->geometry().left()) {
|
||||
// Look for the nearest view from the current
|
||||
if (!candidate || v->geometry().left() > candidate->geometry().left() || (v->geometry().left() == candidate->geometry().left() && v->geometry().top() > candidate->geometry().top())) {
|
||||
candidate = v;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::TopEdge:
|
||||
if (v->geometry().top() < screenView->geometry().top()) {
|
||||
if (!candidate || v->geometry().top() > candidate->geometry().top() || (v->geometry().top() == candidate->geometry().top() && v->geometry().left() > candidate->geometry().left())) {
|
||||
candidate = v;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::RightEdge:
|
||||
if (v->geometry().right() > screenView->geometry().right()) {
|
||||
if (!candidate || v->geometry().right() < candidate->geometry().right() || (v->geometry().right() == candidate->geometry().right() && v->geometry().top() > candidate->geometry().top())) {
|
||||
candidate = v;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::BottomEdge:
|
||||
if (v->geometry().bottom() > screenView->geometry().bottom()) {
|
||||
if (!candidate || v->geometry().bottom() < candidate->geometry().bottom() || (v->geometry().bottom() == candidate->geometry().bottom() && v->geometry().left() > candidate->geometry().left())) {
|
||||
candidate = v;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
void QuickSceneEffect::activateView(QuickSceneView *view)
|
||||
{
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *av = activeView();
|
||||
// Already properly active?
|
||||
if (view == av && av->window()->activeFocusItem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto *otherView : d->views) {
|
||||
if (otherView == view && !view->window()->activeFocusItem()) {
|
||||
QFocusEvent focusEvent(QEvent::FocusIn, Qt::ActiveWindowFocusReason);
|
||||
qApp->sendEvent(view->window(), &focusEvent);
|
||||
} else if (otherView->window()->activeFocusItem()) {
|
||||
} else if (otherView != view && otherView->window()->activeFocusItem()) {
|
||||
QFocusEvent focusEvent(QEvent::FocusOut, Qt::ActiveWindowFocusReason);
|
||||
qApp->sendEvent(otherView->window(), &focusEvent);
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT activeViewChanged(view);
|
||||
}
|
||||
|
||||
// Screen views are repainted just before kwin performs its compositing cycle to avoid stalling for vblank
|
||||
|
@ -362,6 +434,9 @@ void QuickSceneEffect::startInternal()
|
|||
addScreen(screen);
|
||||
}
|
||||
|
||||
// Ensure one view has an active focus item
|
||||
activateView(activeView());
|
||||
|
||||
connect(effects, &EffectsHandler::screenAdded, this, &QuickSceneEffect::handleScreenAdded);
|
||||
connect(effects, &EffectsHandler::screenRemoved, this, &QuickSceneEffect::handleScreenRemoved);
|
||||
|
||||
|
@ -424,19 +499,12 @@ void QuickSceneEffect::windowInputMouseEvent(QEvent *event)
|
|||
|
||||
void QuickSceneEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent)
|
||||
{
|
||||
auto it = std::find_if(d->views.constBegin(), d->views.constEnd(), [](QuickSceneView *v) {
|
||||
return v->window()->activeFocusItem();
|
||||
});
|
||||
auto *screenView = activeView();
|
||||
|
||||
if (it == d->views.constEnd()) {
|
||||
QuickSceneView *screenView = d->views.value(effects->activeScreen());
|
||||
if (screenView) {
|
||||
activateView(screenView);
|
||||
screenView->forwardKeyEvent(keyEvent);
|
||||
}
|
||||
} else {
|
||||
(*it)->forwardKeyEvent(keyEvent);
|
||||
return;
|
||||
if (screenView) {
|
||||
// ActiveView may not have an activeFocusItem yet
|
||||
activateView(screenView);
|
||||
screenView->forwardKeyEvent(keyEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,3 +540,5 @@ bool QuickSceneEffect::touchUp(qint32 id, quint32 time)
|
|||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include <moc_kwinquickeffect.cpp>
|
||||
|
|
|
@ -66,6 +66,7 @@ private:
|
|||
class KWINEFFECTS_EXPORT QuickSceneEffect : public Effect
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QuickSceneView *activeView READ activeView NOTIFY activeViewChanged)
|
||||
|
||||
public:
|
||||
explicit QuickSceneEffect(QObject *parent = nullptr);
|
||||
|
@ -81,6 +82,8 @@ public:
|
|||
*/
|
||||
void setRunning(bool running);
|
||||
|
||||
QuickSceneView *activeView() const;
|
||||
|
||||
/**
|
||||
* Returns all scene views managed by this effect. If the effect is not running,
|
||||
* this function returns an empty QHash.
|
||||
|
@ -92,10 +95,16 @@ public:
|
|||
*/
|
||||
QuickSceneView *viewAt(const QPoint &pos) const;
|
||||
|
||||
/**
|
||||
* Get a view at the given direction from the active view
|
||||
* Returns null if no other views exist in the given direction
|
||||
*/
|
||||
Q_INVOKABLE KWin::QuickSceneView *getView(Qt::Edge edge);
|
||||
|
||||
/**
|
||||
* Sets the given @a view as active. It will get a focusin event and all the other views will be set as inactive
|
||||
*/
|
||||
void activateView(QuickSceneView *view);
|
||||
Q_INVOKABLE void activateView(QuickSceneView *view);
|
||||
|
||||
/**
|
||||
* Returns the source URL.
|
||||
|
@ -134,6 +143,7 @@ public:
|
|||
Q_SIGNALS:
|
||||
void itemDraggedOutOfScreen(QQuickItem *item, QList<EffectScreen *> screens);
|
||||
void itemDroppedOutOfScreen(const QPointF &globalPos, QQuickItem *item, EffectScreen *screen);
|
||||
void activeViewChanged(KWin::QuickSceneView *view);
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@ if (KWIN_BUILD_KCMS)
|
|||
KF5::Service
|
||||
Qt::DBus
|
||||
Qt::UiTools
|
||||
kwineffects
|
||||
)
|
||||
install(TARGETS kcm_kwin4_genericscripted DESTINATION ${KDE_INSTALL_PLUGINDIR}/kwin/effects/configs)
|
||||
endif()
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// own
|
||||
#include "dbuscall.h"
|
||||
#include "desktopbackgrounditem.h"
|
||||
#include "kwinquickeffect.h"
|
||||
#include "screenedgeitem.h"
|
||||
#include "scripting_logging.h"
|
||||
#include "scriptingutils.h"
|
||||
|
@ -658,6 +659,7 @@ void KWin::Scripting::init()
|
|||
qmlRegisterType<ScriptingModels::V3::ClientModel>("org.kde.kwin", 3, 0, "ClientModel");
|
||||
qmlRegisterType<ScriptingModels::V3::ClientFilterModel>("org.kde.kwin", 3, 0, "ClientFilterModel");
|
||||
qmlRegisterType<ScriptingModels::V3::VirtualDesktopModel>("org.kde.kwin", 3, 0, "VirtualDesktopModel");
|
||||
qmlRegisterUncreatableType<KWin::QuickSceneView>("org.kde.kwin", 3, 0, "SceneView", QStringLiteral("Can't instantiate an object of type SceneView"));
|
||||
|
||||
qmlRegisterSingletonType<DeclarativeScriptWorkspaceWrapper>("org.kde.kwin", 3, 0, "Workspace", [](QQmlEngine *qmlEngine, QJSEngine *jsEngine) {
|
||||
Q_UNUSED(qmlEngine)
|
||||
|
|
Loading…
Reference in a new issue