mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
Promote management tiles to status rows in convergence
Wi-Fi, Bluetooth, Sound and Battery behave differently from toggle tiles: tapping them should open a detail panel, not flip a switch. On a phone the long-press convention handles this, but with a mouse long-press is unnatural. Pull these four tiles out of the grid when convergence mode is active and show them as full-width rows above the remaining tiles. Each row has two click zones — a toggle pill on the left that still switches the service on/off, and a detail area on the right (name, status, chevron) that opens the Plasma applet popup. The grid hides the duplicates so they only appear once.
This commit is contained in:
parent
8017e4eaa0
commit
2a137d1ac9
3 changed files with 260 additions and 2 deletions
|
|
@ -137,6 +137,7 @@ ecm_target_qml_sources(mobileshellplugin SOURCES
|
||||||
qml/actiondrawer/private/QuickSettingsDrawer.qml
|
qml/actiondrawer/private/QuickSettingsDrawer.qml
|
||||||
qml/actiondrawer/private/QuickSettingsFullDelegate.qml
|
qml/actiondrawer/private/QuickSettingsFullDelegate.qml
|
||||||
qml/actiondrawer/private/QuickSettingsMinimizedDelegate.qml
|
qml/actiondrawer/private/QuickSettingsMinimizedDelegate.qml
|
||||||
|
qml/actiondrawer/private/QuickSettingsStatusRow.qml
|
||||||
qml/actiondrawer/private/QuickSettingsPanel.qml
|
qml/actiondrawer/private/QuickSettingsPanel.qml
|
||||||
qml/actiondrawer/private/ContentContainer.qml
|
qml/actiondrawer/private/ContentContainer.qml
|
||||||
qml/actiondrawer/private/DetailPopup.qml
|
qml/actiondrawer/private/DetailPopup.qml
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ Item {
|
||||||
readonly property int pageSize: rowCount * columnCount
|
readonly property int pageSize: rowCount * columnCount
|
||||||
readonly property int quickSettingsCount: quickSettingsModel.count
|
readonly property int quickSettingsCount: quickSettingsModel.count
|
||||||
|
|
||||||
|
// Management tiles — promoted to full-width status rows in convergence.
|
||||||
|
readonly property var __managementCommands: ({
|
||||||
|
"plasma-open-settings kcm_mobile_wifi": "org.kde.plasma.networkmanagement",
|
||||||
|
"plasma-open-settings kcm_bluetooth": "org.kde.plasma.bluetooth",
|
||||||
|
"plasma-open-settings kcm_pulseaudio": "org.kde.plasma.volume",
|
||||||
|
"plasma-open-settings kcm_mobile_power": "org.kde.plasma.battery",
|
||||||
|
})
|
||||||
|
readonly property bool isConvergence: ShellSettings.Settings.convergenceModeEnabled
|
||||||
|
function isManagementTile(cmd) { return cmd in __managementCommands; }
|
||||||
|
|
||||||
readonly property alias brightnessPressedValue: brightnessItem.brightnessPressedValue
|
readonly property alias brightnessPressedValue: brightnessItem.brightnessPressedValue
|
||||||
|
|
||||||
function resetSwipeView() {
|
function resetSwipeView() {
|
||||||
|
|
@ -126,6 +136,34 @@ Item {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
// Management status rows (convergence mode only)
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
visible: root.isConvergence
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.quickSettingsModel
|
||||||
|
delegate: QuickSettingsStatusRow {
|
||||||
|
required property var modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: root.isManagementTile(modelData.settingsCommand)
|
||||||
|
text: modelData.text
|
||||||
|
status: modelData.status
|
||||||
|
icon: modelData.icon
|
||||||
|
enabled: modelData.enabled
|
||||||
|
toggleFunction: modelData.toggle
|
||||||
|
onDetailClicked: {
|
||||||
|
let pluginId = root.__managementCommands[modelData.settingsCommand];
|
||||||
|
if (pluginId) detailPopup.show(pluginId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Quick settings view
|
// Quick settings view
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
@ -159,8 +197,10 @@ Item {
|
||||||
delegate: MobileShell.BaseItem {
|
delegate: MobileShell.BaseItem {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
height: root.rowHeight
|
readonly property bool __hidden: root.isConvergence && root.isManagementTile(modelData.settingsCommand)
|
||||||
width: root.columnWidth
|
height: __hidden ? 0 : root.rowHeight
|
||||||
|
width: __hidden ? 0 : root.columnWidth
|
||||||
|
visible: !__hidden
|
||||||
padding: Kirigami.Units.smallSpacing
|
padding: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
contentItem: QuickSettingsFullDelegate {
|
contentItem: QuickSettingsFullDelegate {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.plasma.private.mobileshell as MobileShell
|
||||||
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full-width management row (Wi-Fi, Bluetooth, Audio, Battery) shown in
|
||||||
|
* convergence mode. Two interaction zones:
|
||||||
|
* - Left toggle pill: icon + indicator dot, tap toggles the service.
|
||||||
|
* - Right detail area: name + status + chevron, tap opens detail popup.
|
||||||
|
*/
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string text
|
||||||
|
required property string status
|
||||||
|
required property string icon
|
||||||
|
required property bool enabled
|
||||||
|
required property var toggleFunction
|
||||||
|
|
||||||
|
signal detailClicked()
|
||||||
|
|
||||||
|
implicitHeight: Kirigami.Units.gridUnit * 3.6
|
||||||
|
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||||
|
|
||||||
|
// ── Palette (shared with tile delegates) ────────────────────────────
|
||||||
|
readonly property color enabledBg: Kirigami.ColorUtils.tintWithAlpha(
|
||||||
|
Kirigami.Theme.highlightColor, Kirigami.Theme.backgroundColor, 0.6)
|
||||||
|
readonly property color enabledBgPressed: Kirigami.ColorUtils.tintWithAlpha(
|
||||||
|
Kirigami.Theme.highlightColor, Kirigami.Theme.backgroundColor, 0.4)
|
||||||
|
readonly property color enabledBorder: Qt.darker(Kirigami.Theme.highlightColor, 1.25)
|
||||||
|
|
||||||
|
readonly property color disabledBg: Kirigami.Theme.backgroundColor
|
||||||
|
readonly property color disabledBgPressed: Qt.darker(disabledBg, 1.1)
|
||||||
|
readonly property color disabledBorder: {
|
||||||
|
let bg = Kirigami.Theme.backgroundColor;
|
||||||
|
let fg = Kirigami.Theme.textColor;
|
||||||
|
if (Kirigami.ColorUtils.brightnessForColor(bg) === Kirigami.ColorUtils.Light) {
|
||||||
|
return Kirigami.ColorUtils.linearInterpolation(bg, fg, 0.2);
|
||||||
|
} else {
|
||||||
|
return Kirigami.ColorUtils.linearInterpolation(bg, fg, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MobileShell.HapticsEffect { id: haptics }
|
||||||
|
|
||||||
|
// ── Outer card ──────────────────────────────────────────────────────
|
||||||
|
// Shadow
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 1
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: parent.height
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.075)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card background — always neutral base (the toggle pill carries the
|
||||||
|
// enabled highlight, not the whole row).
|
||||||
|
Rectangle {
|
||||||
|
id: cardBg
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
border.pixelAligned: false
|
||||||
|
border.width: 1
|
||||||
|
border.color: root.disabledBorder
|
||||||
|
color: root.disabledBg
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Kirigami.Units.smallSpacing
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
// ── Toggle pill (left zone) ─────────────────────────────────
|
||||||
|
Item {
|
||||||
|
id: togglePill
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredWidth: root.height - Kirigami.Units.smallSpacing * 2
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: pillBg
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
border.pixelAligned: false
|
||||||
|
border.width: 1
|
||||||
|
border.color: root.enabled ? root.enabledBorder : root.disabledBorder
|
||||||
|
color: {
|
||||||
|
if (root.enabled) {
|
||||||
|
return toggleMouse.pressed ? root.enabledBgPressed : root.enabledBg;
|
||||||
|
}
|
||||||
|
return toggleMouse.pressed ? root.disabledBgPressed : root.disabledBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: Kirigami.Units.shortDuration }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale on press
|
||||||
|
property real zoomScale: (ShellSettings.Settings.animationsEnabled && toggleMouse.pressed) ? 0.9 : 1
|
||||||
|
Behavior on zoomScale {
|
||||||
|
NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.OutExpo }
|
||||||
|
}
|
||||||
|
transform: Scale {
|
||||||
|
origin.x: togglePill.width / 2
|
||||||
|
origin.y: togglePill.height / 2
|
||||||
|
xScale: togglePill.zoomScale
|
||||||
|
yScale: togglePill.zoomScale
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
||||||
|
implicitHeight: implicitWidth
|
||||||
|
source: root.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicator dot
|
||||||
|
Rectangle {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
width: Kirigami.Units.smallSpacing * 1.5
|
||||||
|
height: width
|
||||||
|
radius: width / 2
|
||||||
|
color: root.enabled ? Kirigami.Theme.highlightColor : Kirigami.Theme.disabledTextColor
|
||||||
|
opacity: root.enabled ? 1.0 : 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: toggleMouse
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: haptics.buttonVibrate()
|
||||||
|
onClicked: {
|
||||||
|
if (root.toggleFunction) root.toggleFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Detail area (right zone) ────────────────────────────────
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
// Hover/press highlight
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
color: detailMouse.pressed ? Qt.rgba(Kirigami.Theme.textColor.r,
|
||||||
|
Kirigami.Theme.textColor.g,
|
||||||
|
Kirigami.Theme.textColor.b, 0.06)
|
||||||
|
: detailMouse.containsMouse ? Qt.rgba(Kirigami.Theme.textColor.r,
|
||||||
|
Kirigami.Theme.textColor.g,
|
||||||
|
Kirigami.Theme.textColor.b, 0.03)
|
||||||
|
: "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: detailMouse
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: haptics.buttonVibrate()
|
||||||
|
onClicked: root.detailClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Kirigami.Units.smallSpacing * 2
|
||||||
|
anchors.rightMargin: Kirigami.Units.smallSpacing * 2
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
MobileShell.MarqueeLabel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
inputText: root.text
|
||||||
|
font.weight: Font.Bold
|
||||||
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
|
||||||
|
}
|
||||||
|
|
||||||
|
MobileShell.MarqueeLabel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
inputText: root.status ? root.status : (root.enabled ? i18n("On") : i18n("Off"))
|
||||||
|
opacity: 0.6
|
||||||
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
implicitWidth: Kirigami.Units.iconSizes.small
|
||||||
|
implicitHeight: implicitWidth
|
||||||
|
source: "go-next-symbolic"
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue