1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-04 04:42:05 -04:00
Files
DankMaterialShell/quickshell/Modules/DankBar/Widgets/ControlCenterButton.qml
Ron Harel 7826d827dd feat: add configurable control center group ordering (#2006)
* Add grouped element reordering to control center setting popup.

Reorganize the control center widget menu into grouped rows and add drag handles for reordering.
Introduce controlCenterGroups to drive the grouped popup layout, along with dynamic content width calculation.
Disable dependent options when their parent icon is turned off, and refine DankToggle disabled colors to better distinguish checked and unchecked states.

* Apply Control Center group order to live widget rendering.

Apply persisted `controlCenterGroupOrder` to the actual control center button rendering path instead of only using it in the settings UI.
This refactors `ControlCenterButton.qml` to derive a normalized effective group order, build a small render model from that order, and use model-driven rendering for both vertical and horizontal layouts.

Highlights:
- add a default control center group order and normalize saved order data
- ignore unknown ids, deduplicate duplicates, and append missing known groups
- add shared group visibility helpers and derive a render model from them
- render both vertical and horizontal indicators from the effective order
- preserve existing icon, color, percent text, and visibility behavior
- keep the fallback settings icon outside persisted ordering
- reconnect cached interaction refs for audio, mic, and brightness to the real rendered group containers so wheel and right-click behavior still work
- clear and refresh interaction refs on orientation, visibility, and delegate lifecycle changes
- tighten horizontal composite group sizing by measuring actual rendered content, fixing extra spacing around the audio indicator

Also updates the settings widgets UI to persist and restore control center group ordering consistently with the live control center rendering.
2026-03-16 11:11:26 -04:00

831 lines
36 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Modules.Plugins
import qs.Services
import qs.Widgets
BasePill {
id: root
property bool isActive: false
property var popoutTarget: null
property var widgetData: null
property string screenName: ""
property string screenModel: ""
property bool showNetworkIcon: widgetData?.showNetworkIcon !== undefined ? widgetData.showNetworkIcon : SettingsData.controlCenterShowNetworkIcon
property bool showBluetoothIcon: widgetData?.showBluetoothIcon !== undefined ? widgetData.showBluetoothIcon : SettingsData.controlCenterShowBluetoothIcon
property bool showAudioIcon: widgetData?.showAudioIcon !== undefined ? widgetData.showAudioIcon : SettingsData.controlCenterShowAudioIcon
property bool showAudioPercent: widgetData?.showAudioPercent !== undefined ? widgetData.showAudioPercent : SettingsData.controlCenterShowAudioPercent
property bool showVpnIcon: widgetData?.showVpnIcon !== undefined ? widgetData.showVpnIcon : SettingsData.controlCenterShowVpnIcon
property bool showBrightnessIcon: widgetData?.showBrightnessIcon !== undefined ? widgetData.showBrightnessIcon : SettingsData.controlCenterShowBrightnessIcon
property bool showBrightnessPercent: widgetData?.showBrightnessPercent !== undefined ? widgetData.showBrightnessPercent : SettingsData.controlCenterShowBrightnessPercent
property bool showMicIcon: widgetData?.showMicIcon !== undefined ? widgetData.showMicIcon : SettingsData.controlCenterShowMicIcon
property bool showMicPercent: widgetData?.showMicPercent !== undefined ? widgetData.showMicPercent : SettingsData.controlCenterShowMicPercent
property bool showBatteryIcon: widgetData?.showBatteryIcon !== undefined ? widgetData.showBatteryIcon : SettingsData.controlCenterShowBatteryIcon
property bool showPrinterIcon: widgetData?.showPrinterIcon !== undefined ? widgetData.showPrinterIcon : SettingsData.controlCenterShowPrinterIcon
property bool showScreenSharingIcon: widgetData?.showScreenSharingIcon !== undefined ? widgetData.showScreenSharingIcon : SettingsData.controlCenterShowScreenSharingIcon
property real touchpadThreshold: 100
property real micAccumulator: 0
property real volumeAccumulator: 0
property real brightnessAccumulator: 0
readonly property real vIconSize: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
property var _hRow: null
property var _vCol: null
property var _hAudio: null
property var _hBrightness: null
property var _hMic: null
property var _vAudio: null
property var _vBrightness: null
property var _vMic: null
property var _interactionDelegates: []
readonly property var defaultControlCenterGroupOrder: ["network", "vpn", "bluetooth", "audio", "microphone", "brightness", "battery", "printer", "screenSharing"]
readonly property var effectiveControlCenterGroupOrder: getEffectiveControlCenterGroupOrder()
readonly property var controlCenterRenderModel: getControlCenterRenderModel()
onIsVerticalOrientationChanged: root.clearInteractionRefs()
onWheel: function (wheelEvent) {
const delta = wheelEvent.angleDelta.y;
if (delta === 0)
return;
root.refreshInteractionRefs();
const rootX = wheelEvent.x - root.leftMargin;
const rootY = wheelEvent.y - root.topMargin;
if (root.isVerticalOrientation && _vCol) {
const pos = root.mapToItem(_vCol, rootX, rootY);
if (_vBrightness?.visible && pos.y >= _vBrightness.y && pos.y < _vBrightness.y + _vBrightness.height) {
root.handleBrightnessWheel(delta);
} else if (_vMic?.visible && pos.y >= _vMic.y && pos.y < _vMic.y + _vMic.height) {
root.handleMicWheel(delta);
} else {
root.handleVolumeWheel(delta);
}
} else if (_hRow) {
const pos = root.mapToItem(_hRow, rootX, rootY);
if (_hBrightness?.visible && pos.x >= _hBrightness.x && pos.x < _hBrightness.x + _hBrightness.width) {
root.handleBrightnessWheel(delta);
} else if (_hMic?.visible && pos.x >= _hMic.x && pos.x < _hMic.x + _hMic.width) {
root.handleMicWheel(delta);
} else {
root.handleVolumeWheel(delta);
}
} else {
root.handleVolumeWheel(delta);
}
wheelEvent.accepted = true;
}
onRightClicked: function (rootX, rootY) {
root.refreshInteractionRefs();
if (root.isVerticalOrientation && _vCol) {
const pos = root.mapToItem(_vCol, rootX, rootY);
if (_vAudio?.visible && pos.y >= _vAudio.y && pos.y < _vAudio.y + _vAudio.height) {
AudioService.toggleMute();
return;
}
if (_vMic?.visible && pos.y >= _vMic.y && pos.y < _vMic.y + _vMic.height) {
AudioService.toggleMicMute();
return;
}
} else if (_hRow) {
const pos = root.mapToItem(_hRow, rootX, rootY);
if (_hAudio?.visible && pos.x >= _hAudio.x && pos.x < _hAudio.x + _hAudio.width) {
AudioService.toggleMute();
return;
}
if (_hMic?.visible && pos.x >= _hMic.x && pos.x < _hMic.x + _hMic.width) {
AudioService.toggleMicMute();
return;
}
}
}
Loader {
active: root.showPrinterIcon
sourceComponent: Component {
Ref {
service: CupsService
}
}
}
function getNetworkIconName() {
if (NetworkService.wifiToggling)
return "sync";
switch (NetworkService.networkStatus) {
case "ethernet":
return "lan";
case "vpn":
return NetworkService.ethernetConnected ? "lan" : NetworkService.wifiSignalIcon;
default:
return NetworkService.wifiSignalIcon;
}
}
function getNetworkIconColor() {
if (NetworkService.wifiToggling)
return Theme.primary;
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.surfaceText;
}
function getVolumeIconName() {
if (!AudioService.sink?.audio)
return "volume_up";
if (AudioService.sink.audio.muted)
return "volume_off";
if (AudioService.sink.audio.volume === 0)
return "volume_mute";
if (AudioService.sink.audio.volume * 100 < 33)
return "volume_down";
return "volume_up";
}
function getMicIconName() {
if (!AudioService.source?.audio)
return "mic";
if (AudioService.source.audio.muted || AudioService.source.audio.volume === 0)
return "mic_off";
return "mic";
}
function getMicIconColor() {
if (!AudioService.source?.audio)
return Theme.surfaceText;
if (AudioService.source.audio.muted || AudioService.source.audio.volume === 0)
return Theme.surfaceText;
return Theme.widgetIconColor;
}
function getBrightnessIconName() {
const deviceName = getPinnedBrightnessDevice();
if (!deviceName)
return "brightness_medium";
const level = DisplayService.getDeviceBrightness(deviceName);
if (level <= 33)
return "brightness_low";
if (level <= 66)
return "brightness_medium";
return "brightness_high";
}
function getScreenPinKey() {
if (!root.screenName)
return "";
const screen = Quickshell.screens.find(s => s.name === root.screenName);
if (screen) {
return SettingsData.getScreenDisplayName(screen);
}
if (SettingsData.displayNameMode === "model" && root.screenModel && root.screenModel.length > 0) {
return root.screenModel;
}
return root.screenName;
}
function getPinnedBrightnessDevice() {
const pinKey = getScreenPinKey();
if (!pinKey)
return "";
const pins = SettingsData.brightnessDevicePins || {};
return pins[pinKey] || "";
}
function hasPinnedBrightnessDevice() {
return getPinnedBrightnessDevice().length > 0;
}
function handleVolumeWheel(delta) {
if (!AudioService.sink?.audio)
return;
var step = 5;
const isMouseWheel = Math.abs(delta) >= 120 && (Math.abs(delta) % 120) === 0;
if (!isMouseWheel) {
step = 1;
volumeAccumulator += delta;
if (Math.abs(volumeAccumulator) < touchpadThreshold)
return;
delta = volumeAccumulator;
volumeAccumulator = 0;
}
const maxVol = AudioService.sinkMaxVolume;
const currentVolume = AudioService.sink.audio.volume * 100;
const newVolume = delta > 0 ? Math.min(maxVol, currentVolume + step) : Math.max(0, currentVolume - step);
AudioService.sink.audio.muted = false;
AudioService.sink.audio.volume = newVolume / 100;
AudioService.playVolumeChangeSoundIfEnabled();
}
function handleMicWheel(delta) {
if (!AudioService.source?.audio)
return;
var step = 5;
const isMouseWheel = Math.abs(delta) >= 120 && (Math.abs(delta) % 120) === 0;
if (!isMouseWheel) {
step = 1;
micAccumulator += delta;
if (Math.abs(micAccumulator) < touchpadThreshold)
return;
delta = micAccumulator;
micAccumulator = 0;
}
const currentVolume = AudioService.source.audio.volume * 100;
const newVolume = delta > 0 ? Math.min(100, currentVolume + step) : Math.max(0, currentVolume - step);
AudioService.source.audio.muted = false;
AudioService.source.audio.volume = newVolume / 100;
}
function handleBrightnessWheel(delta) {
const deviceName = getPinnedBrightnessDevice();
if (!deviceName) {
return;
}
var step = 5;
const isMouseWheel = Math.abs(delta) >= 120 && (Math.abs(delta) % 120) === 0;
if (!isMouseWheel) {
step = 1;
brightnessAccumulator += delta;
if (Math.abs(brightnessAccumulator) < touchpadThreshold)
return;
delta = brightnessAccumulator;
brightnessAccumulator = 0;
}
const currentBrightness = DisplayService.getDeviceBrightness(deviceName);
const newBrightness = delta > 0 ? Math.min(100, currentBrightness + step) : Math.max(1, currentBrightness - step);
DisplayService.setBrightness(newBrightness, deviceName);
}
function getBrightness() {
const deviceName = getPinnedBrightnessDevice();
if (!deviceName) {
return;
}
return DisplayService.getDeviceBrightness(deviceName) / 100;
}
function getBatteryIconColor() {
if (!BatteryService.batteryAvailable)
return Theme.widgetIconColor;
if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.error;
if (BatteryService.isCharging || BatteryService.isPluggedIn)
return Theme.primary;
return Theme.widgetIconColor;
}
function hasPrintJobs() {
return CupsService.getTotalJobsNum() > 0;
}
function getControlCenterIconSize() {
return Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale);
}
function getEffectiveControlCenterGroupOrder() {
const knownIds = root.defaultControlCenterGroupOrder;
const savedOrder = root.widgetData?.controlCenterGroupOrder;
const result = [];
const seen = {};
if (savedOrder && typeof savedOrder.length === "number") {
for (let i = 0; i < savedOrder.length; ++i) {
const groupId = savedOrder[i];
if (knownIds.indexOf(groupId) === -1 || seen[groupId])
continue;
seen[groupId] = true;
result.push(groupId);
}
}
for (let i = 0; i < knownIds.length; ++i) {
const groupId = knownIds[i];
if (seen[groupId])
continue;
seen[groupId] = true;
result.push(groupId);
}
return result;
}
function isGroupVisible(groupId) {
switch (groupId) {
case "screenSharing":
return root.showScreenSharingIcon && NiriService.hasCasts;
case "network":
return root.showNetworkIcon && NetworkService.networkAvailable;
case "vpn":
return root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected;
case "bluetooth":
return root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled;
case "audio":
return root.showAudioIcon;
case "microphone":
return root.showMicIcon;
case "brightness":
return root.showBrightnessIcon && DisplayService.brightnessAvailable && root.hasPinnedBrightnessDevice();
case "battery":
return root.showBatteryIcon && BatteryService.batteryAvailable;
case "printer":
return root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs();
default:
return false;
}
}
function isCompositeGroup(groupId) {
return groupId === "audio" || groupId === "microphone" || groupId === "brightness";
}
function getControlCenterRenderModel() {
return root.effectiveControlCenterGroupOrder.map(groupId => ({
"id": groupId,
"visible": root.isGroupVisible(groupId),
"composite": root.isCompositeGroup(groupId)
}));
}
function clearInteractionRefs() {
root._hAudio = null;
root._hBrightness = null;
root._hMic = null;
root._vAudio = null;
root._vBrightness = null;
root._vMic = null;
}
function registerInteractionDelegate(isVertical, item) {
if (!item)
return;
for (let i = 0; i < root._interactionDelegates.length; ++i) {
const entry = root._interactionDelegates[i];
if (entry && entry.item === item) {
entry.isVertical = isVertical;
return;
}
}
root._interactionDelegates = root._interactionDelegates.concat([
{
"isVertical": isVertical,
"item": item
}
]);
}
function unregisterInteractionDelegate(item) {
if (!item)
return;
root._interactionDelegates = root._interactionDelegates.filter(entry => entry && entry.item !== item);
}
function refreshInteractionRefs() {
root.clearInteractionRefs();
for (let i = 0; i < root._interactionDelegates.length; ++i) {
const entry = root._interactionDelegates[i];
const item = entry?.item;
if (!item || !item.visible)
continue;
const groupId = item.interactionGroupId;
if (entry.isVertical) {
if (groupId === "audio")
root._vAudio = item;
else if (groupId === "microphone")
root._vMic = item;
else if (groupId === "brightness")
root._vBrightness = item;
} else {
if (groupId === "audio")
root._hAudio = item;
else if (groupId === "microphone")
root._hMic = item;
else if (groupId === "brightness")
root._hBrightness = item;
}
}
}
function hasNoVisibleIcons() {
return !root.controlCenterRenderModel.some(entry => entry.visible);
}
content: Component {
Item {
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : controlIndicators.implicitWidth
implicitHeight: root.isVerticalOrientation ? controlColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
Component.onCompleted: {
root._hRow = controlIndicators;
root._vCol = controlColumn;
root.clearInteractionRefs();
}
Column {
id: controlColumn
visible: root.isVerticalOrientation
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXS
Repeater {
model: root.controlCenterRenderModel
Item {
id: verticalGroupItem
required property var modelData
required property int index
property string interactionGroupId: modelData.id
width: parent.width
height: {
switch (modelData.id) {
case "audio":
return root.vIconSize + (root.showAudioPercent ? audioPercentV.implicitHeight + 2 : 0);
case "microphone":
return root.vIconSize + (root.showMicPercent ? micPercentV.implicitHeight + 2 : 0);
case "brightness":
return root.vIconSize + (root.showBrightnessPercent ? brightnessPercentV.implicitHeight + 2 : 0);
default:
return root.vIconSize;
}
}
visible: modelData.visible
Component.onCompleted: {
root.registerInteractionDelegate(true, verticalGroupItem);
root.refreshInteractionRefs();
}
Component.onDestruction: {
if (root) {
root.unregisterInteractionDelegate(verticalGroupItem);
root.refreshInteractionRefs();
}
}
onVisibleChanged: root.refreshInteractionRefs()
onInteractionGroupIdChanged: {
root.refreshInteractionRefs();
}
DankIcon {
anchors.centerIn: parent
visible: !verticalGroupItem.modelData.composite
name: {
switch (verticalGroupItem.modelData.id) {
case "screenSharing":
return "screen_record";
case "network":
return root.getNetworkIconName();
case "vpn":
return "vpn_lock";
case "bluetooth":
return "bluetooth";
case "battery":
return Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable);
case "printer":
return "print";
default:
return "settings";
}
}
size: root.vIconSize
color: {
switch (verticalGroupItem.modelData.id) {
case "screenSharing":
return NiriService.hasActiveCast ? Theme.primary : Theme.surfaceText;
case "network":
return root.getNetworkIconColor();
case "vpn":
return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText;
case "bluetooth":
return BluetoothService.connected ? Theme.primary : Theme.surfaceText;
case "battery":
return root.getBatteryIconColor();
case "printer":
return Theme.primary;
default:
return Theme.widgetIconColor;
}
}
}
DankIcon {
id: audioIconV
visible: verticalGroupItem.modelData.id === "audio"
name: root.getVolumeIconName()
size: root.vIconSize
color: Theme.widgetIconColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
}
NumericText {
id: audioPercentV
visible: verticalGroupItem.modelData.id === "audio" && root.showAudioPercent
text: Math.round((AudioService.sink?.audio?.volume ?? 0) * 100) + "%"
reserveText: "100%"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: audioIconV.bottom
anchors.topMargin: 2
}
DankIcon {
id: micIconV
visible: verticalGroupItem.modelData.id === "microphone"
name: root.getMicIconName()
size: root.vIconSize
color: root.getMicIconColor()
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
}
NumericText {
id: micPercentV
visible: verticalGroupItem.modelData.id === "microphone" && root.showMicPercent
text: Math.round((AudioService.source?.audio?.volume ?? 0) * 100) + "%"
reserveText: "100%"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: micIconV.bottom
anchors.topMargin: 2
}
DankIcon {
id: brightnessIconV
visible: verticalGroupItem.modelData.id === "brightness"
name: root.getBrightnessIconName()
size: root.vIconSize
color: Theme.widgetIconColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
}
NumericText {
id: brightnessPercentV
visible: verticalGroupItem.modelData.id === "brightness" && root.showBrightnessPercent
text: Math.round(getBrightness() * 100) + "%"
reserveText: "100%"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: brightnessIconV.bottom
anchors.topMargin: 2
}
}
}
Item {
width: parent.width
height: root.vIconSize
visible: root.hasNoVisibleIcons()
DankIcon {
name: "settings"
size: root.vIconSize
color: root.isActive ? Theme.primary : Theme.widgetIconColor
anchors.centerIn: parent
}
}
}
Row {
id: controlIndicators
visible: !root.isVerticalOrientation
anchors.centerIn: parent
spacing: Theme.spacingXS
Repeater {
model: root.controlCenterRenderModel
Item {
id: horizontalGroupItem
required property var modelData
required property int index
property string interactionGroupId: modelData.id
width: {
switch (modelData.id) {
case "audio":
return audioGroup.width;
case "microphone":
return micGroup.width;
case "brightness":
return brightnessGroup.width;
default:
return root.getControlCenterIconSize();
}
}
implicitWidth: width
height: root.widgetThickness - root.horizontalPadding * 2
visible: modelData.visible
Component.onCompleted: {
root.registerInteractionDelegate(false, horizontalGroupItem);
root.refreshInteractionRefs();
}
Component.onDestruction: {
if (root) {
root.unregisterInteractionDelegate(horizontalGroupItem);
root.refreshInteractionRefs();
}
}
onVisibleChanged: root.refreshInteractionRefs()
onInteractionGroupIdChanged: {
root.refreshInteractionRefs();
}
DankIcon {
id: iconOnlyItem
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
visible: !horizontalGroupItem.modelData.composite
name: {
switch (horizontalGroupItem.modelData.id) {
case "screenSharing":
return "screen_record";
case "network":
return root.getNetworkIconName();
case "vpn":
return "vpn_lock";
case "bluetooth":
return "bluetooth";
case "battery":
return Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable);
case "printer":
return "print";
default:
return "settings";
}
}
size: root.getControlCenterIconSize()
color: {
switch (horizontalGroupItem.modelData.id) {
case "screenSharing":
return NiriService.hasActiveCast ? Theme.primary : Theme.surfaceText;
case "network":
return root.getNetworkIconColor();
case "vpn":
return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText;
case "bluetooth":
return BluetoothService.connected ? Theme.primary : Theme.surfaceText;
case "battery":
return root.getBatteryIconColor();
case "printer":
return Theme.primary;
default:
return Theme.widgetIconColor;
}
}
}
Rectangle {
id: audioGroup
width: audioContent.implicitWidth + 2
implicitWidth: width
height: parent.height
color: "transparent"
anchors.verticalCenter: parent.verticalCenter
visible: horizontalGroupItem.modelData.id === "audio"
Row {
id: audioContent
anchors.left: parent.left
anchors.leftMargin: 1
anchors.verticalCenter: parent.verticalCenter
spacing: 2
DankIcon {
id: audioIcon
name: root.getVolumeIconName()
size: root.getControlCenterIconSize()
color: Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter
}
NumericText {
id: audioPercent
visible: root.showAudioPercent
text: Math.round((AudioService.sink?.audio?.volume ?? 0) * 100) + "%"
reserveText: "100%"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.verticalCenter: parent.verticalCenter
width: visible ? implicitWidth : 0
}
}
}
Rectangle {
id: micGroup
width: micContent.implicitWidth + 2
implicitWidth: width
height: parent.height
color: "transparent"
anchors.verticalCenter: parent.verticalCenter
visible: horizontalGroupItem.modelData.id === "microphone"
Row {
id: micContent
anchors.left: parent.left
anchors.leftMargin: 1
anchors.verticalCenter: parent.verticalCenter
spacing: 2
DankIcon {
id: micIcon
name: root.getMicIconName()
size: root.getControlCenterIconSize()
color: root.getMicIconColor()
anchors.verticalCenter: parent.verticalCenter
}
NumericText {
id: micPercent
visible: root.showMicPercent
text: Math.round((AudioService.source?.audio?.volume ?? 0) * 100) + "%"
reserveText: "100%"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.verticalCenter: parent.verticalCenter
width: visible ? implicitWidth : 0
}
}
}
Rectangle {
id: brightnessGroup
width: brightnessContent.implicitWidth + 2
implicitWidth: width
height: parent.height
color: "transparent"
anchors.verticalCenter: parent.verticalCenter
visible: horizontalGroupItem.modelData.id === "brightness"
Row {
id: brightnessContent
anchors.left: parent.left
anchors.leftMargin: 1
anchors.verticalCenter: parent.verticalCenter
spacing: 2
DankIcon {
id: brightnessIcon
name: root.getBrightnessIconName()
size: root.getControlCenterIconSize()
color: Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter
}
NumericText {
id: brightnessPercent
visible: root.showBrightnessPercent
text: Math.round(getBrightness() * 100) + "%"
reserveText: "100%"
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
color: Theme.widgetTextColor
anchors.verticalCenter: parent.verticalCenter
width: visible ? implicitWidth : 0
}
}
}
}
}
DankIcon {
name: "settings"
size: root.getControlCenterIconSize()
color: root.isActive ? Theme.primary : Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter
visible: root.hasNoVisibleIcons()
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.NoButton
}
}
}
}