dash: settings-style keyboard between the tab row & content

-fix media follows the album accent
- updated keyboard navigation docs
Fixes #3997
Port 2.0
This commit is contained in:
purian23 committed 2026-10-29 23:20:25 -04:00
1 parent f288dbc52e
commit 673ceb261e
20 files changed
+417 -255

No files matched your search

@@ -93,7 +93,7 @@ Injected: `pluginId`, `pluginService`, `popoutService`, `entryId`, `dashHost` (`
Base provides: `options`, `pluginData`, `getData(key, def)`, `setData(key, value)`.
Optional: `implicitHeight`, `focusTarget`, `restoreFocus()`, `handleKeyEvent(event)` (return true to consume; Ctrl+Tab is reserved), `blocksTabNavigation`, `menuActions`, `signal tabRequested(string id)`, `signal navFocusRequested`.
Optional: `implicitHeight`, `focusTarget`, `restoreFocus()`, `handleKeyEvent(event)` (return true to consume; Ctrl+Tab and Ctrl+PgUp/PgDown are reserved), `blocksTabNavigation`, `menuActions`, `signal tabRequested(string id)`, `signal navFocusRequested`.
For tiles inside a tab, use `Card` from `qs.Modules.DankDash.Overview` so colors, radius and tones match the overview.
+28 -193
View File
@@ -8,7 +8,6 @@ import qs.DCommon.Widgets
import qs.Widgets
import qs.Modules.ControlCenter.Widgets
import qs.Modules.DDash.Overview
import "../../DCommon/Common/FocusNavigation.js" as FocusNavigation
DPopout {
id: root
@@ -313,15 +312,8 @@ DPopout {
onCanceled: DashMetrics.panelPreview = null
}
function headerFocusTargets() {
const targets = root.editMode ? pageActions.focusTargets : [tabBar, pageTitle.focusTarget];
return targets.filter(item => item.visible && item.enabled);
}
function focusNavigation(backwards) {
const targets = headerFocusTargets();
const target = backwards ? targets[targets.length - 1] : targets[0];
target?.forceActiveFocus(backwards ? Qt.BacktabFocusReason : Qt.TabFocusReason);
keyboard.focusHeader(backwards);
}
function focusInitial() {
@@ -339,39 +331,6 @@ DPopout {
pages.currentItem?.restoreFocus?.();
}
function contentEntryFor(key) {
switch (key) {
case Qt.Key_Down:
case Qt.Key_Right:
return "forwards";
case Qt.Key_Up:
case Qt.Key_Left:
return "backwards";
}
return "";
}
function enterContent(backwards) {
const target = backwards ? (pages.currentItem?.previousFocusTarget ?? pages.focusTarget) : pages.focusTarget;
FocusNavigation.focusItem(target, backwards);
}
function cycleRegion(backwards) {
if (pages.currentHost?.activeFocus) {
if (pages.currentItem?.cycleFocus?.(backwards) === true)
return;
focusNavigation(backwards);
return;
}
const targets = headerFocusTargets();
const inHeader = targets.some(FocusNavigation.containsFocus);
if (inHeader && FocusNavigation.moveFocus(targets, backwards))
return;
if (inHeader && pages.currentItem?.cycleFocus?.(backwards) === true)
return;
enterContent(backwards);
}
function resetScroll() {
pages.contentY = 0;
}
@@ -410,159 +369,34 @@ DPopout {
focusInitial();
}
Keys.onPressed: function (event) {
if (pageActions.menuOpen)
return;
if (tabOptions.shown) {
if (event.key !== Qt.Key_Escape)
return;
tabOptions.dismiss();
event.accepted = true;
return;
Keys.onPressed: event => event.accepted = keyboard.handleKeyEvent(event)
DashKeyboard {
id: keyboard
active: root.shouldBeVisible
editable: root.activeTabId !== ""
editMode: root.editMode
optionsShown: tabOptions.shown
menuOpen: pageActions.menuOpen
tabItem: pages.currentItem
contentScope: pages.currentHost
contentTarget: pages.focusTarget
headerTargets: root.editMode ? pageActions.focusTargets : [tabBar, pageTitle.focusTarget]
headerEdge: root.verticalNavigation ? (root.navigationEdge === SettingsData.Right ? "right" : "left") : (root.navigationEdge === SettingsData.Bottom ? "down" : "up")
onTabCycleRequested: direction => {
root.cycleTab(direction);
if (!mainContainer.navigationFocused)
mainContainer.focusInitial();
}
const current = pages.currentItem;
if (current && typeof current.handleKeyEvent === "function" && current.handleKeyEvent(event) === true) {
event.accepted = true;
return;
onEditModeRequested: on => root.editMode = on
onOptionsDismissRequested: tabOptions.dismiss()
onCloseRequested: {
if (root.detailTabId !== "")
root.closeDetail();
else
root.dashVisible = false;
}
const entry = contentEntryFor(event.key);
if (entry !== "" && !pages.currentHost?.activeFocus) {
enterContent(entry === "backwards");
event.accepted = true;
return;
}
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) {
if (root.editMode) {
mainContainer.focusNavigation(event.key === Qt.Key_Backtab || !!(event.modifiers & Qt.ShiftModifier));
event.accepted = true;
return;
}
if (current?.blocksTabNavigation)
return;
root.cycleTab(event.key === Qt.Key_Backtab || (event.modifiers & Qt.ShiftModifier) ? -1 : 1);
event.accepted = true;
return;
}
if (event.key !== Qt.Key_Escape)
return;
if (root.editMode) {
root.editMode = false;
event.accepted = true;
return;
}
if (root.detailTabId !== "") {
root.closeDetail();
event.accepted = true;
return;
}
root.dashVisible = false;
event.accepted = true;
}
Shortcut {
sequence: "Tab"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: {
root.cycleTab(1);
mainContainer.focusInitial();
}
}
Shortcut {
sequence: "Shift+Tab"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: {
root.cycleTab(-1);
mainContainer.focusInitial();
}
}
Shortcut {
sequence: "Ctrl+Tab"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen
context: Qt.WindowShortcut
onActivated: {
root.cycleTab(1);
mainContainer.focusInitial();
}
}
Shortcut {
sequence: "Ctrl+Shift+Tab"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen
context: Qt.WindowShortcut
onActivated: {
root.cycleTab(-1);
mainContainer.focusInitial();
}
}
Shortcut {
sequences: ["F2", "Ctrl+E"]
enabled: root.shouldBeVisible && root.activeTabId !== "" && !root.editMode && !tabOptions.shown && !pageActions.menuOpen && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: root.editMode = true
}
Shortcut {
sequence: "F6"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: mainContainer.cycleRegion(false)
}
Shortcut {
sequence: "Shift+F6"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: mainContainer.cycleRegion(true)
}
Shortcut {
sequence: "Alt+Tab"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: pages.currentItem?.cycleCardFocus?.(false)
}
Shortcut {
sequence: "Alt+Shift+Tab"
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: pages.currentItem?.cycleCardFocus?.(true)
}
Shortcut {
sequences: ["Alt+Left", "Alt+H"]
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: pages.currentItem?.moveCardFocus?.("left")
}
Shortcut {
sequences: ["Alt+Right", "Alt+L"]
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: pages.currentItem?.moveCardFocus?.("right")
}
Shortcut {
sequences: ["Alt+Up", "Alt+K"]
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: pages.currentItem?.moveCardFocus?.("up")
}
Shortcut {
sequences: ["Alt+Down", "Alt+J"]
enabled: root.shouldBeVisible && !tabOptions.shown && !pageActions.menuOpen && !root.editMode && !pages.currentItem?.blocksTabNavigation
context: Qt.WindowShortcut
onActivated: pages.currentItem?.moveCardFocus?.("down")
}
DashOptionsSheet {
@@ -634,6 +468,7 @@ DPopout {
onEditRequested: root.editMode = true
visible: root.showTabs && !root.editMode
orientation: root.verticalNavigation ? Qt.Vertical : Qt.Horizontal
tabHighlights: true
currentIndex: root.currentTabIndex
nextFocusTarget: pageActions.focusTargets[0] ?? pages.focusTarget
previousFocusTarget: pages.currentItem?.previousFocusTarget ?? pages.focusTarget
+261
View File
@@ -0,0 +1,261 @@
pragma ComponentBehavior: Bound
import QtQuick
import "../../DCommon/Common/FocusNavigation.js" as FocusNavigation
Item {
id: root
property bool active: false
property bool editable: true
property bool editMode: false
property bool optionsShown: false
property bool menuOpen: false
property var tabItem: null
property Item contentScope: null
property Item contentTarget: null
property var headerTargets: []
property string headerEdge: "up"
readonly property bool idle: active && !optionsShown && !menuOpen
readonly property bool contentFocused: contentScope?.activeFocus ?? false
readonly property bool headerFocused: headerTargets.some(item => item?.activeFocus ?? false)
readonly property bool browsing: idle && !editMode && !(tabItem?.blocksTabNavigation ?? false)
signal tabCycleRequested(int direction)
signal editModeRequested(bool on)
signal optionsDismissRequested
signal closeRequested
function visibleHeaderTargets() {
return headerTargets.filter(item => item && item.visible && item.enabled);
}
function focusHeader(backwards) {
const targets = visibleHeaderTargets();
const target = backwards ? targets[targets.length - 1] : targets[0];
target?.forceActiveFocus(backwards ? Qt.BacktabFocusReason : Qt.TabFocusReason);
}
function enterContent(backwards) {
const target = backwards ? (tabItem?.previousFocusTarget ?? contentTarget) : contentTarget;
FocusNavigation.focusItem(target, backwards);
}
function cycleRegion(backwards) {
const targets = visibleHeaderTargets();
if (!targets.some(FocusNavigation.containsFocus) && targets.length > 0) {
focusHeader(backwards);
return;
}
if (FocusNavigation.moveFocus(targets, backwards))
return;
enterContent(backwards);
}
function inContent(item) {
for (let p = item; p; p = p.parent) {
if (p === contentScope)
return true;
}
return false;
}
// Tab wraps inside the content, as in Settings.
function walkContent(backwards) {
if (!contentFocused) {
enterContent(backwards);
return;
}
let item = Window.activeFocusItem;
for (let step = 0; item && step < 1000; step++) {
item = item.nextItemInFocusChain(!backwards);
if (inContent(item)) {
item.forceActiveFocus(backwards ? Qt.BacktabFocusReason : Qt.TabFocusReason);
return;
}
}
}
// A tab without restoreFocus parks focus on the host, which counts as content.
function toggleRegion() {
if (headerFocused || visibleHeaderTargets().length === 0)
enterContent(false);
else
focusHeader(false);
}
function moveCard(direction) {
if (!contentFocused) {
if (direction === headerEdge)
focusHeader(false);
else
enterContent(false);
return;
}
if (tabItem?.moveCardFocus?.(direction) !== true && direction === headerEdge)
focusHeader(false);
}
function arrowDirection(event) {
if ((event.modifiers & ~Qt.KeypadModifier) !== Qt.NoModifier)
return "";
switch (event.key) {
case Qt.Key_Left:
return "left";
case Qt.Key_Right:
return "right";
case Qt.Key_Up:
return "up";
case Qt.Key_Down:
return "down";
}
return "";
}
function contentEntryFor(key) {
switch (key) {
case Qt.Key_Down:
case Qt.Key_Right:
return "forwards";
case Qt.Key_Up:
case Qt.Key_Left:
return "backwards";
}
return "";
}
function handleKeyEvent(event) {
if (!active || menuOpen)
return false;
if (optionsShown) {
if (event.key !== Qt.Key_Escape)
return false;
optionsDismissRequested();
return true;
}
const forTab = !headerFocused || event.key === Qt.Key_Escape;
if (forTab && typeof tabItem?.handleKeyEvent === "function" && tabItem.handleKeyEvent(event) === true)
return true;
const direction = arrowDirection(event);
if (direction !== "" && contentFocused)
return !tabItem?.blocksTabNavigation && tabItem?.moveCardFocus?.(direction) === true;
const entry = contentEntryFor(event.key);
if (entry !== "" && !contentFocused) {
enterContent(entry === "backwards");
return true;
}
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) {
if (!editMode)
return false;
focusHeader(event.key === Qt.Key_Backtab || !!(event.modifiers & Qt.ShiftModifier));
return true;
}
if (event.key !== Qt.Key_Escape)
return false;
if (editMode)
editModeRequested(false);
else
closeRequested();
return true;
}
// The tab row takes Tab itself to move its highlight.
Shortcut {
sequence: "Tab"
enabled: root.browsing && !root.headerFocused
context: Qt.WindowShortcut
onActivated: root.walkContent(false)
}
Shortcut {
sequence: "Shift+Tab"
enabled: root.browsing && !root.headerFocused
context: Qt.WindowShortcut
onActivated: root.walkContent(true)
}
Shortcut {
sequences: ["Ctrl+Tab", "Ctrl+Shift+Tab"]
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.toggleRegion()
}
Shortcut {
sequence: "Ctrl+PgDown"
enabled: root.idle
context: Qt.WindowShortcut
onActivated: root.tabCycleRequested(1)
}
Shortcut {
sequence: "Ctrl+PgUp"
enabled: root.idle
context: Qt.WindowShortcut
onActivated: root.tabCycleRequested(-1)
}
Shortcut {
sequences: ["F2", "Ctrl+E"]
enabled: root.browsing && root.editable
context: Qt.WindowShortcut
onActivated: root.editModeRequested(true)
}
Shortcut {
sequence: "F6"
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.cycleRegion(false)
}
Shortcut {
sequence: "Shift+F6"
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.cycleRegion(true)
}
Shortcut {
sequence: "Alt+Tab"
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.tabItem?.cycleCardFocus?.(false)
}
Shortcut {
sequence: "Alt+Shift+Tab"
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.tabItem?.cycleCardFocus?.(true)
}
Shortcut {
sequences: ["Alt+Left", "Alt+H"]
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.moveCard("left")
}
Shortcut {
sequences: ["Alt+Right", "Alt+L"]
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.moveCard("right")
}
Shortcut {
sequences: ["Alt+Up", "Alt+K"]
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.moveCard("up")
}
Shortcut {
sequences: ["Alt+Down", "Alt+J"]
enabled: root.browsing
context: Qt.WindowShortcut
onActivated: root.moveCard("down")
}
}
+17 -2
View File
@@ -75,7 +75,17 @@ MediaChromeBase {
Rectangle {
id: viewTogglePill
readonly property bool keyboardFocused: viewToggle.activeFocus && (root.Window.activeFocusItem?.visualFocus ?? false)
// Tab can focus a segment while the group itself has none.
readonly property bool keyboardFocused: {
const item = root.Window.activeFocusItem;
if (!(item?.visualFocus ?? false))
return false;
for (let p = item; p; p = p.parent) {
if (p === viewToggle)
return true;
}
return false;
}
readonly property bool revealed: artHover.hovered || keyboardFocused
anchors.horizontalCenter: parent.horizontalCenter
@@ -102,6 +112,7 @@ MediaChromeBase {
DButtonGroup {
id: viewToggle
focusRingColor: MediaAccentService.focusRing
anchors.centerIn: parent
size: "small"
checkEnabled: false
@@ -219,7 +230,10 @@ MediaChromeBase {
width: parent.width
height: parent.height - controls.y
active: root.lyricsColumn && root.player.live
onLoaded: item.forceActiveFocus(Qt.PopupFocusReason)
onLoaded: {
if (root.player.ownsFocus())
item.forceActiveFocus(Qt.PopupFocusReason);
}
sourceComponent: LyricsOverlay {
player: root.player
@@ -397,6 +411,7 @@ MediaChromeBase {
DButton {
id: playerButton
focusRingColor: MediaAccentService.focusRing
readonly property string panelId: "players"
@@ -358,6 +358,7 @@ FocusScope {
DButton {
id: followButton
focusRingColor: MediaAccentService.focusRing
objectName: "followPlayback"
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
@@ -431,6 +432,7 @@ FocusScope {
}
DButton {
focusRingColor: MediaAccentService.focusRing
anchors.horizontalCenter: parent.horizontalCenter
text: I18n.tr("Retry")
visible: root.controller.state === "error"
@@ -132,7 +132,10 @@ MediaChromeBase {
anchors.bottom: seekBlock.visible ? seekBlock.top : parent.bottom
anchors.bottomMargin: seekBlock.visible ? Theme.spacingM : 0
active: root.player.lyricsOpen && root.player.live
onLoaded: item.forceActiveFocus(Qt.PopupFocusReason)
onLoaded: {
if (root.player.ownsFocus())
item.forceActiveFocus(Qt.PopupFocusReason);
}
sourceComponent: LyricsOverlay {
player: root.player
}
@@ -22,6 +22,7 @@ Row {
DIconButton {
id: lyricsButton
focusRingColor: MediaAccentService.focusRing
variant: "outlined"
checkable: true
checked: root.player.lyricsOpen
@@ -67,6 +68,8 @@ Row {
component GroupButton: DIconButton {
required property string panelId
focusRingColor: MediaAccentService.focusRing
readonly property bool active: root.player.panel === panelId
variant: "outlined"
@@ -32,7 +32,10 @@ Item {
Loader {
anchors.fill: parent
active: root.showLyrics && root.player.lyricsOpen && root.player.live
onLoaded: item.forceActiveFocus(Qt.PopupFocusReason)
onLoaded: {
if (root.player.ownsFocus())
item.forceActiveFocus(Qt.PopupFocusReason);
}
sourceComponent: LyricsOverlay {
player: root.player
@@ -13,6 +13,7 @@ SettingsRow {
required property var node
property bool selected: false
property bool playing: false
readonly property color insetColor: Theme.blend(MediaAccentService.accentContainer, Theme.surface, 0.7)
signal activated
@@ -27,8 +28,8 @@ SettingsRow {
clickable: true
paddingH: Theme.spacingL
paddingV: Theme.spacingL
rowColor: selected ? Theme.selectedContainer : Theme.chipSurface
titleColor: selected ? Theme.onSelectedContainer : Theme.surfaceText
rowColor: selected ? MediaAccentService.accentContainer : Theme.chipSurface
titleColor: selected ? MediaAccentService.onAccentContainer : Theme.surfaceText
subtitleColor: titleColor
topRadius: Theme.cornerRadiusLIncreased
bottomRadius: Theme.cornerRadiusLIncreased
@@ -39,13 +40,13 @@ SettingsRow {
width: Theme.iconButtonSize
height: width
radius: Theme.fullRadius(width, height)
color: root.selected ? Theme.onPrimary : Theme.chipSurfaceNested
color: root.selected ? root.insetColor : Theme.chipSurfaceNested
DIcon {
anchors.centerIn: parent
name: AudioService.sinkIcon(root.node)
size: Theme.iconSizeMedium
color: root.selected ? Theme.onSelectedContainer : Theme.onSurfaceVariant
color: root.selected ? root.titleColor : Theme.onSurfaceVariant
}
}
@@ -58,6 +59,7 @@ SettingsRow {
spacing: Theme.spacingS
DActionButton {
focusRingColor: MediaAccentService.focusRing
buttonSize: Theme.iconButtonSize
iconName: volumeSlider.volumeIcon
iconColor: root.titleColor
@@ -73,10 +75,10 @@ SettingsRow {
volume: root.node?.audio?.volume ?? 0
muted: root.node?.audio?.muted ?? false
maximumVolume: AudioService.getMaxVolumePercent(root.node)
fillColor: Theme.onSelectedContainer
fillTextColor: Theme.selectedContainer
trackColor: Theme.onPrimary
trackTextColor: Theme.onSelectedContainer
fillColor: root.titleColor
fillTextColor: root.rowColor
trackColor: root.insetColor
trackTextColor: root.titleColor
Accessible.name: I18n.tr("Volume") + ": " + root.title
onVolumeChangedByUser: volume => {
const audio = root.node?.audio;
@@ -15,6 +15,7 @@ DSeekbar {
accentColor: root.player.accent
accentTrackColor: MediaAccentService.accentTrack
accentSubtleColor: MediaAccentService.accentSubtle
focusRingColor: MediaAccentService.focusRing
isSeeking: root.player.isSeeking
onIsSeekingChanged: root.player.isSeeking = isSeeking
}
@@ -44,6 +44,7 @@ Item {
DActionButton {
id: volumeButton
focusRingColor: MediaAccentService.focusRing
readonly property string panelId: "volume"
@@ -65,6 +66,7 @@ Item {
DButton {
id: deviceChip
focusRingColor: MediaAccentService.focusRing
readonly property string panelId: "devices"
@@ -87,6 +89,7 @@ Item {
DActionButton {
id: deviceIcon
focusRingColor: MediaAccentService.focusRing
readonly property string panelId: "devices"
@@ -9,6 +9,8 @@ import qs.DCommon.Widgets
DIconButton {
id: root
focusRingColor: MediaAccentService.focusRing
required property var player
required property string mediaAction
property bool keepNavigation: false
@@ -19,6 +19,7 @@ DSlider {
signal muteRequested
size: "m"
focusRingColor: MediaAccentService.focusRing
insetIcon: volumeIcon
insetIconClickable: true
insetIconLabel: muteLabel
@@ -174,6 +174,15 @@ Item {
lyricsOpen = true;
}
// A plain Item's activeFocus stays false while a child holds focus.
function ownsFocus() {
for (let item = Window.activeFocusItem; item; item = item.parent) {
if (item === root)
return true;
}
return false;
}
function toggleLyrics(opener) {
if (!lyricsEnabled)
return;
@@ -154,6 +154,7 @@ Card {
DSeekbar {
id: seekbar
focusRingColor: MediaAccentService.focusRing
anchors.fill: parent
activePlayer: root.activePlayer
stableLength: MprisController.activePlayerStableLength
@@ -68,24 +68,17 @@ FocusScope {
clip: true
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
KeyNavigation.tab: !root.editMode && !tabOptions.shown && !pageActions.menuOpen ? root.tab?.focusTarget ?? null : null
function focusFace() {
pageActions.clearFocus();
const target = root.tab?.focusTarget ?? root;
if (typeof target.requestFocus === "function") {
target.requestFocus(false, Qt.OtherFocusReason);
return true;
}
target.forceActiveFocus(Qt.OtherFocusReason);
sheetHandle.focusTarget.focus = false;
tabLoader.focus = false;
root.forceActiveFocus(Qt.OtherFocusReason);
if (!root.editMode)
root.tab?.restoreFocus?.();
return true;
}
function focusHeader(backwards) {
const targets = root.editMode ? pageActions.focusTargets : [sheetHandle.focusTarget];
targets[backwards ? targets.length - 1 : 0]?.forceActiveFocus(backwards ? Qt.BacktabFocusReason : Qt.TabFocusReason);
}
function reportHeight() {
root.controller.setDashboardContentHeight(root.activityId, root.contentHeight + DashMetrics.islandHandleChromeHeight);
}
@@ -98,44 +91,39 @@ FocusScope {
pageActions.closeMenu();
tabOptions.dismiss();
}
// A switched-to face stays disabled until its fade is halfway, so take focus then.
onEnabledChanged: {
if (enabled && live && root.controller.keyboardDismissRequested && !activeFocus)
focusFace();
}
onEditModeChanged: {
root.controller.setEditing(root.activityId, editMode, DashMetrics.islandEditRoom);
if (!editMode) {
if (!editMode)
panelResizer.cancel();
return;
}
Qt.callLater(() => {
if (!root.live || !root.editMode)
return;
pageActions.clearFocus();
sheetHandle.focusTarget.focus = false;
tabLoader.focus = false;
root.forceActiveFocus(Qt.OtherFocusReason);
if (root.live)
root.focusFace();
});
}
Keys.onPressed: event => {
if (tabOptions.shown || pageActions.menuOpen)
return;
if (root.editMode && (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab)) {
root.focusHeader(event.key === Qt.Key_Backtab || !!(event.modifiers & Qt.ShiftModifier));
event.accepted = true;
return;
}
if (root.tab?.handleKeyEvent?.(event) === true) {
event.accepted = true;
return;
}
if (!root.editMode && (event.key === Qt.Key_F2 || (event.key === Qt.Key_E && (event.modifiers & Qt.ControlModifier)))) {
root.editMode = true;
event.accepted = true;
return;
}
if (event.key !== Qt.Key_Escape || !root.editMode)
return;
root.editMode = false;
root.focusFace();
event.accepted = true;
Keys.onPressed: event => event.accepted = keyboard.handleKeyEvent(event)
DashKeyboard {
id: keyboard
active: root.live
editable: root.entryId !== ""
editMode: root.editMode
optionsShown: tabOptions.shown
menuOpen: pageActions.menuOpen
tabItem: root.tab
contentScope: tabLoader
contentTarget: root.tab?.focusTarget ?? null
headerTargets: root.editMode ? pageActions.focusTargets : [sheetHandle.focusTarget]
onTabCycleRequested: direction => root.controller.cycleDashTab(direction)
onEditModeRequested: on => root.editMode = on
onOptionsDismissRequested: tabOptions.dismiss()
onCloseRequested: root.controller.requestCollapse()
}
Item {
@@ -172,10 +160,7 @@ FocusScope {
editMode: root.editMode
panelResizable: true
onOptionsRequested: tabOptions.presentFor(root.entryId)
onFinished: {
root.editMode = false;
root.focusFace();
}
onFinished: root.editMode = false
}
}
@@ -217,7 +202,7 @@ FocusScope {
target: root.tab
ignoreUnknownSignals: true
function onNavFocusRequested(backwards) {
root.focusHeader(backwards);
keyboard.focusHeader(backwards);
}
}
@@ -150,6 +150,38 @@ QtObject {
return activityId;
}
function dashActivityFor(entryId) {
switch (entryId) {
case DashRegistry.overviewId:
return "home";
case "notifications":
return "notificationcenter";
case "media":
return mediaAvailable ? "media" : "";
case "weather":
case "wellbeing":
case "wallpaper":
return entryId;
}
return "";
}
function requestDashTab(entryId) {
if (entryId === "notifications")
return requestNotificationCenter(false);
const activityId = dashActivityFor(entryId);
return activityId !== "" && requestActivity(activityId, true, true);
}
function cycleDashTab(direction) {
const ids = DashRegistry.visibleTabIds.filter(id => dashActivityFor(id) !== "");
if (ids.length === 0)
return false;
const pos = ids.indexOf(dashEntryIdFor(activeActivity));
const next = ids[pos < 0 ? (direction > 0 ? 0 : ids.length - 1) : (pos + direction + ids.length) % ids.length];
return next === dashEntryIdFor(activeActivity) || requestDashTab(next);
}
function dashboardWidthFor(activityId) {
return Math.min(dashboardAvailableWidth, DashMetrics.widthFor(SettingsData.showWeekNumber, undefined, DashMetrics.panelColumnsFor(dashEntryIdFor(activityId))));
}
@@ -65,6 +65,8 @@ Singleton {
readonly property color accentTrack: Theme.withAlpha(accent, 0.28)
readonly property color accentSubtle: Theme.withAlpha(accent, 0.55)
// A ring colour the user changed from primary wins over the accent.
readonly property color focusRing: Qt.colorEqual(Theme.focusRingColor, Theme.primary) ? accent : Theme.focusRingColor
readonly property string artUrl: TrackArtService.resolvedArtUrl
property var _accent: _pickAccent(TrackArtService.artwork.colors)
+2
View File
@@ -83,8 +83,10 @@ Item {
FocusRing {
radius: Theme.cornerRadiusXS + Theme.focusRingOffset
visible: root.activeFocus
border.color: root.focusRingColor
}
property color accentColor: Theme.primary
property color focusRingColor: Theme.focusRingColor
property color accentTrackColor: Theme.withAlpha(accentColor, 0.28)
property color accentSubtleColor: Theme.withAlpha(accentColor, 0.55)