From 0ed2053e793add6000760c2c72cd513d55a2c628 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Fri, 9 Apr 2021 20:59:05 +0000 Subject: [PATCH] Rework top panel and improve appearance --- .../package/contents/ui/DrawerBackground.qml | 12 +- .../package/contents/ui/FullContainer.qml | 5 + .../ui/FullNotificationsContainer.qml | 3 +- .../package/contents/ui/IndicatorsRow.qml | 147 +++++++ .../package/contents/ui/SlidingContainer.qml | 216 ++++++++++ .../package/contents/ui/SlidingPanel.qml | 225 ---------- .../contents/ui/indicators/Battery.qml | 17 +- .../contents/ui/indicators/Bluetooth.qml | 32 +- .../contents/ui/indicators/SignalStrength.qml | 35 +- .../package/contents/ui/indicators/Volume.qml | 148 +------ .../package/contents/ui/indicators/Wifi.qml | 24 +- .../indicators/providers/BatteryProvider.qml | 25 ++ .../providers/BluetoothProvider.qml | 43 ++ .../providers/SignalStrengthProvider.qml | 41 ++ .../indicators/providers/VolumeProvider.qml | 151 +++++++ .../ui/indicators/providers/WifiProvider.qml | 36 ++ .../panel/package/contents/ui/main.qml | 219 ++++------ .../ui/quicksettings/BrightnessItem.qml | 105 +++-- .../contents/ui/quicksettings/Delegate.qml | 97 ++--- .../ui/quicksettings/QuickSettings.qml | 385 ------------------ .../ui/quicksettings/QuickSettingsModel.qml | 190 +++++++++ .../ui/quicksettings/QuickSettingsPanel.qml | 226 ++++++++++ 22 files changed, 1326 insertions(+), 1056 deletions(-) create mode 100644 containments/panel/package/contents/ui/IndicatorsRow.qml create mode 100644 containments/panel/package/contents/ui/SlidingContainer.qml delete mode 100644 containments/panel/package/contents/ui/SlidingPanel.qml create mode 100644 containments/panel/package/contents/ui/indicators/providers/BatteryProvider.qml create mode 100644 containments/panel/package/contents/ui/indicators/providers/BluetoothProvider.qml create mode 100644 containments/panel/package/contents/ui/indicators/providers/SignalStrengthProvider.qml create mode 100644 containments/panel/package/contents/ui/indicators/providers/VolumeProvider.qml create mode 100644 containments/panel/package/contents/ui/indicators/providers/WifiProvider.qml delete mode 100644 containments/panel/package/contents/ui/quicksettings/QuickSettings.qml create mode 100644 containments/panel/package/contents/ui/quicksettings/QuickSettingsModel.qml create mode 100644 containments/panel/package/contents/ui/quicksettings/QuickSettingsPanel.qml diff --git a/containments/panel/package/contents/ui/DrawerBackground.qml b/containments/panel/package/contents/ui/DrawerBackground.qml index d56a7351..1e158fe8 100644 --- a/containments/panel/package/contents/ui/DrawerBackground.qml +++ b/containments/panel/package/contents/ui/DrawerBackground.qml @@ -7,12 +7,16 @@ import QtQuick 2.6 import QtQuick.Layouts 1.4 import QtQuick.Controls 2.4 as QQC2 +import QtGraphicalEffects 1.12 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kirigami 2.12 as Kirigami QQC2.Control { id: root + + required property color backgroundColor + leftPadding: units.largeSpacing topPadding: units.largeSpacing rightPadding: units.largeSpacing @@ -22,9 +26,15 @@ QQC2.Control { MouseArea { anchors.fill: parent } + PlasmaCore.FrameSvgItem { + imagePath: "widgets/panel-background" + prefix: "shadow" + anchors.fill: container + anchors.margins: -PlasmaCore.Units.smallSpacing + } Rectangle { id: container - color: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.backgroundColor, {"alpha": 0.85*255}) + color: backgroundColor anchors { fill: parent leftMargin: PlasmaCore.Units.smallSpacing diff --git a/containments/panel/package/contents/ui/FullContainer.qml b/containments/panel/package/contents/ui/FullContainer.qml index ae8f4c83..2f1e2c1a 100644 --- a/containments/panel/package/contents/ui/FullContainer.qml +++ b/containments/panel/package/contents/ui/FullContainer.qml @@ -8,6 +8,7 @@ import QtQuick 2.12 import QtQuick.Layouts 1.3 import QtQml.Models 2.12 +import org.kde.kirigami 2.12 as Kirigami import org.kde.plasma.core 2.0 as PlasmaCore DrawerBackground { @@ -15,8 +16,12 @@ DrawerBackground { property Item applet property ObjectModel fullRepresentationModel property ListView fullRepresentationView + + backgroundColor: (applet.backgroundHints === PlasmaCore.Types.NoBackground) ? "transparent" : Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.backgroundColor, {"alpha": 0.8*255}) visible: shouldBeVisible + property bool shouldBeVisible: applet && (applet.status != PlasmaCore.Types.HiddenStatus && applet.status != PlasmaCore.Types.PassiveStatus) + height: parent.height width: visible ? quickSettings.width : 0 Layout.minimumHeight: applet && applet.switchHeight diff --git a/containments/panel/package/contents/ui/FullNotificationsContainer.qml b/containments/panel/package/contents/ui/FullNotificationsContainer.qml index a10112b1..1498bffa 100644 --- a/containments/panel/package/contents/ui/FullNotificationsContainer.qml +++ b/containments/panel/package/contents/ui/FullNotificationsContainer.qml @@ -15,10 +15,11 @@ FullContainer { shouldBeVisible: applet && historyModel.count > 0 visible: shouldBeVisible - + NotificationManager.Notifications { id: historyModel showExpired: true showDismissed: true + expandUnread: true } } diff --git a/containments/panel/package/contents/ui/IndicatorsRow.qml b/containments/panel/package/contents/ui/IndicatorsRow.qml new file mode 100644 index 00000000..1ef7f2cb --- /dev/null +++ b/containments/panel/package/contents/ui/IndicatorsRow.qml @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2015 Marco Martin + * SPDX-FileCopyrightText: 2021 Devin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +import QtQuick 2.12 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.12 +import QtGraphicalEffects 1.12 + +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PlasmaComponents + +import org.kde.plasma.workspace.components 2.0 as PlasmaWorkspace +import org.kde.taskmanager 0.1 as TaskManager + +import org.kde.plasma.private.nanoshell 2.0 as NanoShell +import org.kde.plasma.private.mobileshell 1.0 as MobileShell + +import "LayoutManager.js" as LayoutManager + +import "indicators" as Indicators + +Item { + id: indicatorsRow + required property var colorGroup + required property bool showGradientBackground + required property bool showDropShadow + required property color backgroundColor + + property alias colorScopeColor: icons.backgroundColor + property alias applets: appletIconsRow + + PlasmaCore.DataSource { + id: timeSource + engine: "time" + connectedSources: ["Local"] + interval: 60 * 1000 + } + + DropShadow { + anchors.fill: icons + visible: showDropShadow + cached: true + horizontalOffset: 0 + verticalOffset: 1 + radius: 4.0 + samples: 17 + color: Qt.rgba(0,0,0,0.8) + source: icons + } + + // screen top panel + PlasmaCore.ColorScope { + id: icons + z: 1 + colorGroup: indicatorsRow.colorGroup + anchors.fill: parent + + // background + Rectangle { + anchors.fill: parent + color: backgroundColor + } + Rectangle { + visible: showGradientBackground + anchors.fill: parent + gradient: Gradient { + GradientStop { + position: 1.0 + color: "transparent" + } + GradientStop { + position: 0.0 + color: Qt.rgba(0, 0, 0, 0.1) + } + } + } + + Loader { + id: strengthLoader + height: parent.height + width: item ? item.width : 0 + active: signalStrengthProvider + sourceComponent: Indicators.SignalStrength { + provider: signalStrengthProvider + } + } + + Row { + id: statusNotifierIndicatorsRow + anchors.left: strengthLoader.right + height: parent.height + Repeater { + id: statusNotifierRepeater + model: PlasmaCore.SortFilterModel { + id: filteredStatusNotifiers + filterRole: "Title" + sourceModel: PlasmaCore.DataModel { + dataSource: statusNotifierSource + } + } + + delegate: TaskWidget {} + } + } + + PlasmaComponents.Label { + id: clock + property bool is24HourTime: plasmoid.nativeInterface.isSystem24HourFormat + + anchors.fill: parent + text: Qt.formatTime(timeSource.data.Local.DateTime, is24HourTime ? "h:mm" : "h:mm ap") + color: PlasmaCore.ColorScope.textColor + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pixelSize: height / 2 + } + + RowLayout { + id: appletIconsRow + anchors { + bottom: parent.bottom + right: simpleIndicatorsLayout.left + } + height: parent.height + } + + RowLayout { + id: simpleIndicatorsLayout + anchors { + top: parent.top + bottom: parent.bottom + right: parent.right + rightMargin: units.smallSpacing + } + Indicators.Bluetooth { provider: bluetoothProvider } + Indicators.Wifi { provider: wifiProvider } + Indicators.Volume { provider: volumeProvider } + Indicators.Battery { provider: batteryProvider } + } + } +} diff --git a/containments/panel/package/contents/ui/SlidingContainer.qml b/containments/panel/package/contents/ui/SlidingContainer.qml new file mode 100644 index 00000000..19ce77c8 --- /dev/null +++ b/containments/panel/package/contents/ui/SlidingContainer.qml @@ -0,0 +1,216 @@ +/* + * SPDX-FileCopyrightText: 2014 Marco Martin + * SPDX-FileCopyrightText: 2021 Devin Lin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PlasmaComponents +import org.kde.plasma.private.nanoshell 2.0 as NanoShell + +NanoShell.FullScreenOverlay { + id: window + + property int offset: 0 // slide progress + property int openThreshold: PlasmaCore.Units.gridUnit * 2 + property bool userInteracting: false + property bool initiallyOpened: false // whether the panel is already open after a touch release (then don't restrict to collapsed height) + + // height when quicksettings is fully open + required property int fullyOpenHeight + + // flickable contentY + readonly property int openedContentY: wideScreen || offset > (collapsedHeight + openThreshold) ? -topEmptyAreaHeight : offsetToContentY(collapsedHeight) + readonly property int closedContentY: mainFlickable.contentHeight + + readonly property bool wideScreen: width > height || width > units.gridUnit * 45 + readonly property int drawerWidth: wideScreen ? contentItem.implicitWidth : width + + property int drawerX: 0 + property alias fixedArea: mainScope + property alias flickable: mainFlickable + + color: "transparent" + property alias contentItem: contentArea.contentItem + property int topPanelHeight + property int collapsedHeight + property real topEmptyAreaHeight + + property bool appletsShown: false // whether notifications or media player applets are shown + + signal closed + + width: Screen.width + height: Screen.height + + onInitiallyOpenedChanged: { + if (initiallyOpened) mainFlickable.focus = true; + } + + function offsetToContentY(num) { return -num + window.fullyOpenHeight; } + function contentYToOffset(num) { return offsetToContentY(num); } + + // avoids binding loops + function updateOffset(delta) { + // only go to collapsed height for mousearea when not widescreen + let maximum = window.wideScreen ? window.fullyOpenHeight : collapsedHeight + openThreshold / 2; + offset = Math.max(0, Math.min(maximum, offset + delta)); + if (!mainFlickable.moving && !mainFlickable.dragging && !mainFlickable.flicking) { + mainFlickable.contentY = offsetToContentY(window.offset); + } + } + + enum MovementDirection { + None = 0, + Up, + Down + } + property int direction: SlidingContainer.MovementDirection.None + + function cancelAnimations() { + closeAnim.stop(); + openAnim.stop(); + } + function open() { + cancelAnimations(); + openAnim.restart(); + initiallyOpened = true; + } + function close() { + cancelAnimations(); + closeAnim.restart(); + initiallyOpened = false; + } + function updateState() { + cancelAnimations(); + if (window.offset <= 0) { + // close immediately, so that we don't have to wait units.longDuration + window.visible = false; + window.closed(); + close(); + } else if (window.direction === SlidingContainer.MovementDirection.None) { + if (window.offset < openThreshold) { + close(); + } else { + open(); + } + } else if (offset > openThreshold && window.direction === SlidingContainer.MovementDirection.Down) { + open(); + } else if (mainFlickable.contentY > openThreshold) { + close(); + } else { + open(); + } + } + Timer { + id: updateStateTimer + interval: 0 + onTriggered: updateState() + } + + onActiveChanged: { + if (!active) { + close(); + } + } + + PropertyAnimation { + id: closeAnim + target: mainFlickable + properties: "contentY" + duration: PlasmaCore.Units.longDuration + easing.type: Easing.InOutQuad + to: window.closedContentY + onFinished: { + window.visible = false; + window.closed(); + } + } + PropertyAnimation { + id: openAnim + target: mainFlickable + properties: "contentY" + duration: PlasmaCore.Units.longDuration + easing.type: Easing.InOutQuad + to: window.openedContentY + } + + // fullscreen background + Rectangle { + anchors.fill: parent + color: Qt.rgba(0, 0, 0, 0.75) + opacity: (appletsShown ? 0.85 : 0.6) * Math.max(0, Math.min(1, offset / window.collapsedHeight)) + Behavior on opacity { // smooth opacity changes + NumberAnimation { duration: 70 } + } + } + + PlasmaCore.ColorScope { + id: mainScope + colorGroup: PlasmaCore.Theme.ViewColorGroup + anchors.fill: parent + + Flickable { + id: mainFlickable + anchors.fill: parent + + property real oldContentY + contentY: contentHeight + + onContentYChanged: { + if (contentY === oldContentY) { + window.direction = SlidingContainer.MovementDirection.None; + } else { + window.direction = contentY > oldContentY ? SlidingContainer.MovementDirection.Up : SlidingContainer.MovementDirection.Down; + } + window.offset = contentYToOffset(contentY); + oldContentY = contentY; + + // close panel immediately after panel is not shown, and the flickable is not being dragged + if (initiallyOpened && window.offset <= 0 && !mainFlickable.dragging && !closeAnim.running && !openAnim.running) { + window.updateState(); + focus = false; + } + } + + boundsMovement: Flickable.StopAtBounds + contentWidth: window.width + contentHeight: window.height + bottomMargin: window.height + onMovementStarted: { + window.cancelAnimations(); + window.userInteracting = true; + } + onFlickStarted: window.userInteracting = true; + onMovementEnded: { + window.userInteracting = false; + window.updateState(); + } + onFlickEnded: { + window.userInteracting = true; + window.updateState(); + } + + MouseArea { + id: dismissArea + z: 2 + width: parent.width + height: mainFlickable.contentHeight + onClicked: window.close(); + + // actual sliding contents + PlasmaComponents.Control { + id: contentArea + z: 1 + x: drawerX + width: drawerWidth + } + } + } + } +} diff --git a/containments/panel/package/contents/ui/SlidingPanel.qml b/containments/panel/package/contents/ui/SlidingPanel.qml deleted file mode 100644 index 6b39f21e..00000000 --- a/containments/panel/package/contents/ui/SlidingPanel.qml +++ /dev/null @@ -1,225 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2014 Marco Martin - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -import QtQuick 2.14 -import QtQuick.Layouts 1.1 -import QtQuick.Window 2.2 -import org.kde.plasma.core 2.0 as PlasmaCore -import org.kde.plasma.components 3.0 as PlasmaComponents -import org.kde.plasma.private.nanoshell 2.0 as NanoShell - -NanoShell.FullScreenOverlay { - id: window - - property int offset: 0 - property int openThreshold - property bool userInteracting: false - readonly property bool wideScreen: width > height || width > units.gridUnit * 45 - readonly property int drawerWidth: wideScreen ? contentItem.implicitWidth : width - property int drawerX: 0 - property alias fixedArea: mainScope - property alias flickable: mainFlickable - - color: "transparent" - property alias contentItem: contentArea.contentItem - property int headerHeight - property real topEmptyAreaHeight - - signal closed - - width: Screen.width - height: Screen.height - - enum MovementDirection { - None = 0, - Up, - Down - } - property int direction: SlidingPanel.MovementDirection.None - - function cancelAnimations() { - closeAnim.stop(); - openAnim.stop(); - } - function open() { - cancelAnimations(); - window.showFullScreen(); - openAnim.restart(); - } - function close() { - cancelAnimations(); - closeAnim.restart(); - } - function updateState() { - cancelAnimations(); - if (window.offset <= -headerHeight) { - // close immediately, so that we don't have to wait units.longDuration - window.visible = false; - window.closed(); - } else if (window.direction === SlidingPanel.MovementDirection.None) { - if (offset < openThreshold) { - close(); - } else { - openAnim.restart(); - } - } else if (offset > openThreshold && window.direction === SlidingPanel.MovementDirection.Down) { - openAnim.restart(); - } else if (mainFlickable.contentY > openThreshold) { - close(); - } else { - openAnim.restart(); - } - } - Timer { - id: updateStateTimer - interval: 0 - onTriggered: updateState() - } - - onActiveChanged: { - if (!active) { - close(); - } - } - /*onVisibleChanged: { - if (visible) { - window.width = Screen.width; - window.height = Screen.height; - window.requestActivate(); - } - }*/ - - SequentialAnimation { - id: closeAnim - PropertyAnimation { - target: window - duration: units.longDuration - easing.type: Easing.InOutQuad - properties: "offset" - from: window.offset - to: -headerHeight - } - ScriptAction { - script: { - window.visible = false; - window.closed(); - } - } - } - PropertyAnimation { - id: openAnim - target: window - duration: units.longDuration - easing.type: Easing.InOutQuad - properties: "offset" - from: window.offset - to: contentArea.height - topEmptyAreaHeight - } - - Rectangle { - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: parent.height - headerHeight // don't layer on top panel indicators (area is darkened separately) - color: "black" - opacity: 0.6 * Math.min(1, offset/contentArea.height) - - Rectangle { - height: headerHeight - anchors { - left: parent.left - right: parent.right - bottom: parent.top - } - color: "black" - opacity: 0.2 - } - Rectangle { - height: units.smallSpacing - anchors { - left: parent.left - right: parent.right - top: parent.top - } - gradient: Gradient { - GradientStop { - position: 1.0 - color: Qt.rgba(0, 0, 0, 0.0) - } - GradientStop { - position: 0.5 - color: Qt.rgba(0, 0, 0, 0.4) - } - GradientStop { - position: 1.0 - color: "transparent" - } - } - } - } - PlasmaCore.ColorScope { - id: mainScope - anchors.fill: parent - - Flickable { - id: mainFlickable - anchors { - fill: parent - topMargin: headerHeight - } - Binding { - target: mainFlickable - property: "contentY" - value: -window.offset + contentArea.height - when: !mainFlickable.moving && !mainFlickable.dragging && !mainFlickable.flicking - } - //no loop as those 2 values compute to exactly the same - onContentYChanged: { - if (contentY === oldContentY) { - window.direction = SlidingPanel.MovementDirection.None; - } else { - window.direction = contentY > oldContentY ? SlidingPanel.MovementDirection.Up : SlidingPanel.MovementDirection.Down; - } - window.offset = -contentY + contentArea.height - oldContentY = contentY; - } - property real oldContentY - boundsMovement: Flickable.StopAtBounds - contentWidth: window.width - contentHeight: window.height*2 - headerHeight*2 - bottomMargin: window.height - onMovementStarted: { - window.cancelAnimations(); - window.userInteracting = true; - } - onFlickStarted: window.userInteracting = true; - onMovementEnded: { - window.userInteracting = false; - window.updateState(); - } - onFlickEnded: { - window.userInteracting = true; - window.updateState(); - } - MouseArea { - id: dismissArea - z: 2 - width: parent.width - height: mainFlickable.contentHeight - onClicked: window.close(); - PlasmaComponents.Control { - id: contentArea - z: 1 - y: 0 - x: drawerX - width: drawerWidth - } - } - } - } -} diff --git a/containments/panel/package/contents/ui/indicators/Battery.qml b/containments/panel/package/contents/ui/indicators/Battery.qml index 133fdc54..d870c239 100644 --- a/containments/panel/package/contents/ui/indicators/Battery.qml +++ b/containments/panel/package/contents/ui/indicators/Battery.qml @@ -1,4 +1,5 @@ /* + * SPDX-FileCopyrightText: 2021 Devin Lin * SPDX-FileCopyrightText: 2019 Marco Martin * * SPDX-License-Identifier: GPL-2.0-or-later @@ -11,31 +12,27 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.workspace.components 2.0 as PW +import "providers" RowLayout { - visible: pmSource.data["Battery"]["Has Cumulative"] + required property BatteryProvider provider + visible: provider.isVisible PW.BatteryIcon { id: battery Layout.preferredWidth: height Layout.fillHeight: true hasBattery: true - percent: pmSource.data["Battery"]["Percent"] - pluggedIn: pmSource.data["AC Adapter"] ? pmSource.data["AC Adapter"]["Plugged in"] : false + percent: provider.percent + pluggedIn: provider.pluggedIn height: batteryLabel.height width: batteryLabel.height - - PlasmaCore.DataSource { - id: pmSource - engine: "powermanagement" - connectedSources: ["Battery", "AC Adapter"] - } } PlasmaComponents.Label { id: batteryLabel - text: i18n("%1%", battery.percent) + text: i18n("%1%", provider.percent) Layout.alignment: Qt.AlignVCenter color: PlasmaCore.ColorScope.textColor diff --git a/containments/panel/package/contents/ui/indicators/Bluetooth.qml b/containments/panel/package/contents/ui/indicators/Bluetooth.qml index fc96c405..f79e2d1e 100644 --- a/containments/panel/package/contents/ui/indicators/Bluetooth.qml +++ b/containments/panel/package/contents/ui/indicators/Bluetooth.qml @@ -1,4 +1,5 @@ /* + SPDX-FileCopyrightText: 2021 Devin Lin SPDX-FileCopyrightText: 2013-2017 Jan Grulich @@ -11,38 +12,17 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.bluezqt 1.0 as BluezQt +import "providers" + PlasmaCore.IconItem { id: connectionIcon + required property BluetoothProvider provider - property bool deviceConnected : false - - source: deviceConnected ? "preferences-system-bluetooth-activated" : "preferences-system-bluetooth"; + source: provider.icon; colorGroup: PlasmaCore.ColorScope.colorGroup - visible: BluezQt.Manager.bluetoothOperational + visible: provider.isVisible Layout.fillHeight: true Layout.preferredWidth: height - function updateStatus() - { - var connectedDevices = []; - - for (var i = 0; i < BluezQt.Manager.devices.length; ++i) { - var device = BluezQt.Manager.devices[i]; - if (device.connected) { - connectedDevices.push(device); - } - } - deviceConnected = connectedDevices.length > 0; - } - - Component.onCompleted: { - BluezQt.Manager.deviceAdded.connect(updateStatus); - BluezQt.Manager.deviceRemoved.connect(updateStatus); - BluezQt.Manager.deviceChanged.connect(updateStatus); - BluezQt.Manager.bluetoothBlockedChanged.connect(updateStatus); - BluezQt.Manager.bluetoothOperationalChanged.connect(updateStatus); - - updateStatus(); - } } diff --git a/containments/panel/package/contents/ui/indicators/SignalStrength.qml b/containments/panel/package/contents/ui/indicators/SignalStrength.qml index c0d01d8c..6c14db0d 100644 --- a/containments/panel/package/contents/ui/indicators/SignalStrength.qml +++ b/containments/panel/package/contents/ui/indicators/SignalStrength.qml @@ -1,4 +1,5 @@ /* + * SPDX-FileCopyrightText: 2021 Devin Lin * SPDX-FileCopyrightText: 2015 Marco Martin * * SPDX-License-Identifier: GPL-2.0-or-later @@ -7,34 +8,19 @@ import QtQuick 2.1 import QtQuick.Layouts 1.1 -import MeeGo.QOfono 0.2 - import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +import "providers" + Item { + // we can't use SignalStrengthProvider as the var type as it would import ofono (which may cause it to break if ofono is not installed) + required property QtObject provider + width: strengthIcon.height + strengthLabel.width Layout.minimumWidth: strengthIcon.height + strengthLabel.width - OfonoManager { - id: ofonoManager - } - - OfonoNetworkRegistration { - id: netreg - Component.onCompleted: { - netreg.scan() - } - - modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" - } - - OfonoSimManager { - id: simManager - modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" - } - PlasmaCore.IconItem { id: strengthIcon colorGroup: PlasmaCore.ColorScope.colorGroup @@ -43,12 +29,7 @@ Item { width: height height: parent.height - source: netreg.strength == 100 ? "network-mobile-100" - : netreg.strength >= 80 ? "network-mobile-80" - : netreg.strength >= 60 ? "network-mobile-60" - : netreg.strength >= 40 ? "network-mobile-40" - : netreg.strength >= 20 ? "network-mobile-20" - : "network-mobile-0" + source: provider.icon } PlasmaComponents.Label { @@ -56,7 +37,7 @@ Item { anchors.left: strengthIcon.right anchors.verticalCenter: parent.verticalCenter - text: simManager.pinRequired !== OfonoSimManager.NoPin ? i18n("Sim locked") : netreg.name + text: provider.label color: PlasmaCore.ColorScope.textColor font.pixelSize: parent.height / 2 } diff --git a/containments/panel/package/contents/ui/indicators/Volume.qml b/containments/panel/package/contents/ui/indicators/Volume.qml index b2bed2ec..23d6aa6f 100644 --- a/containments/panel/package/contents/ui/indicators/Volume.qml +++ b/containments/panel/package/contents/ui/indicators/Volume.qml @@ -1,4 +1,5 @@ /* + SPDX-FileCopyrightText: 2021 Devin Lin SPDX-FileCopyrightText: 2019 Aditya Mehra SPDX-FileCopyrightText: 2014-2015 Harald Sitter @@ -11,152 +12,17 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.private.volume 0.1 -PlasmaCore.IconItem { +import "providers" +PlasmaCore.IconItem { id: paIcon + required property VolumeProvider provider + Layout.fillHeight: true Layout.preferredWidth: height - property bool volumeFeedback: true - property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0) - property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0) - readonly property string dummyOutputName: "auto_null" - source: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink) - ? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted) - : iconName(0, true) + source: provider.icon colorGroup: PlasmaCore.ColorScope.colorGroup - visible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted - - function iconName(volume, muted, prefix) { - if (!prefix) { - prefix = "audio-volume"; - } - var icon = null; - var percent = volume / maxVolumeValue; - if (percent <= 0.0 || muted) { - icon = prefix + "-muted"; - } else if (percent <= 0.25) { - icon = prefix + "-low"; - } else if (percent <= 0.75) { - icon = prefix + "-medium"; - } else { - icon = prefix + "-high"; - } - return icon; - } - - function isDummyOutput(output) { - return output && output.name === dummyOutputName; - } - - function boundVolume(volume) { - return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue)); - } - - function volumePercent(volume, max){ - if(!max) { - max = PulseAudio.NormalVolume; - } - return Math.round(volume / max * 100.0); - } - - function playFeedback(sinkIndex) { - if(!volumeFeedback){ - return; - } - if(sinkIndex == undefined) { - sinkIndex = paSinkModel.preferredSink.index; - } - feedback.play(sinkIndex) - } - - function increaseVolume() { - if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { - return; - } - - var volume = boundVolume(paSinkModel.preferredSink.volume + volumeStep); - var percent = volumePercent(volume, maxVolumeValue); - paSinkModel.preferredSink.muted = percent == 0; - paSinkModel.preferredSink.volume = volume; - osd.show(percent); - playFeedback(); - - } - - function decreaseVolume() { - if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { - return; - } - - var volume = boundVolume(paSinkModel.preferredSink.volume - volumeStep); - var percent = volumePercent(volume, maxVolumeValue); - paSinkModel.preferredSink.muted = percent == 0; - paSinkModel.preferredSink.volume = volume; - osd.show(percent); - playFeedback(); - } - - - - function muteVolume() { - if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { - return; - } - - var toMute = !paSinkModel.preferredSink.muted; - paSinkModel.preferredSink.muted = toMute; - osd.show(toMute ? 0 : volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue)); - if (!toMute) { - playFeedback(); - } - } - - SinkModel { - id: paSinkModel - } - - VolumeOSD { - id: osd - } - - VolumeFeedback { - id: feedback - } - - GlobalActionCollection { - // KGlobalAccel cannot transition from kmix to something else, so if - // the user had a custom shortcut set for kmix those would get lost. - // To avoid this we hijack kmix name and actions. Entirely mental but - // best we can do to not cause annoyance for the user. - // The display name actually is updated to whatever registered last - // though, so as far as user visible strings go we should be fine. - // As of 2015-07-21: - // componentName: kmix - // actions: increase_volume, decrease_volume, mute - name: "kmix" - displayName: i18n("Audio") - - GlobalAction { - objectName: "increase_volume" - text: i18n("Increase Volume") - shortcut: Qt.Key_VolumeUp - onTriggered: increaseVolume() - } - - GlobalAction { - objectName: "decrease_volume" - text: i18n("Decrease Volume") - shortcut: Qt.Key_VolumeDown - onTriggered: decreaseVolume() - } - - GlobalAction { - objectName: "mute" - text: i18n("Mute") - shortcut: Qt.Key_VolumeMute - onTriggered: muteVolume() - } - } + visible: provider.isVisible } diff --git a/containments/panel/package/contents/ui/indicators/Wifi.qml b/containments/panel/package/contents/ui/indicators/Wifi.qml index c69217fb..4e49f2ff 100644 --- a/containments/panel/package/contents/ui/indicators/Wifi.qml +++ b/containments/panel/package/contents/ui/indicators/Wifi.qml @@ -1,4 +1,5 @@ /* + SPDX-FileCopyrightText: 2021 Devin Lin SPDX-FileCopyrightText: 2019 Marco Martin SPDX-FileCopyrightText: 2013-2017 Jan Grulich @@ -11,10 +12,13 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.networkmanagement 0.2 as PlasmaNM +import "providers" + PlasmaCore.IconItem { id: connectionIcon + required property WifiProvider provider - source: connectionIconProvider.connectionIcon + source: provider.icon colorGroup: PlasmaCore.ColorScope.colorGroup Layout.fillHeight: true @@ -24,23 +28,7 @@ PlasmaCore.IconItem { id: connectingIndicator anchors.fill: parent - running: connectionIconProvider.connecting + running: provider.indicatorRunning visible: running } - - PlasmaNM.NetworkStatus { - id: networkStatus - } - - PlasmaNM.NetworkModel { - id: connectionModel - } - - PlasmaNM.Handler { - id: handler - } - - PlasmaNM.ConnectionIcon { - id: connectionIconProvider - } } diff --git a/containments/panel/package/contents/ui/indicators/providers/BatteryProvider.qml b/containments/panel/package/contents/ui/indicators/providers/BatteryProvider.qml new file mode 100644 index 00000000..92511efa --- /dev/null +++ b/containments/panel/package/contents/ui/indicators/providers/BatteryProvider.qml @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2021 Devin Lin + * SPDX-FileCopyrightText: 2019 Marco Martin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.4 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PlasmaComponents +import org.kde.plasma.workspace.components 2.0 as PW + +Item { + property bool isVisible: pmSource.data["Battery"]["Has Cumulative"] + property int percent: pmSource.data["Battery"]["Percent"] + property bool pluggedIn: pmSource.data["AC Adapter"] ? pmSource.data["AC Adapter"]["Plugged in"] : false + + property PlasmaCore.DataSource pmSource: PlasmaCore.DataSource { + engine: "powermanagement" + connectedSources: ["Battery", "AC Adapter"] + } +} + diff --git a/containments/panel/package/contents/ui/indicators/providers/BluetoothProvider.qml b/containments/panel/package/contents/ui/indicators/providers/BluetoothProvider.qml new file mode 100644 index 00000000..490154b4 --- /dev/null +++ b/containments/panel/package/contents/ui/indicators/providers/BluetoothProvider.qml @@ -0,0 +1,43 @@ +/* + SPDX-FileCopyrightText: 2021 Devin Lin + SPDX-FileCopyrightText: 2019 Marco Martin + SPDX-FileCopyrightText: 2013-2017 Jan Grulich + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.4 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.bluezqt 1.0 as BluezQt + +QtObject { + property bool isVisible: BluezQt.Manager.bluetoothOperational + property string icon: deviceConnected ? "preferences-system-bluetooth-activated" : "preferences-system-bluetooth" + + property bool deviceConnected : false + + function updateStatus() { + var connectedDevices = []; + + for (var i = 0; i < BluezQt.Manager.devices.length; ++i) { + var device = BluezQt.Manager.devices[i]; + if (device.connected) { + connectedDevices.push(device); + } + } + deviceConnected = connectedDevices.length > 0; + } + + Component.onCompleted: { + BluezQt.Manager.deviceAdded.connect(updateStatus); + BluezQt.Manager.deviceRemoved.connect(updateStatus); + BluezQt.Manager.deviceChanged.connect(updateStatus); + BluezQt.Manager.bluetoothBlockedChanged.connect(updateStatus); + BluezQt.Manager.bluetoothOperationalChanged.connect(updateStatus); + + updateStatus(); + } +} + diff --git a/containments/panel/package/contents/ui/indicators/providers/SignalStrengthProvider.qml b/containments/panel/package/contents/ui/indicators/providers/SignalStrengthProvider.qml new file mode 100644 index 00000000..6d6dc1f2 --- /dev/null +++ b/containments/panel/package/contents/ui/indicators/providers/SignalStrengthProvider.qml @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2021 Devin Lin + * SPDX-FileCopyrightText: 2015 Marco Martin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 + +import MeeGo.QOfono 0.2 + +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +QtObject { + property string icon: netreg.strength == 100 ? "network-mobile-100" + : netreg.strength >= 80 ? "network-mobile-80" + : netreg.strength >= 60 ? "network-mobile-60" + : netreg.strength >= 40 ? "network-mobile-40" + : netreg.strength >= 20 ? "network-mobile-20" + : "network-mobile-0" + + property string label: simManager.pinRequired !== OfonoSimManager.NoPin ? i18n("Sim locked") : netreg.name + + property OfonoManager ofonoManager: OfonoManager {} + + property OfonoNetworkRegistration netreg: OfonoNetworkRegistration { + Component.onCompleted: { + netreg.scan() + } + + modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" + } + + property OfonoSimManager simManager: OfonoSimManager { + modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" + } +} + diff --git a/containments/panel/package/contents/ui/indicators/providers/VolumeProvider.qml b/containments/panel/package/contents/ui/indicators/providers/VolumeProvider.qml new file mode 100644 index 00000000..47c7871c --- /dev/null +++ b/containments/panel/package/contents/ui/indicators/providers/VolumeProvider.qml @@ -0,0 +1,151 @@ +/* + SPDX-FileCopyrightText: 2021 Devin Lin + SPDX-FileCopyrightText: 2019 Aditya Mehra + SPDX-FileCopyrightText: 2014-2015 Harald Sitter + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.4 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.volume 0.1 + +QtObject { + property bool isVisible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted + property string icon: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink) + ? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted) + : iconName(0, true) + + property bool volumeFeedback: true + property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0) + property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0) + readonly property string dummyOutputName: "auto_null" + + function iconName(volume, muted, prefix) { + if (!prefix) { + prefix = "audio-volume"; + } + var icon = null; + var percent = volume / maxVolumeValue; + if (percent <= 0.0 || muted) { + icon = prefix + "-muted"; + } else if (percent <= 0.25) { + icon = prefix + "-low"; + } else if (percent <= 0.75) { + icon = prefix + "-medium"; + } else { + icon = prefix + "-high"; + } + return icon; + } + + function isDummyOutput(output) { + return output && output.name === dummyOutputName; + } + + function boundVolume(volume) { + return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue)); + } + + function volumePercent(volume, max){ + if(!max) { + max = PulseAudio.NormalVolume; + } + return Math.round(volume / max * 100.0); + } + + function playFeedback(sinkIndex) { + if(!volumeFeedback){ + return; + } + if(sinkIndex == undefined) { + sinkIndex = paSinkModel.preferredSink.index; + } + feedback.play(sinkIndex) + } + + function increaseVolume() { + if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { + return; + } + + var volume = boundVolume(paSinkModel.preferredSink.volume + volumeStep); + var percent = volumePercent(volume, maxVolumeValue); + paSinkModel.preferredSink.muted = percent == 0; + paSinkModel.preferredSink.volume = volume; + osd.show(percent); + playFeedback(); + + } + + function decreaseVolume() { + if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { + return; + } + + var volume = boundVolume(paSinkModel.preferredSink.volume - volumeStep); + var percent = volumePercent(volume, maxVolumeValue); + paSinkModel.preferredSink.muted = percent == 0; + paSinkModel.preferredSink.volume = volume; + osd.show(percent); + playFeedback(); + } + + + + function muteVolume() { + if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { + return; + } + + var toMute = !paSinkModel.preferredSink.muted; + paSinkModel.preferredSink.muted = toMute; + osd.show(toMute ? 0 : volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue)); + if (!toMute) { + playFeedback(); + } + } + + property SinkModel paSinkModel: SinkModel {} + + property VolumeOSD osd: VolumeOSD {} + + property VolumeFeedback feedback: VolumeFeedback {} + + property GlobalActionCollection actionCollection: GlobalActionCollection { + // KGlobalAccel cannot transition from kmix to something else, so if + // the user had a custom shortcut set for kmix those would get lost. + // To avoid this we hijack kmix name and actions. Entirely mental but + // best we can do to not cause annoyance for the user. + // The display name actually is updated to whatever registered last + // though, so as far as user visible strings go we should be fine. + // As of 2015-07-21: + // componentName: kmix + // actions: increase_volume, decrease_volume, mute + name: "kmix" + displayName: i18n("Audio") + + GlobalAction { + objectName: "increase_volume" + text: i18n("Increase Volume") + shortcut: Qt.Key_VolumeUp + onTriggered: increaseVolume() + } + + GlobalAction { + objectName: "decrease_volume" + text: i18n("Decrease Volume") + shortcut: Qt.Key_VolumeDown + onTriggered: decreaseVolume() + } + + GlobalAction { + objectName: "mute" + text: i18n("Mute") + shortcut: Qt.Key_VolumeMute + onTriggered: muteVolume() + } + } +} diff --git a/containments/panel/package/contents/ui/indicators/providers/WifiProvider.qml b/containments/panel/package/contents/ui/indicators/providers/WifiProvider.qml new file mode 100644 index 00000000..7cabc5ba --- /dev/null +++ b/containments/panel/package/contents/ui/indicators/providers/WifiProvider.qml @@ -0,0 +1,36 @@ +/* + SPDX-FileCopyrightText: 2021 Devin Lin + SPDX-FileCopyrightText: 2019 Marco Martin + SPDX-FileCopyrightText: 2013-2017 Jan Grulich + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.4 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.networkmanagement 0.2 as PlasmaNM + +Item { + visible: false + property string icon: connectionIconProvider.connectionIcon + property bool indicatorRunning: connectionIconProvider.connecting + + PlasmaNM.NetworkStatus { + id: networkStatus + } + + PlasmaNM.NetworkModel { + id: connectionModel + } + + PlasmaNM.Handler { + id: handler + } + + PlasmaNM.ConnectionIcon { + id: connectionIconProvider + } +} + diff --git a/containments/panel/package/contents/ui/main.qml b/containments/panel/package/contents/ui/main.qml index c542df14..971e0b9b 100644 --- a/containments/panel/package/contents/ui/main.qml +++ b/containments/panel/package/contents/ui/main.qml @@ -1,4 +1,5 @@ /* + * SPDX-FileCopyrightText: 2021 Devin Lin * SPDX-FileCopyrightText: 2015 Marco Martin * * SPDX-License-Identifier: GPL-2.0-or-later @@ -9,6 +10,8 @@ import QtQuick.Layouts 1.3 import QtQml.Models 2.12 import QtGraphicalEffects 1.12 +import org.kde.kirigami 2.12 as Kirigami + import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PlasmaComponents @@ -23,7 +26,7 @@ import "LayoutManager.js" as LayoutManager import "quicksettings" import "indicators" as Indicators - +import "indicators/providers" as IndicatorProviders Item { id: root @@ -37,7 +40,7 @@ Item { property bool reorderingApps: false property var layoutManager: LayoutManager - readonly property color backgroundColor: NanoShell.StartupFeedback.visible ? NanoShell.StartupFeedback.backgroundColor : icons.backgroundColor + readonly property color backgroundColor: NanoShell.StartupFeedback.visible ? NanoShell.StartupFeedback.backgroundColor : topPanel.colorScopeColor readonly property bool showingApp: !MobileShell.HomeScreenControls.homeScreenVisible readonly property bool hasTasks: tasksModel.count > 0 @@ -48,7 +51,7 @@ Item { } function addApplet(applet, x, y) { - var compactContainer = compactContainerComponent.createObject(appletIconsRow) + var compactContainer = compactContainerComponent.createObject(topPanel.applets) print("Applet added: " + applet + " " + applet.title) applet.parent = compactContainer; @@ -115,138 +118,59 @@ Item { property Item applet visible: applet && (applet.status != PlasmaCore.Types.HiddenStatus && applet.status != PlasmaCore.Types.PassiveStatus) Layout.fillHeight: true - Layout.minimumWidth: applet && applet.compactRepresentationItem ? Math.max(applet.compactRepresentationItem.Layout.minimumWidth, appletIconsRow.height) : appletIconsRow.height + Layout.minimumWidth: applet && applet.compactRepresentationItem ? Math.max(applet.compactRepresentationItem.Layout.minimumWidth, topPanel.applets.height) : topPanel.applets.height Layout.maximumWidth: Layout.minimumWidth } } Component { id: fullContainerComponent - FullContainer { - } + FullContainer {} } Component { id: fullNotificationsContainerComponent - FullNotificationsContainer { - } + FullNotificationsContainer {} } - PlasmaCore.DataSource { - id: timeSource - engine: "time" - connectedSources: ["Local"] - interval: 60 * 1000 + // indicator providers + IndicatorProviders.BatteryProvider { + id: batteryProvider } - - DropShadow { - anchors.fill: icons - visible: !showingApp - cached: true - horizontalOffset: 0 - verticalOffset: 1 - radius: 4.0 - samples: 17 - color: Qt.rgba(0,0,0,0.8) - source: icons + IndicatorProviders.BluetoothProvider { + id: bluetoothProvider } - - // screen top panel - PlasmaCore.ColorScope { - id: icons - z: 1 - colorGroup: showingApp ? PlasmaCore.Theme.HeaderColorGroup : PlasmaCore.Theme.ComplementaryColorGroup - //parent: slidingPanel.visible && !slidingPanel.wideScreen ? panelContents : root - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: root.height - Rectangle { - anchors.fill: parent - gradient: Gradient { - GradientStop { - position: 1.0 - color: showingApp ? root.backgroundColor : "transparent" - } - GradientStop { - position: 0.0 - color: showingApp ? root.backgroundColor : Qt.rgba(0, 0, 0, 0.1) - } - } - } - - Loader { - id: strengthLoader - height: parent.height - width: item ? item.width : 0 - source: Qt.resolvedUrl("indicators/SignalStrength.qml") - } - - Row { - id: sniRow - anchors.left: strengthLoader.right - height: parent.height - Repeater { - id: statusNotifierRepeater - model: PlasmaCore.SortFilterModel { - id: filteredStatusNotifiers - filterRole: "Title" - sourceModel: PlasmaCore.DataModel { - dataSource: statusNotifierSource - } - } - - delegate: TaskWidget { - } - } - } - - PlasmaComponents.Label { - id: clock - property bool is24HourTime: plasmoid.nativeInterface.isSystem24HourFormat - - anchors.fill: parent - text: Qt.formatTime(timeSource.data.Local.DateTime, is24HourTime ? "h:mm" : "h:mm ap") - color: PlasmaCore.ColorScope.textColor - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - font.pixelSize: height / 2 - } - - RowLayout { - id: appletIconsRow - anchors { - bottom: parent.bottom - right: simpleIndicatorsLayout.left - } - height: parent.height - } - - //TODO: pluggable - RowLayout { - id: simpleIndicatorsLayout - anchors { - top: parent.top - bottom: parent.bottom - right: parent.right - rightMargin: units.smallSpacing - } - Indicators.Bluetooth {} - Indicators.Wifi {} - Indicators.Volume {} - Indicators.Battery {} - } + property alias signalStrengthProvider: signalStrengthProviderLoader.item + Loader { + id: signalStrengthProviderLoader + source: Qt.resolvedUrl("indicators/providers/SignalStrengthProvider.qml") + } + IndicatorProviders.VolumeProvider { + id: volumeProvider + } + IndicatorProviders.WifiProvider { + id: wifiProvider } - // screen top panel background (background for the rest of the screen in SlidingPanel.qml) + // top panel component + IndicatorsRow { + id: topPanel + anchors.fill: parent + z: 1 + colorGroup: showingApp ? PlasmaCore.Theme.HeaderColorGroup : PlasmaCore.Theme.ComplementaryColorGroup + backgroundColor: !showingApp ? "transparent" : root.backgroundColor + showGradientBackground: !showingApp + showDropShadow: !showingApp + } + + // top panel background (background for the rest of the screen in SlidingPanel.qml) Rectangle { anchors.fill: parent - color: "black" + color: PlasmaCore.Theme.backgroundColor opacity: 0.6 * Math.min(1, slidingPanel.offset/panelContents.height) } + // initial swipe down MouseArea { z: 99 property int oldMouseY: 0 @@ -256,13 +180,12 @@ Item { slidingPanel.cancelAnimations(); slidingPanel.drawerX = Math.min(Math.max(0, mouse.x - slidingPanel.drawerWidth/2), slidingPanel.width - slidingPanel.contentItem.width) slidingPanel.userInteracting = true; + slidingPanel.flickable.contentY = slidingPanel.closedContentY; oldMouseY = mouse.y; - slidingPanel.offset = 0//units.gridUnit * 2; slidingPanel.showFullScreen(); } onPositionChanged: { - slidingPanel.offset = Math.min(slidingPanel.contentItem.height, slidingPanel.offset + (mouse.y - oldMouseY)); - + slidingPanel.updateOffset(mouse.y - oldMouseY); oldMouseY = mouse.y; } onReleased: { @@ -275,61 +198,65 @@ Item { } } - SlidingPanel { + // sliding component + SlidingContainer { id: slidingPanel width: plasmoid.availableScreenRect.width height: plasmoid.availableScreenRect.height - openThreshold: units.gridUnit * 2 - headerHeight: root.height + topPanelHeight: topPanel.height topEmptyAreaHeight: quickSettings.topEmptyAreaHeight - + collapsedHeight: quickSettings.collapsedHeight + fullyOpenHeight: quickSettings.implicitHeight + + appletsShown: fullRepresentationView.count > 0 + offset: quickSettings.height onClosed: quickSettings.closed() - contentItem: Item { - implicitWidth: panelContents.implicitWidth - + contentItem: MouseArea { + // mousearea captures touch presses so that the flickable picks them up for swiping + implicitWidth: slidingPanel.width implicitHeight: Math.min(slidingPanel.height, quickSettings.implicitHeight) GridLayout { id: panelContents anchors.fill: parent - implicitWidth: quickSettings.implicitWidth - implicitHeight: Math.min(slidingPanel.height, quickSettings.implicitHeight) - + columns: slidingPanel.wideScreen ? 2 : 1 rows: slidingPanel.wideScreen ? 1 : 2 - QuickSettings { + QuickSettingsPanel { id: quickSettings + property int trueHeight: height + Math.round(Kirigami.Units.gridUnit * 1.5) // add height of bottom bar + z: 4 Layout.alignment: Qt.AlignTop Layout.preferredWidth: slidingPanel.wideScreen ? Math.min(slidingPanel.width/2, units.gridUnit * 25) : panelContents.width - z: 4 parentSlidingPanel: slidingPanel - onCloseRequested: { - slidingPanel.hide() - } - background: DrawerBackground {} + onCloseRequested: slidingPanel.hide() } + // notifications ListView { id: fullRepresentationView - z: 1 - interactive: width < contentWidth - //parent: slidingPanel.wideScreen ? slidingPanel.flickable.contentItem : panelContents - + implicitHeight: units.gridUnit * 20 + Layout.topMargin: slidingPanel.wideScreen ? 0 : Math.round(Kirigami.Units.gridUnit * 1.5) // add height of bottom bar Layout.preferredWidth: slidingPanel.wideScreen ? Math.min(slidingPanel.width/2, quickSettings.width*fullRepresentationModel.count) : panelContents.width + Layout.preferredHeight: Math.min(plasmoid.screenGeometry.height - quickSettings.implicitHeight - bottomBar.height + slidingPanel.topEmptyAreaHeight, implicitHeight) + z: 1 + interactive: count > 0 && width < contentWidth - //Layout.fillWidth: true clip: slidingPanel.wideScreen - y: slidingPanel.wideScreen ? 0 : quickSettings.height - (quickSettings.height + height) * (1-opacity) - opacity: slidingPanel.wideScreen ? 1 : fullRepresentationModel.count > 0 && (slidingPanel.offset + slidingPanel.headerHeight)/(quickSettings.height -slidingPanel.topEmptyAreaHeight) - Layout.preferredHeight: Math.min(plasmoid.screenGeometry.height - slidingPanel.headerHeight - quickSettings.height - bottomBar.height + slidingPanel.topEmptyAreaHeight, implicitHeight) - //leftMargin: slidingPanel.drawerX + y: slidingPanel.wideScreen ? 0 : quickSettings.trueHeight + opacity: { + if (slidingPanel.wideScreen) { + return 1; + } else { + return fullRepresentationModel.count > 0 && slidingPanel.offset / slidingPanel.collapsedHeight; + } + } preferredHighlightBegin: slidingPanel.drawerX - implicitHeight: units.gridUnit * 20 cacheBuffer: width * 100 highlightFollowsCurrentItem: true highlightRangeMode: ListView.StrictlyEnforceRange @@ -346,10 +273,6 @@ Item { z: -1 onClicked: slidingPanel.close() } - - //implicitHeight: fullRepresentationLayout.implicitHeight - //clip: true - } } } @@ -360,11 +283,11 @@ Item { right: parent.right bottom: parent.bottom } + backgroundColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.backgroundColor, {"alpha": 0.8*255}) parent: slidingPanel.fixedArea opacity: fullRepresentationView.opacity visible: !slidingPanel.wideScreen && fullRepresentationModel.count > 1 - //height: 40 z: 100 contentItem: RowLayout { PlasmaComponents.TabBar { diff --git a/containments/panel/package/contents/ui/quicksettings/BrightnessItem.qml b/containments/panel/package/contents/ui/quicksettings/BrightnessItem.qml index c33da4f7..15d49f3b 100644 --- a/containments/panel/package/contents/ui/quicksettings/BrightnessItem.qml +++ b/containments/panel/package/contents/ui/quicksettings/BrightnessItem.qml @@ -9,40 +9,89 @@ import QtQuick 2.0 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.1 +import org.kde.kirigami 2.12 as Kirigami import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PC3 -RowLayout { +Item { id: brightnessRoot - property alias value: brightnessSlider.value - property alias maximumValue: brightnessSlider.to - - signal moved - - spacing: units.smallSpacing - - PlasmaCore.IconItem { - Layout.alignment: Qt.AlignVCenter - Layout.leftMargin: Kirigami.Units.largeSpacing - Layout.preferredWidth: units.iconSizes.medium - Layout.preferredHeight: width - source: "low-brightness" - } - - Slider { - id: brightnessSlider - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - onMoved: brightnessRoot.moved() - from: 1 + + implicitHeight: brightnessRow.implicitHeight + + property int screenBrightness + property bool disableBrightnessUpdate: true + readonly property int maximumScreenBrightness: pmSource.data["PowerDevil"] ? pmSource.data["PowerDevil"]["Maximum Screen Brightness"] || 0 : 0 + + onScreenBrightnessChanged: { + brightnessSlider.value = brightnessRoot.screenBrightness + if (!disableBrightnessUpdate) { + var service = pmSource.serviceForSource("PowerDevil"); + var operation = service.operationDescription("setBrightness"); + operation.brightness = screenBrightness; + operation.silent = true + service.startOperationCall(operation); + } } - PlasmaCore.IconItem { - Layout.alignment: Qt.AlignVCenter - Layout.rightMargin: Kirigami.Units.largeSpacing - Layout.preferredWidth: units.iconSizes.medium - Layout.preferredHeight: width - source: "high-brightness" + PlasmaCore.DataSource { + id: pmSource + engine: "powermanagement" + connectedSources: ["PowerDevil"] + onSourceAdded: { + if (source === "PowerDevil") { + disconnectSource(source); + connectSource(source); + } + } + onDataChanged: { + disableBrightnessUpdate = true; + brightnessRoot.screenBrightness = pmSource.data["PowerDevil"]["Screen Brightness"]; + disableBrightnessUpdate = false; + } + } + + Component.onCompleted: { + brightnessSlider.moved.connect(function() { + brightnessRoot.screenBrightness = brightnessSlider.value; + }); + disableBrightnessUpdate = false; + } + + RowLayout { + id: brightnessRow + spacing: PlasmaCore.Units.smallSpacing * 2 + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + Kirigami.Icon { + color: PlasmaCore.Theme.textColor + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: PlasmaCore.Units.smallSpacing + Layout.preferredWidth: Math.round(PlasmaCore.Units.gridUnit * 1.75) + Layout.preferredHeight: width + source: "low-brightness" + } + + Slider { + id: brightnessSlider + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + value: screenBrightness + from: 1 + to: maximumScreenBrightness + } + + Kirigami.Icon { + color: PlasmaCore.Theme.textColor + isMask: true + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: PlasmaCore.Units.smallSpacing + Layout.preferredWidth: Math.round(PlasmaCore.Units.gridUnit * 1.75) + Layout.preferredHeight: width + source: "high-brightness" + } } } diff --git a/containments/panel/package/contents/ui/quicksettings/Delegate.qml b/containments/panel/package/contents/ui/quicksettings/Delegate.qml index 9872eb8b..df359793 100644 --- a/containments/panel/package/contents/ui/quicksettings/Delegate.qml +++ b/containments/panel/package/contents/ui/quicksettings/Delegate.qml @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2015 Marco Martin + * SPDX-FileCopyrightText: 2021 Devin Lin * * SPDX-License-Identifier: LGPL-2.0-or-later */ @@ -14,6 +15,9 @@ import org.kde.plasma.private.nanoshell 2.0 as NanoShell ColumnLayout { id: delegateRoot spacing: units.smallSpacing + + required property var settingsModel + signal closeRequested signal panelClosed @@ -24,67 +28,68 @@ ColumnLayout { required property string settingsCommand required property var toggleFunction property alias labelOpacity: label.opacity + + property color disabledButtonColor: PlasmaCore.Theme.backgroundColor + property color disabledPressedButtonColor: Qt.darker(disabledButtonColor, 1.1) + property color enabledButtonColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {"alpha": 0.4*255}) + property color enabledPressedButtonColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {"alpha": 0.6*255}); Rectangle { - Layout.preferredWidth: units.iconSizes.large + units.smallSpacing + Layout.preferredWidth: PlasmaCore.Units.iconSizes.large + PlasmaCore.Units.smallSpacing Layout.minimumHeight: width Layout.alignment: Qt.AlignHCenter - radius: units.smallSpacing + radius: PlasmaCore.Units.smallSpacing border.color: delegateRoot.enabled ? Qt.darker(Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {}), 1.25) : Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.textColor, {"alpha": 0.2*255}) color: { if (delegateRoot.enabled) { - return Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {"alpha": iconMouseArea.pressed ? 0.5*255 : 0.3*255}); + return iconMouseArea.pressed ? enabledPressedButtonColor : enabledButtonColor } else { - if (iconMouseArea.pressed) { - return Qt.darker(Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.backgroundColor, {"alpha": 0.9*255}), 1.25); - } else { - return Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.backgroundColor, {"alpha": 0.3*255}); - } + return iconMouseArea.pressed ? disabledPressedButtonColor : disabledButtonColor } } - PlasmaCore.IconItem { + Kirigami.Icon { id: icon - colorGroup: PlasmaCore.ColorScope.colorGroup - anchors { - fill: parent - margins: units.smallSpacing - } + color: PlasmaCore.Theme.textColor + anchors.centerIn: parent + implicitWidth: Math.round(parent.width * 0.6) + implicitHeight: width source: delegateRoot.icon - MouseArea { - id: iconMouseArea - anchors.fill: parent - onClicked: { - if (delegateRoot.toggle) { - delegateRoot.toggle(); - } else if (delegateRoot.toggleFunction) { - root[delegateRoot.toggleFunction](); - } else if (delegateRoot.settingsCommand) { - NanoShell.StartupFeedback.open( - delegateRoot.icon, - delegateRoot.text, - icon.Kirigami.ScenePosition.x + icon.width/2, - icon.Kirigami.ScenePosition.y + icon.height/2, - Math.min(icon.width, icon.height)) - plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand); - root.closeRequested(); - } + } + + MouseArea { + id: iconMouseArea + anchors.fill: parent + onClicked: { + if (delegateRoot.toggle) { + delegateRoot.toggle(); + } else if (delegateRoot.toggleFunction) { + settingsModel[delegateRoot.toggleFunction](); + } else if (delegateRoot.settingsCommand) { + NanoShell.StartupFeedback.open( + delegateRoot.icon, + delegateRoot.text, + icon.Kirigami.ScenePosition.x + icon.width/2, + icon.Kirigami.ScenePosition.y + icon.height/2, + Math.min(icon.width, icon.height)) + plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand); + root.closeRequested(); } - onPressAndHold: { - if (delegateRoot.settingsCommand) { - NanoShell.StartupFeedback.open( - delegateRoot.icon, - delegateRoot.text, - icon.Kirigami.ScenePosition.x + icon.width/2, - icon.Kirigami.ScenePosition.y + icon.height/2, - Math.min(icon.width, icon.height)) - closeRequested(); - plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand); - } else if (delegateRoot.toggleFunction) { - root[delegateRoot.toggleFunction](); - } + } + onPressAndHold: { + if (delegateRoot.settingsCommand) { + NanoShell.StartupFeedback.open( + delegateRoot.icon, + delegateRoot.text, + icon.Kirigami.ScenePosition.x + icon.width/2, + icon.Kirigami.ScenePosition.y + icon.height/2, + Math.min(icon.width, icon.height)) + closeRequested(); + plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand); + } else if (delegateRoot.toggleFunction) { + root[delegateRoot.toggleFunction](); } } } @@ -131,7 +136,7 @@ ColumnLayout { plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand); closeRequested(); } else if (delegateRoot.toggleFunction) { - root[delegateRoot.toggleFunction](); + settingsModel[delegateRoot.toggleFunction](); } } } diff --git a/containments/panel/package/contents/ui/quicksettings/QuickSettings.qml b/containments/panel/package/contents/ui/quicksettings/QuickSettings.qml deleted file mode 100644 index ff1a31d5..00000000 --- a/containments/panel/package/contents/ui/quicksettings/QuickSettings.qml +++ /dev/null @@ -1,385 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015 Marco Martin - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -import QtQuick 2.14 -import QtQuick.Layouts 1.1 -import QtQuick.Window 2.2 -import org.kde.plasma.core 2.0 as PlasmaCore -import org.kde.plasma.components 2.0 as PlasmaComponents -import org.kde.plasma.networkmanagement 0.2 as PlasmaNM -import org.kde.bluezqt 1.0 as BluezQt -import org.kde.colorcorrect 0.1 as CC -import org.kde.plasma.private.nanoshell 2.0 as NanoShell - -import org.kde.plasma.components 3.0 as PC3 - -Item { - id: root - - implicitWidth: column.implicitWidth + units.smallSpacing * 6 - implicitHeight: column.implicitHeight + units.smallSpacing * 2 - - signal closeRequested - signal closed - - property bool expandedMode: parentSlidingPanel.wideScreen - readonly property real expandedRatio: expandedMode ? 1 : Math.max(0, Math.min(1, (parentSlidingPanel.offset - firstRowHeight - parentSlidingPanel.headerHeight) / otherRowsHeight)) - - readonly property real topEmptyAreaHeight: parentSlidingPanel.userInteracting - ? (root.height - collapsedHeight) * (1 - expandedRatio) - : (expandedMode ? 0 : root.height - collapsedHeight) - - readonly property real collapsedHeight: firstRowHeight + PlasmaCore.Units.smallSpacing * 2 - readonly property real firstRowHeight: flow.children[0].height - readonly property real otherRowsHeight: column.height - firstRowHeight - - Connections { - target: root.parentSlidingPanel - function onUserInteractingChanged() { - if (!parentSlidingPanel.userInteracting) { - if (root.expandedRatio > 0.7) { - root.expandedMode = true; - } - } - } - } - - property bool screenshotRequested: false - - property NanoShell.FullScreenOverlay parentSlidingPanel - property Item background - onBackgroundChanged: { - background.parent = backgroundParent - background.anchors.fill = backgroundParent - } - - PlasmaNM.Handler { - id: nmHandler - } - - PlasmaNM.EnabledConnections { - id: enabledConnections - } - - Connections { - target: root.Window.window - function onVisibilityChanged() { - root.expandedMode = parentSlidingPanel.wideScreen; - - } - } - - Connections { - target: BluezQt.Manager - - function onBluetoothOperationalChanged() { - settingsModel.get(2).enabled = BluezQt.Manager.bluetoothOperational - } - } - - function toggleAirplane() { - print("toggle airplane mode") - } - - function toggleTorch() { - plasmoid.nativeInterface.toggleTorch() - settingsModel.get(6).enabled = plasmoid.nativeInterface.torchEnabled - } - - function toggleWifi() { - nmHandler.enableWireless(!enabledConnections.wirelessEnabled) - settingsModel.get(1).enabled = !enabledConnections.wirelessEnabled - } - - function toggleWwan() { - nmHandler.enableWwan(!enabledConnections.wwanEnabled) - settingsModel.get(3).enabled = !enabledConnections.wwanEnabled - } - - function toggleRotation() { - const enable = !plasmoid.nativeInterface.autoRotateEnabled - plasmoid.nativeInterface.autoRotateEnabled = enable - settingsModel.get(9).enabled = enable - } - - function toggleBluetooth() { - var enable = !BluezQt.Manager.bluetoothOperational; - BluezQt.Manager.bluetoothBlocked = !enable; - - for (var i = 0; i < BluezQt.Manager.adapters.length; ++i) { - var adapter = BluezQt.Manager.adapters[i]; - adapter.powered = enable; - } - } - - function toggleNightColor() { - if (compositorAdaptor.active) { - compositorAdaptor.activeStaged = false; - } else { - compositorAdaptor.activeStaged = true; - compositorAdaptor.modeStaged = 3; // always on - } - compositorAdaptor.sendConfigurationAll(); - settingsModel.get(10).enabled = compositorAdaptor.active; - } - - function requestShutdown() { - print("Shutdown requested, depends on ksmserver running"); - var service = pmSource.serviceForSource("PowerDevil"); - //note the strange camelCasing is intentional - var operation = service.operationDescription("requestShutDown"); - return service.startOperationCall(operation); - } - - signal plasmoidTriggered(var applet, var id) - Layout.minimumHeight: flow.implicitHeight + units.largeSpacing*2 - - property int screenBrightness - property bool disableBrightnessUpdate: true - readonly property int maximumScreenBrightness: pmSource.data["PowerDevil"] ? pmSource.data["PowerDevil"]["Maximum Screen Brightness"] || 0 : 0 - - onScreenBrightnessChanged: { - if(!disableBrightnessUpdate) { - var service = pmSource.serviceForSource("PowerDevil"); - var operation = service.operationDescription("setBrightness"); - operation.brightness = screenBrightness; - operation.silent = true - service.startOperationCall(operation); - } - } - - function requestScreenshot() { - screenshotRequested = true; - root.closeRequested(); - } - - onClosed: { - if (screenshotRequested) { - plasmoid.nativeInterface.takeScreenshot(); - screenshotRequested = false; - } - } - - PlasmaCore.DataSource { - id: pmSource - engine: "powermanagement" - connectedSources: ["PowerDevil"] - onSourceAdded: { - if (source === "PowerDevil") { - disconnectSource(source); - connectSource(source); - } - } - onDataChanged: { - disableBrightnessUpdate = true; - root.screenBrightness = pmSource.data["PowerDevil"]["Screen Brightness"]; - disableBrightnessUpdate = false; - } - } - - // night color - CC.CompositorAdaptor { - id: compositorAdaptor - } - - //HACK: make the list know about the applet delegate which is a qtobject - QtObject { - id: nullApplet - } - Component.onCompleted: { - //NOTE: add all in javascript as the static decl of listelements can't have scripts - settingsModel.append({ - "text": i18n("Settings"), - "icon": "configure", - "enabled": false, - "settingsCommand": "plasma-settings", - "toggleFunction": "", - "applet": null - }); - settingsModel.append({ - "text": i18n("Wifi"), - "icon": "network-wireless-signal", - "settingsCommand": "plasma-settings -m kcm_mobile_wifi", - "toggleFunction": "toggleWifi", - "enabled": enabledConnections.wirelessEnabled, - "applet": null - }); - settingsModel.append({ - "text": i18n("Bluetooth"), - "icon": "network-bluetooth", - "settingsCommand": "plasma-settings -m kcm_bluetooth", - "toggleFunction": "toggleBluetooth", - "delegate": "", - "enabled": BluezQt.Manager.bluetoothOperational, - "applet": null - }); - settingsModel.append({ - "text": i18n("Mobile Data"), - "icon": "network-modem", - "settingsCommand": "plasma-settings -m kcm_mobile_broadband", - "toggleFunction": "toggleWwan", - "enabled": enabledConnections.wwanEnabled, - "applet": null - }); - settingsModel.append({ - "text": i18n("Battery"), - "icon": "battery-full", - "enabled": false, - "settingsCommand": "plasma-settings -m kcm_mobile_power", - "toggleFunction": "", - "applet": null - }); - settingsModel.append({ - "text": i18n("Sound"), - "icon": "audio-speakers-symbolic", - "enabled": false, - "settingsCommand": "plasma-settings -m kcm_pulseaudio", - "toggleFunction": "", - "applet": null - }); - settingsModel.append({ - "text": i18n("Flashlight"), - "icon": "flashlight-on", - "enabled": plasmoid.nativeInterface.torchEnabled, - "settingsCommand": "", - "toggleFunction": "toggleTorch", - "applet": null - }); - settingsModel.append({ - "text": i18n("Location"), - "icon": "gps", - "enabled": false, - "settingsCommand": "", - "applet": null - }); - settingsModel.append({ - "text": i18n("Screenshot"), - "icon": "spectacle", - "enabled": false, - "settingsCommand": "", - "toggleFunction": "requestScreenshot", - "applet": null - }); - settingsModel.append({ - "text": i18n("Auto-rotate"), - "icon": "rotation-allowed", - "enabled": plasmoid.nativeInterface.autoRotateEnabled, - "settingsCommand": "", - "toggleFunction": "toggleRotation", - "applet": null - }); - settingsModel.append({ - "text": i18n("Night Color"), - "icon": "redshift-status-on", - "enabled": compositorAdaptor.active, - "settingsCommand": "", // change once night color kcm is added - "toggleFunction": "toggleNightColor", - "applet": null - }); - - brightnessSlider.moved.connect(function() { - root.screenBrightness = brightnessSlider.value; - }); - disableBrightnessUpdate = false; - } - - ListModel { - id: settingsModel - } - - Item { - id: backgroundParent - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: firstRowHeight + otherRowsHeight * root.expandedRatio - } - - Item { - anchors { - fill: parent - margins: units.smallSpacing - } - - readonly property real cellSizeHint: units.iconSizes.large + units.smallSpacing * 6 - readonly property real columnWidth: Math.floor(width / Math.floor(width / cellSizeHint)) - - ColumnLayout { - id: column - anchors.fill: parent - spacing: 0 - - Flow { - id: flow - Layout.fillWidth: true - Layout.leftMargin: units.smallSpacing + (units.largeSpacing - units.smallSpacing) * root.expandedRatio - Layout.rightMargin: units.largeSpacing - Layout.topMargin: units.largeSpacing - - readonly property real cellSizeHint: units.iconSizes.large + units.smallSpacing * 6 - readonly property real columnWidth: Math.floor(width / Math.floor(width / cellSizeHint)) - spacing: 0 - Repeater { - model: settingsModel - delegate: Delegate { - id: delegateItem - - //FIXME: why this is needed? - width: flow.columnWidth - (y > 0 ? 0 : (flow.columnWidth/Math.floor(flow.width / flow.columnWidth)) * (1 - root.expandedRatio)) - height: item ? item.implicitHeight : 0 - - labelOpacity: y > 0 ? 1 : root.expandedRatio - - opacity: y <= 0 ? 1 : root.expandedRatio - transform: Translate { - y: otherRowsHeight * (1 - root.expandedRatio) - } - - Connections { - target: delegateItem - onCloseRequested: root.closeRequested(); - } - Connections { - target: root - onClosed: delegateItem.panelClosed(); - } - } - } - - move: Transition { - NumberAnimation { - duration: units.shortDuration - //Here a linear easing actually looks better - //easing.type: Easing.InOutQuad - properties: "x,y" - } - } - } - BrightnessItem { - id: brightnessSlider - - Layout.bottomMargin: units.largeSpacing - Layout.leftMargin: units.largeSpacing - Layout.rightMargin: units.largeSpacing - Layout.fillWidth: true - - value: root.screenBrightness - maximumValue: root.maximumScreenBrightness - opacity: root.expandedRatio - transform: Translate { - y: otherRowsHeight * (1 - root.expandedRatio) - } - - Connections { - target: root - onScreenBrightnessChanged: brightnessSlider.value = root.screenBrightness - } - } - } - } -} diff --git a/containments/panel/package/contents/ui/quicksettings/QuickSettingsModel.qml b/containments/panel/package/contents/ui/quicksettings/QuickSettingsModel.qml new file mode 100644 index 00000000..b31c8c3b --- /dev/null +++ b/containments/panel/package/contents/ui/quicksettings/QuickSettingsModel.qml @@ -0,0 +1,190 @@ +/* + * SPDX-FileCopyrightText: 2015 Marco Martin + * SPDX-FileCopyrightText: 2021 Devin Lin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.networkmanagement 0.2 as PlasmaNM +import org.kde.bluezqt 1.0 as BluezQt +import org.kde.colorcorrect 0.1 as CC +import org.kde.plasma.private.nanoshell 2.0 as NanoShell + +import org.kde.plasma.components 3.0 as PC3 + +Item { + property alias model: settingsModel + property bool screenshotRequested: false + + signal panelClosed() + + onPanelClosed: { + if (screenshotRequested) { + plasmoid.nativeInterface.takeScreenshot(); + screenshotRequested = false; + } + } + + ListModel { + id: settingsModel + } + + PlasmaNM.Handler { + id: nmHandler + } + + PlasmaNM.EnabledConnections { + id: enabledConnections + } + + // night color + CC.CompositorAdaptor { + id: compositorAdaptor + } + + Connections { + target: BluezQt.Manager + function onBluetoothOperationalChanged() { + settingsModel.get(2).enabled = BluezQt.Manager.bluetoothOperational + } + } + + function toggleAirplane() { + print("toggle airplane mode") + } + + function toggleTorch() { + plasmoid.nativeInterface.toggleTorch() + settingsModel.get(6).enabled = plasmoid.nativeInterface.torchEnabled + } + + function toggleWifi() { + nmHandler.enableWireless(!enabledConnections.wirelessEnabled) + settingsModel.get(1).enabled = !enabledConnections.wirelessEnabled + } + + function toggleWwan() { + nmHandler.enableWwan(!enabledConnections.wwanEnabled) + settingsModel.get(3).enabled = !enabledConnections.wwanEnabled + } + + function toggleRotation() { + const enable = !plasmoid.nativeInterface.autoRotateEnabled + plasmoid.nativeInterface.autoRotateEnabled = enable + settingsModel.get(9).enabled = enable + } + + function toggleBluetooth() { + var enable = !BluezQt.Manager.bluetoothOperational; + BluezQt.Manager.bluetoothBlocked = !enable; + + for (var i = 0; i < BluezQt.Manager.adapters.length; ++i) { + var adapter = BluezQt.Manager.adapters[i]; + adapter.powered = enable; + } + } + + function toggleNightColor() { + if (compositorAdaptor.active) { + compositorAdaptor.activeStaged = false; + } else { + compositorAdaptor.activeStaged = true; + compositorAdaptor.modeStaged = 3; // always on + } + compositorAdaptor.sendConfigurationAll(); + settingsModel.get(10).enabled = compositorAdaptor.active; + } + + // components needed for quick settings + function requestScreenshot() { + screenshotRequested = true; + root.closeRequested(); + } + + // initialize quick settings + Component.onCompleted: { + //NOTE: add all in javascript as the static decl of listelements can't have scripts + settingsModel.append({ + "text": i18n("Settings"), + "icon": "configure", + "enabled": false, + "settingsCommand": "plasma-settings", + "toggleFunction": "" + }); + settingsModel.append({ + "text": i18n("Wifi"), + "icon": "network-wireless-signal", + "settingsCommand": "plasma-settings -m kcm_mobile_wifi", + "toggleFunction": "toggleWifi", + "enabled": enabledConnections.wirelessEnabled + }); + settingsModel.append({ + "text": i18n("Bluetooth"), + "icon": "network-bluetooth", + "settingsCommand": "plasma-settings -m kcm_bluetooth", + "toggleFunction": "toggleBluetooth", + "delegate": "", + "enabled": BluezQt.Manager.bluetoothOperational + }); + settingsModel.append({ + "text": i18n("Mobile Data"), + "icon": "network-modem", + "settingsCommand": "plasma-settings -m kcm_mobile_broadband", + "toggleFunction": "toggleWwan", + "enabled": enabledConnections.wwanEnabled + }); + settingsModel.append({ + "text": i18n("Battery"), + "icon": "battery-full", + "enabled": false, + "settingsCommand": "plasma-settings -m kcm_mobile_power", + "toggleFunction": "" + }); + settingsModel.append({ + "text": i18n("Sound"), + "icon": "audio-speakers-symbolic", + "enabled": false, + "settingsCommand": "plasma-settings -m kcm_pulseaudio", + "toggleFunction": "" + }); + settingsModel.append({ + "text": i18n("Flashlight"), + "icon": "flashlight-on", + "enabled": plasmoid.nativeInterface.torchEnabled, + "settingsCommand": "", + "toggleFunction": "toggleTorch" + }); + settingsModel.append({ + "text": i18n("Location"), + "icon": "gps", + "enabled": false, + "settingsCommand": "" + }); + settingsModel.append({ + "text": i18n("Screenshot"), + "icon": "spectacle", + "enabled": false, + "settingsCommand": "", + "toggleFunction": "requestScreenshot" + }); + settingsModel.append({ + "text": i18n("Auto-rotate"), + "icon": "rotation-allowed", + "enabled": plasmoid.nativeInterface.autoRotateEnabled, + "settingsCommand": "", + "toggleFunction": "toggleRotation" + }); + settingsModel.append({ + "text": i18n("Night Color"), + "icon": "redshift-status-on", + "enabled": compositorAdaptor.active, + "settingsCommand": "", // change once night color kcm is added + "toggleFunction": "toggleNightColor" + }); + } +} diff --git a/containments/panel/package/contents/ui/quicksettings/QuickSettingsPanel.qml b/containments/panel/package/contents/ui/quicksettings/QuickSettingsPanel.qml new file mode 100644 index 00000000..3f8840dd --- /dev/null +++ b/containments/panel/package/contents/ui/quicksettings/QuickSettingsPanel.qml @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2015 Marco Martin + * SPDX-FileCopyrightText: 2021 Devin Lin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 +import QtGraphicalEffects 1.12 +import org.kde.kirigami 2.12 as Kirigami +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.networkmanagement 0.2 as PlasmaNM +import org.kde.bluezqt 1.0 as BluezQt +import org.kde.colorcorrect 0.1 as CC +import org.kde.plasma.private.nanoshell 2.0 as NanoShell + +import org.kde.plasma.components 3.0 as PC3 + +import "../" + +Item { + id: root + implicitWidth: column.implicitWidth + PlasmaCore.Units.smallSpacing * 6 + implicitHeight: column.implicitHeight + PlasmaCore.Units.smallSpacing * 2 + + signal closeRequested + signal closed + + property bool expandedMode: parentSlidingPanel.wideScreen + readonly property real expandedRatio: expandedMode ? 1 : Math.max(0, Math.min(1, (parentSlidingPanel.offset - (parentSlidingPanel.topPanelHeight + firstRowHeight) - parentSlidingPanel.topPanelHeight) / otherRowsHeight + 0.05)) // HACK: add 0.05 to prevent jumping since this height isn't exact + + readonly property real topEmptyAreaHeight: parentSlidingPanel.userInteracting + ? (root.height - collapsedHeight) * (1 - expandedRatio) + : (expandedMode ? 0 : root.height - collapsedHeight) + + readonly property real collapsedHeight: parentSlidingPanel.topPanelHeight + firstRowHeight + PlasmaCore.Units.smallSpacing * 2 + readonly property real firstRowHeight: flow.children[0].height + readonly property real otherRowsHeight: column.implicitHeight - firstRowHeight - parentSlidingPanel.topPanelHeight + + Connections { + target: root.parentSlidingPanel + function onUserInteractingChanged() { + if (!parentSlidingPanel.userInteracting) { + if (root.expandedRatio > 0.7) { + root.expandedMode = true; + } + } + } + } + + property NanoShell.FullScreenOverlay parentSlidingPanel + + Connections { + target: root.Window.window + function onVisibilityChanged() { + root.expandedMode = parentSlidingPanel.wideScreen; + } + } + + signal plasmoidTriggered(var applet, var id) + Layout.minimumHeight: flow.implicitHeight + units.largeSpacing*2 + + onClosed: quickSettingsModel.panelClosed() + + property QuickSettingsModel quickSettingsModel: QuickSettingsModel {} + + // shadow below panel (only if not widescreen) + Rectangle { + visible: !parentSlidingPanel.wideScreen + anchors.bottom: background.bottom + anchors.left: background.left + anchors.right: background.right + height: PlasmaCore.Units.gridUnit + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.rgba(0, 0, 0, 0.05) + } + GradientStop { + position: 1.0 + color: "transparent" + } + } + } + // shadow for bottom bar + RectangularGlow { + z: 1 + anchors.topMargin: 1 + anchors.fill: bottomBar + cached: true + glowRadius: 4 + spread: 0.2 + color: Qt.rgba(0, 0, 0, 0.1) + } + // shadow for whole panel (only if widescreen) + RectangularGlow { + visible: parentSlidingPanel.wideScreen + anchors.topMargin: 1 + anchors.top: background.top + anchors.left: background.left + anchors.right: background.right + anchors.bottom: bottomBar.bottom + cached: true + glowRadius: 4 + spread: 0.2 + color: Qt.rgba(0, 0, 0, 0.1) + } + + // bottom "handle bar" + Rectangle { + id: bottomBar + anchors.top: background.bottom + anchors.left: background.left + anchors.right: background.right + color: PlasmaCore.Theme.backgroundColor + height: Math.round(PlasmaCore.Units.gridUnit * 1.3) + z: 1 + + Kirigami.Icon { + visible: !parentSlidingPanel.wideScreen + color: PlasmaCore.Theme.disabledTextColor + source: expandedRatio >= 1 ? "go-up-symbolic" : "go-down-symbolic" + implicitWidth: PlasmaCore.Units.gridUnit + implicitHeight: width + anchors.centerIn: parent + } + } + + Rectangle { + id: background + color: PlasmaCore.Theme.backgroundColor + anchors.fill: parent + + ColumnLayout { + id: column + anchors.leftMargin: PlasmaCore.Units.smallSpacing + anchors.rightMargin: PlasmaCore.Units.smallSpacing + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 0 + clip: expandedRatio > 0 && expandedRatio < 1 // only clip when necessary to improve performance + + readonly property real cellSizeHint: units.iconSizes.large + units.smallSpacing * 6 + readonly property real columnWidth: Math.floor(width / Math.floor(width / cellSizeHint)) + + IndicatorsRow { + id: indicatorsRow + z: 1 + Layout.fillWidth: true + Layout.preferredHeight: parentSlidingPanel.topPanelHeight + colorGroup: PlasmaCore.Theme.NormalColorGroup + backgroundColor: "transparent" + showGradientBackground: false + showDropShadow: false + transform: Translate { + y: otherRowsHeight * (1 - root.expandedRatio) - PlasmaCore.Units.smallSpacing + } + } + + Flow { + id: flow + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.leftMargin: units.smallSpacing + (units.largeSpacing - units.smallSpacing) * root.expandedRatio + Layout.rightMargin: units.smallSpacing + (units.largeSpacing - units.smallSpacing) * root.expandedRatio + Layout.topMargin: units.largeSpacing + + readonly property real cellSizeHint: units.iconSizes.large + units.smallSpacing * 6 + readonly property real columns: Math.floor(width / cellSizeHint) + readonly property real columnsWhenCollapsed: 1.05 // .05 to account for the fact that we have an overshoot on the panel on first flick, we don't want the movement to be jarring + readonly property real columnWidth: Math.floor(width / (columns + (columnsWhenCollapsed - columnsWhenCollapsed * root.expandedRatio))) + + spacing: 0 + Repeater { + model: quickSettingsModel.model + delegate: Delegate { + id: delegateItem + settingsModel: quickSettingsModel + width: flow.columnWidth + + labelOpacity: y > 0 ? 1 : root.expandedRatio + opacity: y <= 0 ? 1 : root.expandedRatio + transform: Translate { + y: otherRowsHeight * (1 - root.expandedRatio) - PlasmaCore.Units.smallSpacing * 2 + } + + Connections { + target: delegateItem + onCloseRequested: root.closeRequested(); + } + Connections { + target: root + onClosed: delegateItem.panelClosed(); + } + } + } + + move: Transition { + NumberAnimation { + duration: units.shortDuration + easing.type: Easing.Linear + properties: "x,y" + } + } + } + BrightnessItem { + id: brightnessSlider + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: units.smallSpacing + Layout.bottomMargin: units.smallSpacing + Layout.leftMargin: units.largeSpacing + Layout.rightMargin: units.largeSpacing + Layout.fillWidth: true + + opacity: root.expandedRatio + transform: Translate { + y: otherRowsHeight * (1 - root.expandedRatio) - PlasmaCore.Units.smallSpacing * 2 + } + } + } + } +}