Files

154 lines
5.0 KiB
QML

import QtQuick
import qs.Common
FocusScope {
id: root
required property Item sidebar
required property Item content
required property var parentModal
property string activePane: "sidebar"
property Item sidebarFocus: null
readonly property Item focusedItem: Window.activeFocusItem
property int focusRevision: 0
focus: true
// Controls take focus on press with the mouse reason, so visualFocus tells how the activating control was reached
function keyboardDriven() {
return focusedItem?.visualFocus ?? false;
}
function contains(ancestor, item) {
for (let p = item; p; p = p.parent) {
if (p === ancestor)
return true;
}
return false;
}
function transientOwnsFocus() {
for (let p = focusedItem; p && p !== root; p = p.parent) {
if (p.capturing === true || p.Accessible.role === Accessible.Dialog || p.Accessible.role === Accessible.PopupMenu)
return true;
}
return false;
}
onFocusedItemChanged: {
if (transientOwnsFocus())
return;
if (contains(sidebar, focusedItem)) {
if (focusedItem !== sidebar)
sidebarFocus = focusedItem;
activePane = "sidebar";
} else if (contains(content, focusedItem)) {
activePane = "content";
content.rememberFocus();
// Focus that falls back to a container when its control is destroyed must not scroll the page
if (keyboardDriven() || focusedItem.cursorPosition !== undefined)
content._reveal(focusedItem);
}
}
function focusSidebar() {
focusRevision++;
activePane = "sidebar";
if (parentModal.isCompactMode)
parentModal.menuVisible = true;
if (sidebarFocus?.visible && sidebarFocus.enabled && contains(sidebar, sidebarFocus)) {
sidebarFocus.forceActiveFocus(Qt.TabFocusReason);
sidebar.ensureRowVisible(sidebarFocus);
} else {
sidebar.focusNavigation();
}
}
function focusContent(keyboard = true) {
focusRevision++;
activePane = "content";
if (parentModal.isCompactMode)
parentModal.menuVisible = false;
content.keyboardNavigation = keyboard;
content._focusPage();
}
function focusSearch() {
focusRevision++;
activePane = "sidebar";
if (parentModal.isCompactMode)
parentModal.menuVisible = true;
sidebar.focusSearch();
}
function pointerFocus(pane) {
const revision = ++focusRevision;
activePane = pane;
// Child controls get the press first. A background click only moves the keyboard region, highlighting nothing.
Qt.callLater(() => {
if (revision !== focusRevision || transientOwnsFocus())
return;
const region = pane === "sidebar" ? sidebar : content;
if (contains(region, focusedItem))
return;
if (pane === "sidebar")
sidebar.forceActiveFocus(Qt.MouseFocusReason);
else
content.parkFocus();
});
}
TapHandler {
parent: root.sidebar
onPressedChanged: {
if (pressed)
root.pointerFocus("sidebar");
}
}
// Page scrollers accept presses before this handler sees them; SettingsContent gives each one its own
TapHandler {
parent: root.content
onPressedChanged: {
if (pressed)
root.pointerFocus("content");
}
}
TapHandler {
acceptedButtons: Qt.BackButton
onTapped: {
if (!transientOwnsFocus())
root.content.goBack(false);
}
}
Keys.onPressed: event => {
const mods = event.modifiers & ~Qt.KeypadModifier;
if (transientOwnsFocus()) {
if ((event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab || event.key === Qt.Key_F) && (mods & Qt.ControlModifier))
event.accepted = true;
return;
}
if ((event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) && (mods === Qt.ControlModifier || mods === (Qt.ControlModifier | Qt.ShiftModifier))) {
if (activePane === "sidebar")
focusContent();
else
focusSidebar();
event.accepted = true;
} else if (mods === Qt.AltModifier && (event.key === Qt.Key_Left || event.key === Qt.Key_Right)) {
const forward = (event.key === Qt.Key_Right) !== I18n.isRtl;
if (forward !== (activePane === "sidebar"))
return;
if (forward)
focusContent();
else
focusSidebar();
event.accepted = true;
} else if (event.key === Qt.Key_F && mods === Qt.ControlModifier) {
focusSearch();
event.accepted = true;
}
}
}