mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-30 06:57:49 -04:00
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:
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.
|
||||
|
||||
|
||||
+1
-1
Submodule dux-qml-common updated: dbf7e6c3d4...604ba61ee5.
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in new issue
Block a user