mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
Compare commits
1 Commits
8161fd6acb
...
cc-editmod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db28879185 |
@@ -69,8 +69,23 @@ Item {
|
||||
Component {
|
||||
id: diskUsageDetailComponent
|
||||
DiskUsageDetail {
|
||||
currentMountPath: root.expandedWidgetData?.currentMountPath || "/"
|
||||
currentMountPath: root.expandedWidgetData?.mountPath || "/"
|
||||
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||
|
||||
onMountPathChanged: (newMountPath) => {
|
||||
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
const newWidgets = widgets.map(w => {
|
||||
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
|
||||
const updatedWidget = Object.assign({}, w)
|
||||
updatedWidget.mountPath = newMountPath
|
||||
return updatedWidget
|
||||
}
|
||||
return w
|
||||
})
|
||||
SettingsData.setControlCenterWidgets(newWidgets)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,9 @@ import qs.Common
|
||||
import qs.Services
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
import qs.Modules.ControlCenter.Components
|
||||
import "../utils/layout.js" as LayoutUtils
|
||||
|
||||
Item {
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property bool editMode: false
|
||||
@@ -18,66 +19,23 @@ Item {
|
||||
signal moveWidget(int fromIndex, int toIndex)
|
||||
signal toggleWidgetSize(int index)
|
||||
|
||||
readonly property int gridColumns: 4
|
||||
readonly property real cellWidth: (width - (gridSpacing + 1) * (gridColumns - 1)) / gridColumns
|
||||
readonly property real cellHeight: 60
|
||||
readonly property real gridSpacing: 4
|
||||
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
||||
|
||||
height: {
|
||||
const dummy = [SettingsData.controlCenterWidgets?.length, widgetPositions.length]
|
||||
return calculateGridHeight() + (detailHost.active ? detailHost.height + Theme.spacingL : 0)
|
||||
property var currentRowWidgets: []
|
||||
property real currentRowWidth: 0
|
||||
property int expandedRowIndex: -1
|
||||
|
||||
function calculateRowsAndWidgets() {
|
||||
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex)
|
||||
}
|
||||
|
||||
function calculateGridHeight() {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
if (widgets.length === 0)
|
||||
return 0
|
||||
|
||||
let rows = []
|
||||
let currentRow = []
|
||||
let currentWidth = 0
|
||||
const spacing = gridSpacing
|
||||
const baseWidth = width
|
||||
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
const widget = widgets[i]
|
||||
const widgetWidth = widget.width || 50
|
||||
|
||||
let itemWidth
|
||||
if (widgetWidth <= 25) {
|
||||
itemWidth = (baseWidth - spacing * 3) / 4
|
||||
} else if (widgetWidth <= 50) {
|
||||
itemWidth = (baseWidth - spacing) / 2
|
||||
} else if (widgetWidth <= 75) {
|
||||
itemWidth = (baseWidth - spacing * 2) * 0.75
|
||||
} else {
|
||||
itemWidth = baseWidth
|
||||
}
|
||||
|
||||
if (currentRow.length > 0 && (currentWidth + spacing + itemWidth > baseWidth)) {
|
||||
rows.push([...currentRow])
|
||||
currentRow = [widget]
|
||||
currentWidth = itemWidth
|
||||
} else {
|
||||
currentRow.push(widget)
|
||||
currentWidth += (currentRow.length > 1 ? spacing : 0) + itemWidth
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRow.length > 0) {
|
||||
rows.push(currentRow)
|
||||
}
|
||||
|
||||
return rows.length * cellHeight + (rows.length > 0 ? (rows.length - 1) * spacing : 0)
|
||||
property var layoutResult: {
|
||||
const dummy = [expandedSection, expandedWidgetIndex, model?.controlCenterWidgets]
|
||||
return calculateRowsAndWidgets()
|
||||
}
|
||||
|
||||
DragDropDetailHost {
|
||||
id: detailHost
|
||||
y: calculateGridHeight()
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
expandedSection: root.expandedSection
|
||||
expandedWidgetData: root.expandedWidgetData
|
||||
onLayoutResultChanged: {
|
||||
expandedRowIndex = layoutResult.expandedRowIndex
|
||||
}
|
||||
|
||||
function moveToTop(item) {
|
||||
@@ -92,131 +50,115 @@ Item {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: widgetRepeater
|
||||
model: SettingsData.controlCenterWidgets || []
|
||||
model: root.layoutResult.rows
|
||||
|
||||
DragDropWidgetWrapper {
|
||||
id: widgetWrapper
|
||||
|
||||
editMode: root.editMode
|
||||
widgetData: modelData
|
||||
widgetIndex: index
|
||||
gridCellWidth: root.cellWidth
|
||||
gridCellHeight: root.cellHeight
|
||||
gridColumns: root.gridColumns
|
||||
gridLayout: root
|
||||
isSlider: {
|
||||
const id = modelData.id || ""
|
||||
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider"
|
||||
Column {
|
||||
width: root.width
|
||||
spacing: 0
|
||||
property int rowIndex: index
|
||||
property var rowWidgets: modelData
|
||||
property bool isSliderOnlyRow: {
|
||||
const widgets = rowWidgets || []
|
||||
if (widgets.length === 0) return false
|
||||
return widgets.every(w => w.id === "volumeSlider" || w.id === "brightnessSlider" || w.id === "inputVolumeSlider")
|
||||
}
|
||||
topPadding: isSliderOnlyRow ? (root.editMode ? 4 : -6) : 0
|
||||
bottomPadding: isSliderOnlyRow ? (root.editMode ? 4 : -6) : 0
|
||||
|
||||
widgetComponent: {
|
||||
const id = modelData.id || ""
|
||||
if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
|
||||
return compoundPillComponent
|
||||
} else if (id === "volumeSlider") {
|
||||
return audioSliderComponent
|
||||
} else if (id === "brightnessSlider") {
|
||||
return brightnessSliderComponent
|
||||
} else if (id === "inputVolumeSlider") {
|
||||
return inputAudioSliderComponent
|
||||
} else if (id === "battery") {
|
||||
const widgetWidth = modelData.width || 50
|
||||
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
|
||||
} else if (id === "diskUsage") {
|
||||
return diskUsagePillComponent
|
||||
} else {
|
||||
const widgetWidth = modelData.width || 50
|
||||
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
|
||||
Flow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Repeater {
|
||||
model: rowWidgets || []
|
||||
|
||||
DragDropWidgetWrapper {
|
||||
widgetData: modelData
|
||||
property int globalWidgetIndex: {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
if (widgets[i].id === modelData.id) {
|
||||
if (modelData.id === "diskUsage") {
|
||||
if (widgets[i].instanceId === modelData.instanceId) {
|
||||
return i
|
||||
}
|
||||
} else {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
property int widgetWidth: modelData.width || 50
|
||||
width: {
|
||||
const baseWidth = root.width
|
||||
const spacing = Theme.spacingS
|
||||
if (widgetWidth <= 25) {
|
||||
return (baseWidth - spacing * 3) / 4
|
||||
} else if (widgetWidth <= 50) {
|
||||
return (baseWidth - spacing) / 2
|
||||
} else if (widgetWidth <= 75) {
|
||||
return (baseWidth - spacing * 2) * 0.75
|
||||
} else {
|
||||
return baseWidth
|
||||
}
|
||||
}
|
||||
height: isSliderOnlyRow ? 48 : 60
|
||||
|
||||
editMode: root.editMode
|
||||
widgetIndex: globalWidgetIndex
|
||||
gridCellWidth: width
|
||||
gridCellHeight: height
|
||||
gridColumns: 4
|
||||
gridLayout: root
|
||||
isSlider: {
|
||||
const id = modelData.id || ""
|
||||
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider"
|
||||
}
|
||||
|
||||
widgetComponent: {
|
||||
const id = modelData.id || ""
|
||||
if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
|
||||
return compoundPillComponent
|
||||
} else if (id === "volumeSlider") {
|
||||
return audioSliderComponent
|
||||
} else if (id === "brightnessSlider") {
|
||||
return brightnessSliderComponent
|
||||
} else if (id === "inputVolumeSlider") {
|
||||
return inputAudioSliderComponent
|
||||
} else if (id === "battery") {
|
||||
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
|
||||
} else if (id === "diskUsage") {
|
||||
return diskUsagePillComponent
|
||||
} else {
|
||||
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
|
||||
}
|
||||
}
|
||||
|
||||
onWidgetMoved: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
onRemoveWidget: index => root.removeWidget(index)
|
||||
onToggleWidgetSize: index => root.toggleWidgetSize(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x: calculateWidgetX(index)
|
||||
y: calculateWidgetY(index)
|
||||
DetailHost {
|
||||
width: parent.width
|
||||
height: active ? (250 + Theme.spacingS) : 0
|
||||
property bool active: {
|
||||
if (root.expandedSection === "") return false
|
||||
|
||||
onWidgetMoved: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
onRemoveWidget: index => root.removeWidget(index)
|
||||
onToggleWidgetSize: index => root.toggleWidgetSize(index)
|
||||
if (root.expandedSection.startsWith("diskUsage_") && root.expandedWidgetData) {
|
||||
const expandedInstanceId = root.expandedWidgetData.instanceId
|
||||
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
enabled: !editMode
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Easing.OutCubic
|
||||
return rowIndex === root.expandedRowIndex
|
||||
}
|
||||
visible: active
|
||||
expandedSection: root.expandedSection
|
||||
expandedWidgetData: root.expandedWidgetData
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
enabled: !editMode
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var widgetPositions: calculateAllWidgetPositions()
|
||||
|
||||
function calculateAllWidgetPositions() {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
let positions = []
|
||||
let currentX = 0
|
||||
let currentY = 0
|
||||
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
const widget = widgets[i]
|
||||
const widgetWidth = widget.width || 50
|
||||
let cellsNeeded = 1
|
||||
|
||||
if (widgetWidth <= 25)
|
||||
cellsNeeded = 1
|
||||
else if (widgetWidth <= 50)
|
||||
cellsNeeded = 2
|
||||
else if (widgetWidth <= 75)
|
||||
cellsNeeded = 3
|
||||
else
|
||||
cellsNeeded = 4
|
||||
|
||||
if (currentX + cellsNeeded > gridColumns) {
|
||||
currentX = 0
|
||||
currentY++
|
||||
}
|
||||
|
||||
const horizontalSpacing = gridSpacing
|
||||
positions[i] = {
|
||||
"x": currentX * cellWidth + (currentX > 0 ? currentX * horizontalSpacing : 0),
|
||||
"y": currentY * cellHeight + (currentY > 0 ? currentY * gridSpacing : 0),
|
||||
"cellsUsed": cellsNeeded
|
||||
}
|
||||
|
||||
currentX += cellsNeeded
|
||||
|
||||
if (currentX >= gridColumns) {
|
||||
currentX = 0
|
||||
currentY++
|
||||
}
|
||||
}
|
||||
|
||||
return positions
|
||||
}
|
||||
|
||||
function calculateWidgetX(widgetIndex) {
|
||||
if (widgetIndex < 0 || widgetIndex >= widgetPositions.length)
|
||||
return 0
|
||||
return widgetPositions[widgetIndex].x
|
||||
}
|
||||
|
||||
function calculateWidgetY(widgetIndex) {
|
||||
if (widgetIndex < 0 || widgetIndex >= widgetPositions.length)
|
||||
return 0
|
||||
return widgetPositions[widgetIndex].y
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onControlCenterWidgetsChanged() {
|
||||
widgetPositions = calculateAllWidgetPositions()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +169,7 @@ Item {
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: cellHeight
|
||||
height: 60
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
@@ -395,10 +337,9 @@ Item {
|
||||
return false
|
||||
}
|
||||
}
|
||||
enabled: !root.editMode && (widgetDef?.enabled ?? true)
|
||||
enabled: widgetDef?.enabled ?? true
|
||||
onToggled: {
|
||||
if (root.editMode)
|
||||
return
|
||||
if (root.editMode) return
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
{
|
||||
@@ -431,12 +372,11 @@ Item {
|
||||
}
|
||||
}
|
||||
onExpandClicked: {
|
||||
if (!root.editMode)
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
if (root.editMode) return
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (root.editMode)
|
||||
return
|
||||
if (root.editMode) return
|
||||
const id = widgetData.id || ""
|
||||
if (id === "audioOutput") {
|
||||
if (!AudioService.sink || !AudioService.sink.audio)
|
||||
@@ -471,43 +411,67 @@ Item {
|
||||
|
||||
Component {
|
||||
id: audioSliderComponent
|
||||
AudioSliderRow {
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 14
|
||||
enabled: !root.editMode
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
height: 16
|
||||
|
||||
AudioSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: brightnessSliderComponent
|
||||
BrightnessSliderRow {
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 14
|
||||
enabled: !root.editMode
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
height: 16
|
||||
|
||||
BrightnessSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: inputAudioSliderComponent
|
||||
InputAudioSliderRow {
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 14
|
||||
enabled: !root.editMode
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
height: 16
|
||||
|
||||
InputAudioSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: batteryPillComponent
|
||||
BatteryPill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: cellHeight
|
||||
enabled: !root.editMode
|
||||
height: 60
|
||||
|
||||
onExpandClicked: {
|
||||
if (!root.editMode)
|
||||
root.expandClicked(parent.widgetData, parent.widgetIndex)
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,12 +479,15 @@ Item {
|
||||
Component {
|
||||
id: smallBatteryComponent
|
||||
SmallBatteryButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 48
|
||||
enabled: !root.editMode
|
||||
|
||||
onClicked: {
|
||||
if (!root.editMode)
|
||||
root.expandClicked(parent.widgetData, parent.widgetIndex)
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -530,9 +497,8 @@ Item {
|
||||
ToggleButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: cellHeight
|
||||
height: 60
|
||||
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
@@ -545,7 +511,7 @@ Item {
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
||||
default:
|
||||
return widgetDef?.icon || "help"
|
||||
return "help"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +526,7 @@ Item {
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited ? "Keeping Awake" : "Keep Awake"
|
||||
default:
|
||||
return widgetDef?.text || "Unknown"
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,7 +547,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
enabled: !root.editMode && (widgetDef?.enabled ?? true)
|
||||
enabled: !root.editMode
|
||||
|
||||
onClicked: {
|
||||
if (root.editMode)
|
||||
@@ -618,7 +584,6 @@ Item {
|
||||
SmallToggleButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
@@ -633,7 +598,7 @@ Item {
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
||||
default:
|
||||
return widgetDef?.icon || "help"
|
||||
return "help"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,7 +619,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
enabled: !root.editMode && (widgetDef?.enabled ?? true)
|
||||
enabled: !root.editMode
|
||||
|
||||
onClicked: {
|
||||
if (root.editMode)
|
||||
@@ -689,14 +654,18 @@ Item {
|
||||
Component {
|
||||
id: diskUsagePillComponent
|
||||
DiskUsagePill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: cellHeight
|
||||
enabled: !root.editMode
|
||||
mountPath: parent.widgetData?.mountPath || "/"
|
||||
instanceId: parent.widgetData?.instanceId || ""
|
||||
height: 60
|
||||
|
||||
mountPath: widgetData.mountPath || "/"
|
||||
instanceId: widgetData.instanceId || ""
|
||||
|
||||
onExpandClicked: {
|
||||
if (!root.editMode)
|
||||
root.expandClicked(parent.widgetData, parent.widgetIndex)
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ Item {
|
||||
property int gridColumns: 4
|
||||
property var gridLayout: null
|
||||
|
||||
z: dragArea.drag.active ? 10000 : 1
|
||||
|
||||
signal widgetMoved(int fromIndex, int toIndex)
|
||||
signal removeWidget(int index)
|
||||
signal toggleWidgetSize(int index)
|
||||
@@ -27,7 +29,7 @@ Item {
|
||||
else if (widgetWidth <= 75) return gridCellWidth * 3
|
||||
else return gridCellWidth * 4
|
||||
}
|
||||
height: gridCellHeight
|
||||
height: isSlider ? 16 : gridCellHeight
|
||||
|
||||
Rectangle {
|
||||
id: dragIndicator
|
||||
@@ -37,7 +39,7 @@ Item {
|
||||
border.width: dragArea.drag.active ? 2 : 0
|
||||
radius: Theme.cornerRadius
|
||||
opacity: dragArea.drag.active ? 0.8 : 1.0
|
||||
z: dragArea.drag.active ? 1000 : 1
|
||||
z: dragArea.drag.active ? 10000 : 1
|
||||
|
||||
Behavior on border.width {
|
||||
NumberAnimation { duration: 150 }
|
||||
@@ -56,13 +58,14 @@ Item {
|
||||
property int globalWidgetIndex: root.widgetIndex
|
||||
property int widgetWidth: root.widgetData?.width || 50
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: editModeBlocker
|
||||
anchors.fill: parent
|
||||
enabled: root.editMode
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onPressed: mouse.accepted = true
|
||||
onWheel: wheel.accepted = true
|
||||
onPressed: function(mouse) { mouse.accepted = true }
|
||||
onWheel: function(wheel) { wheel.accepted = true }
|
||||
z: 100
|
||||
}
|
||||
}
|
||||
@@ -71,23 +74,23 @@ Item {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
enabled: editMode
|
||||
cursorShape: editMode ? Qt.OpenHandCursor : Qt.ArrowCursor
|
||||
cursorShape: editMode ? Qt.OpenHandCursor : Qt.PointingHandCursor
|
||||
drag.target: editMode ? root : null
|
||||
drag.axis: Drag.XAndYAxis
|
||||
drag.smoothed: true
|
||||
|
||||
onPressed: {
|
||||
onPressed: function(mouse) {
|
||||
if (editMode) {
|
||||
cursorShape = Qt.ClosedHandCursor
|
||||
root.z = 1000
|
||||
root.parent.moveToTop(root)
|
||||
if (root.gridLayout && root.gridLayout.moveToTop) {
|
||||
root.gridLayout.moveToTop(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
onReleased: function(mouse) {
|
||||
if (editMode) {
|
||||
cursorShape = Qt.OpenHandCursor
|
||||
root.z = 1
|
||||
root.snapToGrid()
|
||||
}
|
||||
}
|
||||
@@ -97,6 +100,19 @@ Item {
|
||||
Drag.hotSpot.x: width / 2
|
||||
Drag.hotSpot.y: height / 2
|
||||
|
||||
function swapIndices(i, j) {
|
||||
if (i === j) return;
|
||||
const arr = SettingsData.controlCenterWidgets;
|
||||
if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) return;
|
||||
|
||||
const copy = arr.slice();
|
||||
const tmp = copy[i];
|
||||
copy[i] = copy[j];
|
||||
copy[j] = tmp;
|
||||
|
||||
SettingsData.setControlCenterWidgets(copy);
|
||||
}
|
||||
|
||||
function snapToGrid() {
|
||||
if (!editMode || !gridLayout) return
|
||||
|
||||
@@ -104,80 +120,90 @@ Item {
|
||||
const cellWidth = gridLayout.width / gridColumns
|
||||
const cellHeight = gridCellHeight + Theme.spacingS
|
||||
|
||||
let targetCol = Math.max(0, Math.round(globalPos.x / cellWidth))
|
||||
let targetRow = Math.max(0, Math.round(globalPos.y / cellHeight))
|
||||
const centerX = globalPos.x + (root.width / 2)
|
||||
const centerY = globalPos.y + (root.height / 2)
|
||||
|
||||
const widgetCells = Math.ceil(root.width / cellWidth)
|
||||
let targetCol = Math.max(0, Math.floor(centerX / cellWidth))
|
||||
let targetRow = Math.max(0, Math.floor(centerY / cellHeight))
|
||||
|
||||
if (targetCol + widgetCells > gridColumns) {
|
||||
targetCol = Math.max(0, gridColumns - widgetCells)
|
||||
}
|
||||
targetCol = Math.min(targetCol, gridColumns - 1)
|
||||
|
||||
const newIndex = findBestInsertionIndex(targetRow, targetCol)
|
||||
|
||||
if (newIndex !== widgetIndex && newIndex >= 0 && newIndex < (SettingsData.controlCenterWidgets?.length || 0)) {
|
||||
widgetMoved(widgetIndex, newIndex)
|
||||
swapIndices(widgetIndex, newIndex)
|
||||
}
|
||||
}
|
||||
|
||||
function findBestInsertionIndex(targetRow, targetCol) {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
if (!widgets.length) return 0
|
||||
const widgets = SettingsData.controlCenterWidgets || [];
|
||||
const n = widgets.length;
|
||||
if (!n || widgetIndex < 0 || widgetIndex >= n) return -1;
|
||||
|
||||
const targetPosition = targetRow * gridColumns + targetCol
|
||||
function spanFor(width) {
|
||||
const w = width ?? 50;
|
||||
if (w <= 25) return 1;
|
||||
if (w <= 50) return 2;
|
||||
if (w <= 75) return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
// Find the widget position closest to our target
|
||||
let bestIndex = 0
|
||||
let bestDistance = Infinity
|
||||
const cols = gridColumns || 4;
|
||||
|
||||
for (let i = 0; i <= widgets.length; i++) {
|
||||
if (i === widgetIndex) continue
|
||||
let row = 0, col = 0;
|
||||
let draggedOrigKey = null;
|
||||
|
||||
let currentPos = calculatePositionForIndex(i)
|
||||
let distance = Math.abs(currentPos - targetPosition)
|
||||
const pos = [];
|
||||
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance
|
||||
bestIndex = i > widgetIndex ? i - 1 : i
|
||||
for (let i = 0; i < n; i++) {
|
||||
const span = Math.min(spanFor(widgets[i].width), cols);
|
||||
|
||||
if (col + span > cols) {
|
||||
row++;
|
||||
col = 0;
|
||||
}
|
||||
|
||||
const startCol = col;
|
||||
const centerKey = row * cols + (startCol + (span - 1) / 2);
|
||||
|
||||
if (i === widgetIndex) {
|
||||
draggedOrigKey = centerKey;
|
||||
} else {
|
||||
pos.push({ index: i, row, startCol, span, centerKey });
|
||||
}
|
||||
|
||||
col += span;
|
||||
if (col >= cols) {
|
||||
row++;
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.max(0, Math.min(bestIndex, widgets.length - 1))
|
||||
}
|
||||
if (pos.length === 0) return -1;
|
||||
|
||||
function calculatePositionForIndex(index) {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
let currentX = 0
|
||||
let currentY = 0
|
||||
const centerColCoord = targetCol + 0.5;
|
||||
const targetKey = targetRow * cols + centerColCoord;
|
||||
|
||||
for (let i = 0; i < index && i < widgets.length; i++) {
|
||||
if (i === widgetIndex) continue
|
||||
|
||||
const widget = widgets[i]
|
||||
const widgetWidth = widget.width || 50
|
||||
let cellsNeeded = widgetWidth <= 25 ? 1 : widgetWidth <= 50 ? 2 : widgetWidth <= 75 ? 3 : 4
|
||||
|
||||
if (currentX + cellsNeeded > gridColumns) {
|
||||
currentX = 0
|
||||
currentY++
|
||||
}
|
||||
|
||||
currentX += cellsNeeded
|
||||
if (currentX >= gridColumns) {
|
||||
currentX = 0
|
||||
currentY++
|
||||
for (let k = 0; k < pos.length; k++) {
|
||||
const p = pos[k];
|
||||
if (p.row === targetRow && centerColCoord >= p.startCol && centerColCoord < (p.startCol + p.span)) {
|
||||
return p.index;
|
||||
}
|
||||
}
|
||||
|
||||
return currentY * gridColumns + currentX
|
||||
}
|
||||
let lo = 0, hi = pos.length - 1;
|
||||
if (targetKey <= pos[0].centerKey) return pos[0].index;
|
||||
if (targetKey >= pos[hi].centerKey) return pos[hi].index;
|
||||
|
||||
function getWidgetWidth(widgetWidth) {
|
||||
const cellWidth = gridLayout ? gridLayout.width / gridColumns : gridCellWidth
|
||||
if (widgetWidth <= 25) return cellWidth
|
||||
else if (widgetWidth <= 50) return cellWidth * 2
|
||||
else if (widgetWidth <= 75) return cellWidth * 3
|
||||
else return cellWidth * 4
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
const mk = pos[mid].centerKey;
|
||||
if (targetKey < mk) hi = mid - 1;
|
||||
else if (targetKey > mk) lo = mid + 1;
|
||||
else return pos[mid].index;
|
||||
}
|
||||
const movingUp = (draggedOrigKey != null) ? (targetKey < draggedOrigKey) : false;
|
||||
return (movingUp ? pos[lo].index : pos[hi].index);
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -200,6 +226,7 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: removeWidget(widgetIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,719 +0,0 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
import qs.Modules.ControlCenter.Components
|
||||
import "../utils/layout.js" as LayoutUtils
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property bool editMode: false
|
||||
property string expandedSection: ""
|
||||
property int expandedWidgetIndex: -1
|
||||
property var model: null
|
||||
property var expandedWidgetData: null
|
||||
|
||||
signal expandClicked(var widgetData, int globalIndex)
|
||||
signal removeWidget(int index)
|
||||
signal moveWidget(int fromIndex, int toIndex)
|
||||
signal toggleWidgetSize(int index)
|
||||
|
||||
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
||||
|
||||
property var currentRowWidgets: []
|
||||
property real currentRowWidth: 0
|
||||
property int expandedRowIndex: -1
|
||||
|
||||
function calculateRowsAndWidgets() {
|
||||
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex)
|
||||
}
|
||||
|
||||
property var layoutResult: {
|
||||
const dummy = [expandedSection, expandedWidgetIndex, model?.controlCenterWidgets]
|
||||
return calculateRowsAndWidgets()
|
||||
}
|
||||
|
||||
onLayoutResultChanged: {
|
||||
expandedRowIndex = layoutResult.expandedRowIndex
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.layoutResult.rows
|
||||
|
||||
Column {
|
||||
width: root.width
|
||||
spacing: 0
|
||||
property int rowIndex: index
|
||||
property var rowWidgets: modelData
|
||||
property bool isSliderOnlyRow: {
|
||||
const widgets = rowWidgets || []
|
||||
if (widgets.length === 0) return false
|
||||
return widgets.every(w => w.id === "volumeSlider" || w.id === "brightnessSlider" || w.id === "inputVolumeSlider")
|
||||
}
|
||||
topPadding: isSliderOnlyRow ? (root.editMode ? 4 : -12) : 0
|
||||
bottomPadding: isSliderOnlyRow ? (root.editMode ? 4 : -12) : 0
|
||||
|
||||
Flow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Repeater {
|
||||
model: rowWidgets || []
|
||||
|
||||
Item {
|
||||
property var widgetData: modelData
|
||||
property int globalWidgetIndex: {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
if (widgets[i].id === modelData.id) {
|
||||
if (modelData.id === "diskUsage") {
|
||||
if (widgets[i].instanceId === modelData.instanceId) {
|
||||
return i
|
||||
}
|
||||
} else {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
property int widgetWidth: modelData.width || 50
|
||||
width: {
|
||||
const baseWidth = root.width
|
||||
const spacing = Theme.spacingS
|
||||
if (widgetWidth <= 25) {
|
||||
return (baseWidth - spacing * 3) / 4
|
||||
} else if (widgetWidth <= 50) {
|
||||
return (baseWidth - spacing) / 2
|
||||
} else if (widgetWidth <= 75) {
|
||||
return (baseWidth - spacing * 2) * 0.75
|
||||
} else {
|
||||
return baseWidth
|
||||
}
|
||||
}
|
||||
height: 60
|
||||
|
||||
Loader {
|
||||
id: widgetLoader
|
||||
anchors.fill: parent
|
||||
property var widgetData: parent.widgetData
|
||||
property int widgetIndex: parent.globalWidgetIndex
|
||||
property int globalWidgetIndex: parent.globalWidgetIndex
|
||||
property int widgetWidth: parent.widgetWidth
|
||||
|
||||
sourceComponent: {
|
||||
const id = modelData.id || ""
|
||||
if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
|
||||
return compoundPillComponent
|
||||
} else if (id === "volumeSlider") {
|
||||
return audioSliderComponent
|
||||
} else if (id === "brightnessSlider") {
|
||||
return brightnessSliderComponent
|
||||
} else if (id === "inputVolumeSlider") {
|
||||
return inputAudioSliderComponent
|
||||
} else if (id === "battery") {
|
||||
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
|
||||
} else if (id === "diskUsage") {
|
||||
return diskUsagePillComponent
|
||||
} else {
|
||||
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DetailHost {
|
||||
width: parent.width
|
||||
height: active ? (250 + Theme.spacingS) : 0
|
||||
property bool active: {
|
||||
if (root.expandedSection === "") return false
|
||||
|
||||
if (root.expandedSection.startsWith("diskUsage_") && root.expandedWidgetData) {
|
||||
const expandedInstanceId = root.expandedWidgetData.instanceId
|
||||
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
|
||||
}
|
||||
|
||||
return rowIndex === root.expandedRowIndex
|
||||
}
|
||||
visible: active
|
||||
expandedSection: root.expandedSection
|
||||
expandedWidgetData: root.expandedWidgetData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: compoundPillComponent
|
||||
CompoundPill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: 60
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi": {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return "sync"
|
||||
}
|
||||
if (NetworkService.networkStatus === "ethernet") {
|
||||
return "settings_ethernet"
|
||||
}
|
||||
if (NetworkService.networkStatus === "wifi") {
|
||||
return NetworkService.wifiSignalIcon
|
||||
}
|
||||
if (NetworkService.wifiEnabled) {
|
||||
return "wifi_off"
|
||||
}
|
||||
return "wifi_off"
|
||||
}
|
||||
case "bluetooth": {
|
||||
if (!BluetoothService.available) {
|
||||
return "bluetooth_disabled"
|
||||
}
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
||||
return "bluetooth_disabled"
|
||||
}
|
||||
return "bluetooth"
|
||||
}
|
||||
case "audioOutput": {
|
||||
if (!AudioService.sink) return "volume_off"
|
||||
let volume = AudioService.sink.audio.volume
|
||||
let muted = AudioService.sink.audio.muted
|
||||
if (muted || volume === 0.0) return "volume_off"
|
||||
if (volume <= 0.33) return "volume_down"
|
||||
if (volume <= 0.66) return "volume_up"
|
||||
return "volume_up"
|
||||
}
|
||||
case "audioInput": {
|
||||
if (!AudioService.source) return "mic_off"
|
||||
let muted = AudioService.source.audio.muted
|
||||
return muted ? "mic_off" : "mic"
|
||||
}
|
||||
default: return widgetDef?.icon || "help"
|
||||
}
|
||||
}
|
||||
primaryText: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi": {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
||||
}
|
||||
if (NetworkService.networkStatus === "ethernet") {
|
||||
return "Ethernet"
|
||||
}
|
||||
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID) {
|
||||
return NetworkService.currentWifiSSID
|
||||
}
|
||||
if (NetworkService.wifiEnabled) {
|
||||
return "Not connected"
|
||||
}
|
||||
return "WiFi off"
|
||||
}
|
||||
case "bluetooth": {
|
||||
if (!BluetoothService.available) {
|
||||
return "Bluetooth"
|
||||
}
|
||||
if (!BluetoothService.adapter) {
|
||||
return "No adapter"
|
||||
}
|
||||
if (!BluetoothService.adapter.enabled) {
|
||||
return "Disabled"
|
||||
}
|
||||
return "Enabled"
|
||||
}
|
||||
case "audioOutput": return AudioService.sink?.description || "No output device"
|
||||
case "audioInput": return AudioService.source?.description || "No input device"
|
||||
default: return widgetDef?.text || "Unknown"
|
||||
}
|
||||
}
|
||||
secondaryText: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi": {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return "Please wait..."
|
||||
}
|
||||
if (NetworkService.networkStatus === "ethernet") {
|
||||
return "Connected"
|
||||
}
|
||||
if (NetworkService.networkStatus === "wifi") {
|
||||
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
|
||||
}
|
||||
if (NetworkService.wifiEnabled) {
|
||||
return "Select network"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
case "bluetooth": {
|
||||
if (!BluetoothService.available) {
|
||||
return "No adapters"
|
||||
}
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
||||
return "Off"
|
||||
}
|
||||
const primaryDevice = (() => {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) {
|
||||
return null
|
||||
}
|
||||
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
||||
for (let device of devices) {
|
||||
if (device && device.connected) {
|
||||
return device
|
||||
}
|
||||
}
|
||||
return null
|
||||
})()
|
||||
if (primaryDevice) {
|
||||
return primaryDevice.name || primaryDevice.alias || primaryDevice.deviceName || "Connected Device"
|
||||
}
|
||||
return "No devices"
|
||||
}
|
||||
case "audioOutput": {
|
||||
if (!AudioService.sink) {
|
||||
return "Select device"
|
||||
}
|
||||
if (AudioService.sink.audio.muted) {
|
||||
return "Muted"
|
||||
}
|
||||
return Math.round(AudioService.sink.audio.volume * 100) + "%"
|
||||
}
|
||||
case "audioInput": {
|
||||
if (!AudioService.source) {
|
||||
return "Select device"
|
||||
}
|
||||
if (AudioService.source.audio.muted) {
|
||||
return "Muted"
|
||||
}
|
||||
return Math.round(AudioService.source.audio.volume * 100) + "%"
|
||||
}
|
||||
default: return widgetDef?.description || ""
|
||||
}
|
||||
}
|
||||
isActive: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi": {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return false
|
||||
}
|
||||
if (NetworkService.networkStatus === "ethernet") {
|
||||
return true
|
||||
}
|
||||
if (NetworkService.networkStatus === "wifi") {
|
||||
return true
|
||||
}
|
||||
return NetworkService.wifiEnabled
|
||||
}
|
||||
case "bluetooth": return !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled)
|
||||
case "audioOutput": return !!(AudioService.sink && !AudioService.sink.audio.muted)
|
||||
case "audioInput": return !!(AudioService.source && !AudioService.source.audio.muted)
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
enabled: (widgetDef?.enabled ?? true)
|
||||
onToggled: {
|
||||
if (root.editMode) return
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi": {
|
||||
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
|
||||
NetworkService.toggleWifiRadio()
|
||||
}
|
||||
break
|
||||
}
|
||||
case "bluetooth": {
|
||||
if (BluetoothService.available && BluetoothService.adapter) {
|
||||
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
|
||||
}
|
||||
break
|
||||
}
|
||||
case "audioOutput": {
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
AudioService.sink.audio.muted = !AudioService.sink.audio.muted
|
||||
}
|
||||
break
|
||||
}
|
||||
case "audioInput": {
|
||||
if (AudioService.source && AudioService.source.audio) {
|
||||
AudioService.source.audio.muted = !AudioService.source.audio.muted
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
onExpandClicked: {
|
||||
if (root.editMode) return
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
const id = widgetData.id || ""
|
||||
if (id === "audioOutput") {
|
||||
if (!AudioService.sink || !AudioService.sink.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = AudioService.sink.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
AudioService.sink.audio.muted = false
|
||||
AudioService.sink.audio.volume = newVolume / 100
|
||||
wheelEvent.accepted = true
|
||||
} else if (id === "audioInput") {
|
||||
if (!AudioService.source || !AudioService.source.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = AudioService.source.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
AudioService.source.audio.muted = false
|
||||
AudioService.source.audio.volume = newVolume / 100
|
||||
wheelEvent.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: false
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioSliderComponent
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: 16
|
||||
|
||||
AudioSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: true
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: brightnessSliderComponent
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 16
|
||||
|
||||
BrightnessSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: true
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: inputAudioSliderComponent
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 16
|
||||
|
||||
InputAudioSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: true
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: batteryPillComponent
|
||||
BatteryPill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 60
|
||||
|
||||
onExpandClicked: {
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: false
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: smallBatteryComponent
|
||||
SmallBatteryButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
onClicked: {
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: false
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: toggleButtonComponent
|
||||
ToggleButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: 60
|
||||
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode": return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||
case "darkMode": return "contrast"
|
||||
case "doNotDisturb": return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
|
||||
case "idleInhibitor": return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
||||
default: return widgetDef?.icon || "help"
|
||||
}
|
||||
}
|
||||
|
||||
text: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode": return "Night Mode"
|
||||
case "darkMode": return SessionData.isLightMode ? "Light Mode" : "Dark Mode"
|
||||
case "doNotDisturb": return "Do Not Disturb"
|
||||
case "idleInhibitor": return SessionService.idleInhibited ? "Keeping Awake" : "Keep Awake"
|
||||
default: return widgetDef?.text || "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
secondaryText: ""
|
||||
|
||||
iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0
|
||||
|
||||
isActive: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode": return DisplayService.nightModeEnabled || false
|
||||
case "darkMode": return !SessionData.isLightMode
|
||||
case "doNotDisturb": return SessionData.doNotDisturb || false
|
||||
case "idleInhibitor": return SessionService.idleInhibited || false
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
enabled: (widgetDef?.enabled ?? true) && !root.editMode
|
||||
|
||||
onClicked: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode": {
|
||||
if (DisplayService.automationAvailable) {
|
||||
DisplayService.toggleNightMode()
|
||||
}
|
||||
break
|
||||
}
|
||||
case "darkMode": {
|
||||
Theme.toggleLightMode()
|
||||
break
|
||||
}
|
||||
case "doNotDisturb": {
|
||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
||||
break
|
||||
}
|
||||
case "idleInhibitor": {
|
||||
SessionService.toggleIdleInhibit()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: false
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: smallToggleComponent
|
||||
SmallToggleButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode": return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||
case "darkMode": return "contrast"
|
||||
case "doNotDisturb": return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
|
||||
case "idleInhibitor": return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
||||
default: return widgetDef?.icon || "help"
|
||||
}
|
||||
}
|
||||
|
||||
iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0
|
||||
|
||||
isActive: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode": return DisplayService.nightModeEnabled || false
|
||||
case "darkMode": return !SessionData.isLightMode
|
||||
case "doNotDisturb": return SessionData.doNotDisturb || false
|
||||
case "idleInhibitor": return SessionService.idleInhibited || false
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
enabled: (widgetDef?.enabled ?? true) && !root.editMode
|
||||
|
||||
onClicked: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode": {
|
||||
if (DisplayService.automationAvailable) {
|
||||
DisplayService.toggleNightMode()
|
||||
}
|
||||
break
|
||||
}
|
||||
case "darkMode": {
|
||||
Theme.toggleLightMode()
|
||||
break
|
||||
}
|
||||
case "doNotDisturb": {
|
||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
||||
break
|
||||
}
|
||||
case "idleInhibitor": {
|
||||
SessionService.toggleIdleInhibit()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: false
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: diskUsagePillComponent
|
||||
DiskUsagePill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 60
|
||||
|
||||
mountPath: widgetData.mountPath || "/"
|
||||
instanceId: widgetData.instanceId || ""
|
||||
|
||||
onExpandClicked: {
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
|
||||
EditModeOverlay {
|
||||
anchors.fill: parent
|
||||
editMode: root.editMode
|
||||
widgetData: parent.widgetData
|
||||
widgetIndex: parent.widgetIndex
|
||||
showSizeControls: true
|
||||
isSlider: false
|
||||
onRemoveWidget: (index) => root.removeWidget(index)
|
||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
Row {
|
||||
id: root
|
||||
|
||||
property int currentSize: 50
|
||||
@@ -11,75 +11,41 @@ Item {
|
||||
|
||||
signal sizeChanged(int newSize)
|
||||
|
||||
width: 28
|
||||
height: 28
|
||||
|
||||
readonly property var availableSizes: isSlider ? [50, 100] : [25, 50, 75, 100]
|
||||
readonly property int currentSizeIndex: availableSizes.indexOf(currentSize)
|
||||
|
||||
Canvas {
|
||||
id: pieCanvas
|
||||
anchors.fill: parent
|
||||
spacing: 2
|
||||
|
||||
onPaint: {
|
||||
const ctx = getContext("2d")
|
||||
const centerX = width / 2
|
||||
const centerY = height / 2
|
||||
const radius = Math.min(width, height) / 2 - 2
|
||||
Repeater {
|
||||
model: root.availableSizes
|
||||
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
Rectangle {
|
||||
width: 16
|
||||
height: 16
|
||||
radius: 3
|
||||
color: modelData === root.currentSize ? Theme.primary : Theme.surfaceContainer
|
||||
border.color: modelData === root.currentSize ? Theme.primary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
ctx.strokeStyle = Theme.primary
|
||||
ctx.lineWidth = 1.5
|
||||
ctx.beginPath()
|
||||
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI)
|
||||
ctx.stroke()
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.toString()
|
||||
font.pixelSize: 8
|
||||
font.weight: Font.Medium
|
||||
color: modelData === root.currentSize ? Theme.primaryContainer : Theme.surfaceText
|
||||
}
|
||||
|
||||
if (availableSizes.length > 0 && currentSizeIndex >= 0) {
|
||||
const segmentAngle = (2 * Math.PI) / availableSizes.length
|
||||
const startAngle = -Math.PI / 2
|
||||
const endAngle = startAngle + segmentAngle * (currentSizeIndex + 1)
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.currentSize = modelData
|
||||
root.sizeChanged(modelData)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillStyle = Theme.primary
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(centerX, centerY)
|
||||
ctx.arc(centerX, centerY, radius - 1, startAngle, endAngle)
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: Theme.surfaceContainer
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const nextIndex = (currentSizeIndex + 1) % availableSizes.length
|
||||
const newSize = availableSizes[nextIndex]
|
||||
currentSize = newSize
|
||||
pieCanvas.requestPaint()
|
||||
sizeChanged(newSize)
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentSizeChanged: {
|
||||
pieCanvas.requestPaint()
|
||||
}
|
||||
|
||||
onIsSliderChanged: {
|
||||
if (isSlider && currentSize !== 50 && currentSize !== 100) {
|
||||
currentSize = 50
|
||||
}
|
||||
pieCanvas.requestPaint()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user