1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

cc: allow multiple brightness sliders

This commit is contained in:
bbedward
2025-10-15 10:31:55 -04:00
parent 342cd55bc0
commit 8bb2a64663
7 changed files with 330 additions and 106 deletions

View File

@@ -13,6 +13,7 @@ Item {
property var pluginDetailInstance: null property var pluginDetailInstance: null
property var widgetModel: null property var widgetModel: null
property var collapseCallback: null
Loader { Loader {
id: pluginDetailLoader id: pluginDetailLoader
@@ -32,6 +33,54 @@ Item {
sourceComponent: null sourceComponent: null
} }
Connections {
target: coreDetailLoader.item
enabled: root.expandedSection.startsWith("brightnessSlider_")
ignoreUnknownSignals: true
function onDeviceNameChanged(newDeviceName) {
if (root.expandedWidgetData && root.expandedWidgetData.id === "brightnessSlider") {
const widgets = SettingsData.controlCenterWidgets || []
const newWidgets = widgets.map(w => {
if (w.id === "brightnessSlider" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w)
updatedWidget.deviceName = newDeviceName
return updatedWidget
}
return w
})
SettingsData.setControlCenterWidgets(newWidgets)
if (root.collapseCallback) {
root.collapseCallback()
}
}
}
}
Connections {
target: coreDetailLoader.item
enabled: root.expandedSection.startsWith("diskUsage_")
ignoreUnknownSignals: true
function 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)
if (root.collapseCallback) {
root.collapseCallback()
}
}
}
}
onExpandedSectionChanged: { onExpandedSectionChanged: {
if (pluginDetailInstance) { if (pluginDetailInstance) {
pluginDetailInstance.destroy() pluginDetailInstance.destroy()
@@ -91,6 +140,12 @@ Item {
return return
} }
if (root.expandedSection.startsWith("brightnessSlider_")) {
coreDetailLoader.sourceComponent = brightnessDetailComponent
coreDetailLoader.active = parent.height > 0
return
}
switch (root.expandedSection) { switch (root.expandedSection) {
case "network": case "network":
case "wifi": coreDetailLoader.sourceComponent = networkDetailComponent; break case "wifi": coreDetailLoader.sourceComponent = networkDetailComponent; break
@@ -144,22 +199,14 @@ Item {
DiskUsageDetail { DiskUsageDetail {
currentMountPath: root.expandedWidgetData?.mountPath || "/" currentMountPath: root.expandedWidgetData?.mountPath || "/"
instanceId: root.expandedWidgetData?.instanceId || "" instanceId: root.expandedWidgetData?.instanceId || ""
}
}
Component {
onMountPathChanged: (newMountPath) => { id: brightnessDetailComponent
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") { BrightnessDetail {
const widgets = SettingsData.controlCenterWidgets || [] currentDeviceName: root.expandedWidgetData?.deviceName || ""
const newWidgets = widgets.map(w => { instanceId: root.expandedWidgetData?.instanceId || ""
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w)
updatedWidget.mountPath = newMountPath
return updatedWidget
}
return w
})
SettingsData.setControlCenterWidgets(newWidgets)
}
}
} }
} }
} }

View File

@@ -20,6 +20,11 @@ Column {
signal removeWidget(int index) signal removeWidget(int index)
signal moveWidget(int fromIndex, int toIndex) signal moveWidget(int fromIndex, int toIndex)
signal toggleWidgetSize(int index) signal toggleWidgetSize(int index)
signal collapseRequested()
function requestCollapse() {
collapseRequested()
}
spacing: editMode ? Theme.spacingL : Theme.spacingS spacing: editMode ? Theme.spacingL : Theme.spacingS
@@ -82,7 +87,7 @@ Column {
const widgets = SettingsData.controlCenterWidgets || [] const widgets = SettingsData.controlCenterWidgets || []
for (var i = 0; i < widgets.length; i++) { for (var i = 0; i < widgets.length; i++) {
if (widgets[i].id === modelData.id) { if (widgets[i].id === modelData.id) {
if (modelData.id === "diskUsage") { if (modelData.id === "diskUsage" || modelData.id === "brightnessSlider") {
if (widgets[i].instanceId === modelData.instanceId) { if (widgets[i].instanceId === modelData.instanceId) {
return i return i
} }
@@ -164,6 +169,11 @@ Column {
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId) return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
} }
if (root.expandedSection.startsWith("brightnessSlider_") && root.expandedWidgetData) {
const expandedInstanceId = root.expandedWidgetData.instanceId
return rowWidgets.some(w => w.id === "brightnessSlider" && w.instanceId === expandedInstanceId)
}
return rowIndex === root.expandedRowIndex return rowIndex === root.expandedRowIndex
} }
visible: active visible: active
@@ -171,6 +181,7 @@ Column {
expandedWidgetData: root.expandedWidgetData expandedWidgetData: root.expandedWidgetData
bluetoothCodecSelector: root.bluetoothCodecSelector bluetoothCodecSelector: root.bluetoothCodecSelector
widgetModel: root.model widgetModel: root.model
collapseCallback: root.requestCollapse
} }
} }
} }
@@ -467,10 +478,19 @@ Column {
height: 16 height: 16
BrightnessSliderRow { BrightnessSliderRow {
id: brightnessSliderRow
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width width: parent.width
height: 14 height: 14
deviceName: widgetData.deviceName || ""
instanceId: widgetData.instanceId || ""
property color sliderTrackColor: Theme.surfaceContainerHigh property color sliderTrackColor: Theme.surfaceContainerHigh
onIconClicked: {
if (!root.editMode && DisplayService.devices && DisplayService.devices.length > 1) {
root.expandClicked(widgetData, widgetIndex)
}
}
} }
} }
} }

View File

@@ -160,6 +160,8 @@ DankPopout {
root.expandedWidgetData = widgetData root.expandedWidgetData = widgetData
if (widgetData.id === "diskUsage") { if (widgetData.id === "diskUsage") {
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default")) root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"))
} else if (widgetData.id === "brightnessSlider") {
root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default"))
} else { } else {
root.toggleSection(widgetData.id) root.toggleSection(widgetData.id)
} }
@@ -167,6 +169,7 @@ DankPopout {
onRemoveWidget: (index) => widgetModel.removeWidget(index) onRemoveWidget: (index) => widgetModel.removeWidget(index)
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex) onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
onToggleWidgetSize: (index) => widgetModel.toggleWidgetSize(index) onToggleWidgetSize: (index) => widgetModel.toggleWidgetSize(index)
onCollapseRequested: root.collapseAll()
} }
EditControls { EditControls {

View File

@@ -0,0 +1,174 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property string currentDeviceName: ""
property string instanceId: ""
signal deviceNameChanged(string newDeviceName)
implicitHeight: brightnessContent.height + Theme.spacingM
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 0
DankFlickable {
id: brightnessContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
contentHeight: brightnessColumn.height
clip: true
Column {
id: brightnessColumn
width: parent.width
spacing: Theme.spacingS
Item {
width: parent.width
height: 100
visible: !DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0
Column {
anchors.centerIn: parent
spacing: Theme.spacingM
DankIcon {
anchors.horizontalCenter: parent.horizontalCenter
name: DisplayService.brightnessAvailable ? "brightness_6" : "error"
size: 32
color: DisplayService.brightnessAvailable ? Theme.primary : Theme.error
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
text: DisplayService.brightnessAvailable ? "No brightness devices available" : "Brightness control not available"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
horizontalAlignment: Text.AlignHCenter
}
}
}
Repeater {
model: DisplayService.devices || []
delegate: Rectangle {
required property var modelData
required property int index
width: parent.width
height: 80
radius: Theme.cornerRadius
color: Theme.surfaceContainerHighest
border.color: modelData.name === currentDeviceName ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: modelData.name === currentDeviceName ? 2 : 0
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingM
spacing: Theme.spacingM
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 2
DankIcon {
name: {
const deviceClass = modelData.class || ""
const deviceName = modelData.name || ""
if (deviceClass === "backlight" || deviceClass === "ddc") {
const brightness = modelData.percentage || 50
if (brightness <= 33) return "brightness_low"
if (brightness <= 66) return "brightness_medium"
return "brightness_high"
} else if (deviceName.includes("kbd")) {
return "keyboard"
} else {
return "lightbulb"
}
}
size: Theme.iconSize
color: modelData.name === currentDeviceName ? Theme.primary : Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: (modelData.percentage || 50) + "%"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - 50 - Theme.spacingM
StyledText {
text: {
const name = modelData.name || ""
const deviceClass = modelData.class || ""
if (deviceClass === "backlight") {
return name.replace("_", " ").replace(/\b\w/g, c => c.toUpperCase())
}
return name
}
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: modelData.name === currentDeviceName ? Font.Medium : Font.Normal
elide: Text.ElideRight
width: parent.width
}
StyledText {
text: modelData.name
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideRight
width: parent.width
}
StyledText {
text: {
const deviceClass = modelData.class || ""
if (deviceClass === "backlight") return "Backlight device"
if (deviceClass === "ddc") return "DDC/CI monitor"
if (deviceClass === "leds") return "LED device"
return deviceClass
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideRight
width: parent.width
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
currentDeviceName = modelData.name
deviceNameChanged(modelData.name)
}
}
}
}
}
}
}

View File

@@ -105,7 +105,8 @@ QtObject {
"icon": "brightness_6", "icon": "brightness_6",
"type": "slider", "type": "slider",
"enabled": DisplayService.brightnessAvailable, "enabled": DisplayService.brightnessAvailable,
"warning": !DisplayService.brightnessAvailable ? "Brightness control not available" : undefined "warning": !DisplayService.brightnessAvailable ? "Brightness control not available" : undefined,
"allowMultiple": true
}, { }, {
"id": "inputVolumeSlider", "id": "inputVolumeSlider",
"text": "Input Volume Slider", "text": "Input Volume Slider",

View File

@@ -8,9 +8,49 @@ import qs.Widgets
Row { Row {
id: root id: root
property string deviceName: ""
property string instanceId: ""
signal iconClicked()
height: 40 height: 40
spacing: 0 spacing: 0
property string targetDeviceName: {
if (!DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0) {
return ""
}
if (deviceName && deviceName.length > 0) {
const found = DisplayService.devices.find(dev => dev.name === deviceName)
return found ? found.name : ""
}
const currentDeviceName = DisplayService.currentDevice
if (currentDeviceName) {
const found = DisplayService.devices.find(dev => dev.name === currentDeviceName)
return found ? found.name : ""
}
return DisplayService.devices.length > 0 ? DisplayService.devices[0].name : ""
}
property var targetDevice: {
if (!targetDeviceName || !DisplayService.devices) {
return null
}
return DisplayService.devices.find(dev => dev.name === targetDeviceName) || null
}
property real targetBrightness: {
if (!targetDeviceName) {
return 0
}
return DisplayService.getDeviceBrightness(targetDeviceName)
}
Rectangle { Rectangle {
width: Theme.iconSize + Theme.spacingS * 2 width: Theme.iconSize + Theme.spacingS * 2
height: Theme.iconSize + Theme.spacingS * 2 height: Theme.iconSize + Theme.spacingS * 2
@@ -24,23 +64,18 @@ Row {
id: iconArea id: iconArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: DisplayService.devices && DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: function(event) { onClicked: {
if (DisplayService.devices.length > 1) { if (DisplayService.devices && DisplayService.devices.length > 1) {
if (deviceMenu.visible) { root.iconClicked()
deviceMenu.close()
} else {
deviceMenu.popup(iconArea, 0, iconArea.height + Theme.spacingXS)
}
event.accepted = true
} }
} }
onEntered: { onEntered: {
tooltipLoader.active = true tooltipLoader.active = true
if (tooltipLoader.item) { if (tooltipLoader.item) {
const tooltipText = DisplayService.currentDevice ? "bl device: " + DisplayService.currentDevice : "Backlight Control" const tooltipText = targetDevice ? "bl device: " + targetDevice.name : "Backlight Control"
const p = iconArea.mapToItem(null, iconArea.width / 2, 0) const p = iconArea.mapToItem(null, iconArea.width / 2, 0)
tooltipLoader.item.show(tooltipText, p.x, p.y - 40, null) tooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
} }
@@ -56,15 +91,23 @@ Row {
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
if (!DisplayService.brightnessAvailable) return "brightness_low" if (!DisplayService.brightnessAvailable || !targetDevice) {
return "brightness_low"
}
let brightness = DisplayService.brightnessLevel if (targetDevice.class === "backlight" || targetDevice.class === "ddc") {
if (brightness <= 33) return "brightness_low" const brightness = targetBrightness
if (brightness <= 66) return "brightness_medium" if (brightness <= 33) return "brightness_low"
return "brightness_high" if (brightness <= 66) return "brightness_medium"
return "brightness_high"
} else if (targetDevice.name.includes("kbd")) {
return "keyboard"
} else {
return "lightbulb"
}
} }
size: Theme.iconSize size: Theme.iconSize
color: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText color: DisplayService.brightnessAvailable && targetDevice && targetBrightness > 0 ? Theme.primary : Theme.surfaceText
} }
} }
} }
@@ -72,88 +115,19 @@ Row {
DankSlider { DankSlider {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
enabled: DisplayService.brightnessAvailable enabled: DisplayService.brightnessAvailable && targetDeviceName.length > 0
minimum: 1 minimum: 1
maximum: 100 maximum: 100
value: { value: targetBrightness
let level = DisplayService.brightnessLevel
if (level > 100) {
let deviceInfo = DisplayService.getCurrentDeviceInfo()
if (deviceInfo && deviceInfo.max > 0) {
return Math.round((level / deviceInfo.max) * 100)
}
return 50
}
return level
}
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
if (DisplayService.brightnessAvailable) { if (DisplayService.brightnessAvailable && targetDeviceName) {
DisplayService.setBrightness(newValue) DisplayService.setBrightness(newValue, targetDeviceName)
} }
} }
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
trackColor: Theme.surfaceContainerHigh trackColor: Theme.surfaceContainerHigh
} }
Menu {
id: deviceMenu
width: 200
closePolicy: Popup.CloseOnEscape
background: Rectangle {
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.width: 0
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
}
Instantiator {
model: DisplayService.devices
delegate: MenuItem {
required property var modelData
required property int index
property string deviceName: modelData.name || ""
property string deviceClass: modelData.class || ""
text: deviceName
font.pixelSize: Theme.fontSizeMedium
height: 40
indicator: Rectangle {
visible: DisplayService.currentDevice === parent.deviceName
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingS
width: 4
height: parent.height - Theme.spacingS * 2
radius: 2
color: Theme.primary
}
contentItem: StyledText {
text: parent.text
font: parent.font
color: DisplayService.currentDevice === parent.deviceName ? Theme.primary : Theme.surfaceText
leftPadding: Theme.spacingL
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
radius: Theme.cornerRadius / 2
}
onTriggered: {
DisplayService.setCurrentDevice(deviceName, true)
deviceMenu.close()
}
}
onObjectAdded: (index, object) => deviceMenu.insertItem(index, object)
onObjectRemoved: (index, object) => deviceMenu.removeItem(object)
}
}
Loader { Loader {
id: tooltipLoader id: tooltipLoader
active: false active: false

View File

@@ -15,6 +15,11 @@ function addWidget(widgetId) {
widget.mountPath = "/" widget.mountPath = "/"
} }
if (widgetId === "brightnessSlider") {
widget.instanceId = generateUniqueId()
widget.deviceName = ""
}
widgets.push(widget) widgets.push(widget)
SettingsData.setControlCenterWidgets(widgets) SettingsData.setControlCenterWidgets(widgets)
} }