mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 06:13: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/QuickSettingsFullDelegate.qml
|
||||
qml/actiondrawer/private/QuickSettingsMinimizedDelegate.qml
|
||||
qml/actiondrawer/private/QuickSettingsStatusRow.qml
|
||||
qml/actiondrawer/private/QuickSettingsPanel.qml
|
||||
qml/actiondrawer/private/ContentContainer.qml
|
||||
qml/actiondrawer/private/DetailPopup.qml
|
||||
|
|
|
|||
|
|
@ -54,6 +54,16 @@ Item {
|
|||
readonly property int pageSize: rowCount * columnCount
|
||||
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
|
||||
|
||||
function resetSwipeView() {
|
||||
|
|
@ -126,6 +136,34 @@ Item {
|
|||
anchors.left: parent.left
|
||||
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
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -159,8 +197,10 @@ Item {
|
|||
delegate: MobileShell.BaseItem {
|
||||
required property var modelData
|
||||
|
||||
height: root.rowHeight
|
||||
width: root.columnWidth
|
||||
readonly property bool __hidden: root.isConvergence && root.isManagementTile(modelData.settingsCommand)
|
||||
height: __hidden ? 0 : root.rowHeight
|
||||
width: __hidden ? 0 : root.columnWidth
|
||||
visible: !__hidden
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
|
||||
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