mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-14 17:52:10 -04:00
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.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
@@ -38,12 +39,20 @@ BasePill {
|
|||||||
property var _vAudio: null
|
property var _vAudio: null
|
||||||
property var _vBrightness: null
|
property var _vBrightness: null
|
||||||
property var _vMic: 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) {
|
onWheel: function (wheelEvent) {
|
||||||
const delta = wheelEvent.angleDelta.y;
|
const delta = wheelEvent.angleDelta.y;
|
||||||
if (delta === 0)
|
if (delta === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
root.refreshInteractionRefs();
|
||||||
|
|
||||||
const rootX = wheelEvent.x - root.leftMargin;
|
const rootX = wheelEvent.x - root.leftMargin;
|
||||||
const rootY = wheelEvent.y - root.topMargin;
|
const rootY = wheelEvent.y - root.topMargin;
|
||||||
|
|
||||||
@@ -72,6 +81,8 @@ BasePill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRightClicked: function (rootX, rootY) {
|
onRightClicked: function (rootX, rootY) {
|
||||||
|
root.refreshInteractionRefs();
|
||||||
|
|
||||||
if (root.isVerticalOrientation && _vCol) {
|
if (root.isVerticalOrientation && _vCol) {
|
||||||
const pos = root.mapToItem(_vCol, rootX, rootY);
|
const pos = root.mapToItem(_vCol, rootX, rootY);
|
||||||
if (_vAudio?.visible && pos.y >= _vAudio.y && pos.y < _vAudio.y + _vAudio.height) {
|
if (_vAudio?.visible && pos.y >= _vAudio.y && pos.y < _vAudio.y + _vAudio.height) {
|
||||||
@@ -279,26 +290,142 @@ BasePill {
|
|||||||
return CupsService.getTotalJobsNum() > 0;
|
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() {
|
function hasNoVisibleIcons() {
|
||||||
if (root.showScreenSharingIcon && NiriService.hasCasts)
|
return !root.controlCenterRenderModel.some(entry => entry.visible);
|
||||||
return false;
|
|
||||||
if (root.showNetworkIcon && NetworkService.networkAvailable)
|
|
||||||
return false;
|
|
||||||
if (root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected)
|
|
||||||
return false;
|
|
||||||
if (root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled)
|
|
||||||
return false;
|
|
||||||
if (root.showAudioIcon)
|
|
||||||
return false;
|
|
||||||
if (root.showMicIcon)
|
|
||||||
return false;
|
|
||||||
if (root.showBrightnessIcon && DisplayService.brightnessAvailable && root.hasPinnedBrightnessDevice())
|
|
||||||
return false;
|
|
||||||
if (root.showBatteryIcon && BatteryService.batteryAvailable)
|
|
||||||
return false;
|
|
||||||
if (root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs())
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
@@ -309,12 +436,7 @@ BasePill {
|
|||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
root._hRow = controlIndicators;
|
root._hRow = controlIndicators;
|
||||||
root._vCol = controlColumn;
|
root._vCol = controlColumn;
|
||||||
root._hAudio = audioIcon.parent;
|
root.clearInteractionRefs();
|
||||||
root._hBrightness = brightnessIcon.parent;
|
|
||||||
root._hMic = micIcon.parent;
|
|
||||||
root._vAudio = audioIconV.parent;
|
|
||||||
root._vBrightness = brightnessIconV.parent;
|
|
||||||
root._vMic = micIconV.parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -324,162 +446,151 @@ BasePill {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Item {
|
Repeater {
|
||||||
width: parent.width
|
model: root.controlCenterRenderModel
|
||||||
height: root.vIconSize
|
Item {
|
||||||
visible: root.showScreenSharingIcon && NiriService.hasCasts
|
id: verticalGroupItem
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
property string interactionGroupId: modelData.id
|
||||||
|
|
||||||
DankIcon {
|
width: parent.width
|
||||||
name: "screen_record"
|
height: {
|
||||||
size: root.vIconSize
|
switch (modelData.id) {
|
||||||
color: NiriService.hasActiveCast ? Theme.primary : Theme.surfaceText
|
case "audio":
|
||||||
anchors.centerIn: parent
|
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
|
||||||
|
|
||||||
Item {
|
Component.onCompleted: {
|
||||||
width: parent.width
|
root.registerInteractionDelegate(true, verticalGroupItem);
|
||||||
height: root.vIconSize
|
root.refreshInteractionRefs();
|
||||||
visible: root.showNetworkIcon && NetworkService.networkAvailable
|
}
|
||||||
|
Component.onDestruction: {
|
||||||
|
if (root) {
|
||||||
|
root.unregisterInteractionDelegate(verticalGroupItem);
|
||||||
|
root.refreshInteractionRefs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onVisibleChanged: root.refreshInteractionRefs()
|
||||||
|
onInteractionGroupIdChanged: {
|
||||||
|
root.refreshInteractionRefs();
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: root.getNetworkIconName()
|
anchors.centerIn: parent
|
||||||
size: root.vIconSize
|
visible: !verticalGroupItem.modelData.composite
|
||||||
color: root.getNetworkIconColor()
|
name: {
|
||||||
anchors.centerIn: parent
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
DankIcon {
|
||||||
width: parent.width
|
id: audioIconV
|
||||||
height: root.vIconSize
|
visible: verticalGroupItem.modelData.id === "audio"
|
||||||
visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected
|
name: root.getVolumeIconName()
|
||||||
|
size: root.vIconSize
|
||||||
|
color: Theme.widgetIconColor
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
NumericText {
|
||||||
name: "vpn_lock"
|
id: audioPercentV
|
||||||
size: root.vIconSize
|
visible: verticalGroupItem.modelData.id === "audio" && root.showAudioPercent
|
||||||
color: NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText
|
text: Math.round((AudioService.sink?.audio?.volume ?? 0) * 100) + "%"
|
||||||
anchors.centerIn: parent
|
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
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
DankIcon {
|
||||||
width: parent.width
|
id: micIconV
|
||||||
height: root.vIconSize
|
visible: verticalGroupItem.modelData.id === "microphone"
|
||||||
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
|
name: root.getMicIconName()
|
||||||
|
size: root.vIconSize
|
||||||
|
color: root.getMicIconColor()
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
NumericText {
|
||||||
name: "bluetooth"
|
id: micPercentV
|
||||||
size: root.vIconSize
|
visible: verticalGroupItem.modelData.id === "microphone" && root.showMicPercent
|
||||||
color: BluetoothService.connected ? Theme.primary : Theme.surfaceText
|
text: Math.round((AudioService.source?.audio?.volume ?? 0) * 100) + "%"
|
||||||
anchors.centerIn: parent
|
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
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
DankIcon {
|
||||||
width: parent.width
|
id: brightnessIconV
|
||||||
height: root.vIconSize + (root.showAudioPercent ? audioPercentV.implicitHeight + 2 : 0)
|
visible: verticalGroupItem.modelData.id === "brightness"
|
||||||
visible: root.showAudioIcon
|
name: root.getBrightnessIconName()
|
||||||
|
size: root.vIconSize
|
||||||
|
color: Theme.widgetIconColor
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
NumericText {
|
||||||
id: audioIconV
|
id: brightnessPercentV
|
||||||
name: root.getVolumeIconName()
|
visible: verticalGroupItem.modelData.id === "brightness" && root.showBrightnessPercent
|
||||||
size: root.vIconSize
|
text: Math.round(getBrightness() * 100) + "%"
|
||||||
color: Theme.widgetIconColor
|
reserveText: "100%"
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||||
anchors.top: parent.top
|
color: Theme.widgetTextColor
|
||||||
}
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: brightnessIconV.bottom
|
||||||
NumericText {
|
anchors.topMargin: 2
|
||||||
id: audioPercentV
|
}
|
||||||
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.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: audioIconV.bottom
|
|
||||||
anchors.topMargin: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: root.vIconSize + (root.showMicPercent ? micPercentV.implicitHeight + 2 : 0)
|
|
||||||
visible: root.showMicIcon
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: micIconV
|
|
||||||
name: root.getMicIconName()
|
|
||||||
size: root.vIconSize
|
|
||||||
color: root.getMicIconColor()
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
}
|
|
||||||
|
|
||||||
NumericText {
|
|
||||||
id: micPercentV
|
|
||||||
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.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: micIconV.bottom
|
|
||||||
anchors.topMargin: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: root.vIconSize + (root.showBrightnessPercent ? brightnessPercentV.implicitHeight + 2 : 0)
|
|
||||||
visible: root.showBrightnessIcon && DisplayService.brightnessAvailable && root.hasPinnedBrightnessDevice()
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: brightnessIconV
|
|
||||||
name: root.getBrightnessIconName()
|
|
||||||
size: root.vIconSize
|
|
||||||
color: Theme.widgetIconColor
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
}
|
|
||||||
|
|
||||||
NumericText {
|
|
||||||
id: brightnessPercentV
|
|
||||||
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.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: brightnessIconV.bottom
|
|
||||||
anchors.topMargin: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: root.vIconSize
|
|
||||||
visible: root.showBatteryIcon && BatteryService.batteryAvailable
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
|
|
||||||
size: root.vIconSize
|
|
||||||
color: root.getBatteryIconColor()
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: root.vIconSize
|
|
||||||
visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs()
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "print"
|
|
||||||
size: root.vIconSize
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,157 +614,206 @@ BasePill {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
DankIcon {
|
Repeater {
|
||||||
name: "screen_record"
|
model: root.controlCenterRenderModel
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
|
||||||
color: NiriService.hasActiveCast ? Theme.primary : Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showScreenSharingIcon && NiriService.hasCasts
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
Item {
|
||||||
id: networkIcon
|
id: horizontalGroupItem
|
||||||
name: root.getNetworkIconName()
|
required property var modelData
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
required property int index
|
||||||
color: root.getNetworkIconColor()
|
property string interactionGroupId: modelData.id
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showNetworkIcon && NetworkService.networkAvailable
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
width: {
|
||||||
id: vpnIcon
|
switch (modelData.id) {
|
||||||
name: "vpn_lock"
|
case "audio":
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
return audioGroup.width;
|
||||||
color: NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText
|
case "microphone":
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
return micGroup.width;
|
||||||
visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected
|
case "brightness":
|
||||||
}
|
return brightnessGroup.width;
|
||||||
|
default:
|
||||||
|
return root.getControlCenterIconSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
implicitWidth: width
|
||||||
|
height: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
visible: modelData.visible
|
||||||
|
|
||||||
DankIcon {
|
Component.onCompleted: {
|
||||||
id: bluetoothIcon
|
root.registerInteractionDelegate(false, horizontalGroupItem);
|
||||||
name: "bluetooth"
|
root.refreshInteractionRefs();
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
}
|
||||||
color: BluetoothService.connected ? Theme.primary : Theme.surfaceText
|
Component.onDestruction: {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
if (root) {
|
||||||
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
|
root.unregisterInteractionDelegate(horizontalGroupItem);
|
||||||
}
|
root.refreshInteractionRefs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onVisibleChanged: root.refreshInteractionRefs()
|
||||||
|
onInteractionGroupIdChanged: {
|
||||||
|
root.refreshInteractionRefs();
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
DankIcon {
|
||||||
width: audioIcon.implicitWidth + (root.showAudioPercent ? audioPercent.reservedWidth : 0) + 4
|
id: iconOnlyItem
|
||||||
implicitWidth: width
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: root.widgetThickness - root.horizontalPadding * 2
|
anchors.left: parent.left
|
||||||
color: "transparent"
|
visible: !horizontalGroupItem.modelData.composite
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
name: {
|
||||||
visible: root.showAudioIcon
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
Rectangle {
|
||||||
id: audioIcon
|
id: audioGroup
|
||||||
name: root.getVolumeIconName()
|
width: audioContent.implicitWidth + 2
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
implicitWidth: width
|
||||||
color: Theme.widgetIconColor
|
height: parent.height
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
color: "transparent"
|
||||||
anchors.left: parent.left
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: 2
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
anchors.left: audioIcon.right
|
|
||||||
anchors.leftMargin: 2
|
|
||||||
width: reservedWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: micIcon.implicitWidth + (root.showMicPercent ? micPercent.reservedWidth : 0) + 4
|
|
||||||
implicitWidth: width
|
|
||||||
height: root.widgetThickness - root.horizontalPadding * 2
|
|
||||||
color: "transparent"
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showMicIcon
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: micIcon
|
|
||||||
name: root.getMicIconName()
|
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
|
||||||
color: root.getMicIconColor()
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
anchors.left: micIcon.right
|
|
||||||
anchors.leftMargin: 2
|
|
||||||
width: reservedWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: brightnessIcon.implicitWidth + (root.showBrightnessPercent ? brightnessPercent.reservedWidth : 0) + 4
|
|
||||||
height: root.widgetThickness - root.horizontalPadding * 2
|
|
||||||
color: "transparent"
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showBrightnessIcon && DisplayService.brightnessAvailable && root.hasPinnedBrightnessDevice()
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: brightnessIcon
|
|
||||||
name: root.getBrightnessIconName()
|
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
|
||||||
color: Theme.widgetIconColor
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
anchors.left: brightnessIcon.right
|
|
||||||
anchors.leftMargin: 2
|
|
||||||
width: reservedWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: batteryIcon
|
|
||||||
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
|
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
|
||||||
color: root.getBatteryIconColor()
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showBatteryIcon && BatteryService.batteryAvailable
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: printerIcon
|
|
||||||
name: "print"
|
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: "settings"
|
name: "settings"
|
||||||
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
size: root.getControlCenterIconSize()
|
||||||
color: root.isActive ? Theme.primary : Theme.widgetIconColor
|
color: root.isActive ? Theme.primary : Theme.widgetIconColor
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: root.hasNoVisibleIcons()
|
visible: root.hasNoVisibleIcons()
|
||||||
|
|||||||
@@ -391,6 +391,7 @@ Item {
|
|||||||
widgetObj.showBatteryIcon = SettingsData.controlCenterShowBatteryIcon;
|
widgetObj.showBatteryIcon = SettingsData.controlCenterShowBatteryIcon;
|
||||||
widgetObj.showPrinterIcon = SettingsData.controlCenterShowPrinterIcon;
|
widgetObj.showPrinterIcon = SettingsData.controlCenterShowPrinterIcon;
|
||||||
widgetObj.showScreenSharingIcon = SettingsData.controlCenterShowScreenSharingIcon;
|
widgetObj.showScreenSharingIcon = SettingsData.controlCenterShowScreenSharingIcon;
|
||||||
|
widgetObj.controlCenterGroupOrder = ["network", "vpn", "bluetooth", "audio", "microphone", "brightness", "battery", "printer", "screenSharing"];
|
||||||
}
|
}
|
||||||
if (widgetId === "runningApps") {
|
if (widgetId === "runningApps") {
|
||||||
widgetObj.runningAppsCompactMode = SettingsData.runningAppsCompactMode;
|
widgetObj.runningAppsCompactMode = SettingsData.runningAppsCompactMode;
|
||||||
@@ -429,7 +430,7 @@ Item {
|
|||||||
"id": widget.id,
|
"id": widget.id,
|
||||||
"enabled": widget.enabled
|
"enabled": widget.enabled
|
||||||
};
|
};
|
||||||
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
if (widget[keys[i]] !== undefined)
|
if (widget[keys[i]] !== undefined)
|
||||||
result[keys[i]] = widget[keys[i]];
|
result[keys[i]] = widget[keys[i]];
|
||||||
@@ -498,6 +499,32 @@ Item {
|
|||||||
return;
|
return;
|
||||||
var newWidget = cloneWidgetData(widgets[widgetIndex]);
|
var newWidget = cloneWidgetData(widgets[widgetIndex]);
|
||||||
newWidget[settingName] = value;
|
newWidget[settingName] = value;
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
switch (settingName) {
|
||||||
|
case "showAudioIcon":
|
||||||
|
newWidget.showAudioPercent = false;
|
||||||
|
break;
|
||||||
|
case "showMicIcon":
|
||||||
|
newWidget.showMicPercent = false;
|
||||||
|
break;
|
||||||
|
case "showBrightnessIcon":
|
||||||
|
newWidget.showBrightnessPercent = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
widgets[widgetIndex] = newWidget;
|
||||||
|
setWidgetsForSection(sectionId, widgets);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleControlCenterGroupOrderChanged(sectionId, widgetIndex, groupOrder) {
|
||||||
|
var widgets = getWidgetsForSection(sectionId).slice();
|
||||||
|
if (widgetIndex < 0 || widgetIndex >= widgets.length)
|
||||||
|
return;
|
||||||
|
var previousWidget = widgets[widgetIndex];
|
||||||
|
var newWidget = cloneWidgetData(previousWidget);
|
||||||
|
newWidget.controlCenterGroupOrder = groupOrder.slice();
|
||||||
widgets[widgetIndex] = newWidget;
|
widgets[widgetIndex] = newWidget;
|
||||||
setWidgetsForSection(sectionId, widgets);
|
setWidgetsForSection(sectionId, widgets);
|
||||||
}
|
}
|
||||||
@@ -655,6 +682,8 @@ Item {
|
|||||||
item.showPrinterIcon = widget.showPrinterIcon;
|
item.showPrinterIcon = widget.showPrinterIcon;
|
||||||
if (widget.showScreenSharingIcon !== undefined)
|
if (widget.showScreenSharingIcon !== undefined)
|
||||||
item.showScreenSharingIcon = widget.showScreenSharingIcon;
|
item.showScreenSharingIcon = widget.showScreenSharingIcon;
|
||||||
|
if (widget.controlCenterGroupOrder !== undefined)
|
||||||
|
item.controlCenterGroupOrder = widget.controlCenterGroupOrder;
|
||||||
if (widget.minimumWidth !== undefined)
|
if (widget.minimumWidth !== undefined)
|
||||||
item.minimumWidth = widget.minimumWidth;
|
item.minimumWidth = widget.minimumWidth;
|
||||||
if (widget.showSwap !== undefined)
|
if (widget.showSwap !== undefined)
|
||||||
@@ -948,6 +977,9 @@ Item {
|
|||||||
onControlCenterSettingChanged: (sectionId, index, setting, value) => {
|
onControlCenterSettingChanged: (sectionId, index, setting, value) => {
|
||||||
widgetsTab.handleControlCenterSettingChanged(sectionId, index, setting, value);
|
widgetsTab.handleControlCenterSettingChanged(sectionId, index, setting, value);
|
||||||
}
|
}
|
||||||
|
onControlCenterGroupOrderChanged: (sectionId, index, groupOrder) => {
|
||||||
|
widgetsTab.handleControlCenterGroupOrderChanged(sectionId, index, groupOrder);
|
||||||
|
}
|
||||||
onPrivacySettingChanged: (sectionId, index, setting, value) => {
|
onPrivacySettingChanged: (sectionId, index, setting, value) => {
|
||||||
widgetsTab.handlePrivacySettingChanged(sectionId, index, setting, value);
|
widgetsTab.handlePrivacySettingChanged(sectionId, index, setting, value);
|
||||||
}
|
}
|
||||||
@@ -1012,6 +1044,9 @@ Item {
|
|||||||
onControlCenterSettingChanged: (sectionId, index, setting, value) => {
|
onControlCenterSettingChanged: (sectionId, index, setting, value) => {
|
||||||
widgetsTab.handleControlCenterSettingChanged(sectionId, index, setting, value);
|
widgetsTab.handleControlCenterSettingChanged(sectionId, index, setting, value);
|
||||||
}
|
}
|
||||||
|
onControlCenterGroupOrderChanged: (sectionId, index, groupOrder) => {
|
||||||
|
widgetsTab.handleControlCenterGroupOrderChanged(sectionId, index, groupOrder);
|
||||||
|
}
|
||||||
onPrivacySettingChanged: (sectionId, index, setting, value) => {
|
onPrivacySettingChanged: (sectionId, index, setting, value) => {
|
||||||
widgetsTab.handlePrivacySettingChanged(sectionId, index, setting, value);
|
widgetsTab.handlePrivacySettingChanged(sectionId, index, setting, value);
|
||||||
}
|
}
|
||||||
@@ -1076,6 +1111,9 @@ Item {
|
|||||||
onControlCenterSettingChanged: (sectionId, index, setting, value) => {
|
onControlCenterSettingChanged: (sectionId, index, setting, value) => {
|
||||||
widgetsTab.handleControlCenterSettingChanged(sectionId, index, setting, value);
|
widgetsTab.handleControlCenterSettingChanged(sectionId, index, setting, value);
|
||||||
}
|
}
|
||||||
|
onControlCenterGroupOrderChanged: (sectionId, index, groupOrder) => {
|
||||||
|
widgetsTab.handleControlCenterGroupOrderChanged(sectionId, index, groupOrder);
|
||||||
|
}
|
||||||
onPrivacySettingChanged: (sectionId, index, setting, value) => {
|
onPrivacySettingChanged: (sectionId, index, setting, value) => {
|
||||||
widgetsTab.handlePrivacySettingChanged(sectionId, index, setting, value);
|
widgetsTab.handlePrivacySettingChanged(sectionId, index, setting, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Column {
|
|||||||
signal gpuSelectionChanged(string sectionId, int widgetIndex, int selectedIndex)
|
signal gpuSelectionChanged(string sectionId, int widgetIndex, int selectedIndex)
|
||||||
signal diskMountSelectionChanged(string sectionId, int widgetIndex, string mountPath)
|
signal diskMountSelectionChanged(string sectionId, int widgetIndex, string mountPath)
|
||||||
signal controlCenterSettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
|
signal controlCenterSettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
|
||||||
|
signal controlCenterGroupOrderChanged(string sectionId, int widgetIndex, var groupOrder)
|
||||||
signal privacySettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
|
signal privacySettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
|
||||||
signal minimumWidthChanged(string sectionId, int widgetIndex, bool enabled)
|
signal minimumWidthChanged(string sectionId, int widgetIndex, bool enabled)
|
||||||
signal showSwapChanged(string sectionId, int widgetIndex, bool enabled)
|
signal showSwapChanged(string sectionId, int widgetIndex, bool enabled)
|
||||||
@@ -39,7 +40,7 @@ Column {
|
|||||||
"id": widget.id,
|
"id": widget.id,
|
||||||
"enabled": widget.enabled
|
"enabled": widget.enabled
|
||||||
};
|
};
|
||||||
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge"];
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
if (widget[keys[i]] !== undefined)
|
if (widget[keys[i]] !== undefined)
|
||||||
result[keys[i]] = widget[keys[i]];
|
result[keys[i]] = widget[keys[i]];
|
||||||
@@ -90,7 +91,6 @@ Column {
|
|||||||
height: 70
|
height: 70
|
||||||
z: held ? 2 : 1
|
z: held ? 2 : 1
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: itemBackground
|
id: itemBackground
|
||||||
|
|
||||||
@@ -587,6 +587,7 @@ Column {
|
|||||||
controlCenterContextMenu.widgetData = modelData;
|
controlCenterContextMenu.widgetData = modelData;
|
||||||
controlCenterContextMenu.sectionId = root.sectionId;
|
controlCenterContextMenu.sectionId = root.sectionId;
|
||||||
controlCenterContextMenu.widgetIndex = index;
|
controlCenterContextMenu.widgetIndex = index;
|
||||||
|
controlCenterContextMenu.controlCenterGroups = controlCenterContextMenu.getOrderedControlCenterGroups();
|
||||||
|
|
||||||
var buttonPos = ccMenuButton.mapToItem(root, 0, 0);
|
var buttonPos = ccMenuButton.mapToItem(root, 0, 0);
|
||||||
var popupWidth = controlCenterContextMenu.width;
|
var popupWidth = controlCenterContextMenu.width;
|
||||||
@@ -1054,13 +1055,236 @@ Column {
|
|||||||
property string sectionId: ""
|
property string sectionId: ""
|
||||||
property int widgetIndex: -1
|
property int widgetIndex: -1
|
||||||
|
|
||||||
width: 220
|
readonly property real minimumContentWidth: controlCenterContentMetrics.implicitWidth + Theme.spacingS * 2
|
||||||
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
readonly property real controlCenterRowHeight: 32
|
||||||
|
readonly property real controlCenterRowSpacing: 1
|
||||||
|
readonly property real controlCenterGroupVerticalPadding: Theme.spacingXS * 2
|
||||||
|
readonly property real controlCenterMenuSpacing: 2
|
||||||
|
width: Math.max(220, minimumContentWidth)
|
||||||
|
height: getControlCenterPopupHeight(controlCenterGroups)
|
||||||
padding: 0
|
padding: 0
|
||||||
modal: true
|
modal: true
|
||||||
focus: true
|
focus: true
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
cancelControlCenterDrag();
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var defaultControlCenterGroups: [
|
||||||
|
{
|
||||||
|
id: "network",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "lan",
|
||||||
|
label: I18n.tr("Network"),
|
||||||
|
setting: "showNetworkIcon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "vpn",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "vpn_lock",
|
||||||
|
label: I18n.tr("VPN"),
|
||||||
|
setting: "showVpnIcon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bluetooth",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "bluetooth",
|
||||||
|
label: I18n.tr("Bluetooth"),
|
||||||
|
setting: "showBluetoothIcon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "audio",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "volume_up",
|
||||||
|
label: I18n.tr("Audio"),
|
||||||
|
setting: "showAudioIcon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "percent",
|
||||||
|
label: I18n.tr("Volume"),
|
||||||
|
setting: "showAudioPercent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "microphone",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "mic",
|
||||||
|
label: I18n.tr("Microphone"),
|
||||||
|
setting: "showMicIcon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "percent",
|
||||||
|
label: I18n.tr("Microphone Volume"),
|
||||||
|
setting: "showMicPercent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "brightness",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "brightness_high",
|
||||||
|
label: I18n.tr("Brightness"),
|
||||||
|
setting: "showBrightnessIcon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "percent",
|
||||||
|
label: I18n.tr("Brightness Value"),
|
||||||
|
setting: "showBrightnessPercent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "battery",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "battery_full",
|
||||||
|
label: I18n.tr("Battery"),
|
||||||
|
setting: "showBatteryIcon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "printer",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "print",
|
||||||
|
label: I18n.tr("Printer"),
|
||||||
|
setting: "showPrinterIcon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "screenSharing",
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
icon: "screen_record",
|
||||||
|
label: I18n.tr("Screen Sharing"),
|
||||||
|
setting: "showScreenSharingIcon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
property var controlCenterGroups: defaultControlCenterGroups
|
||||||
|
property int draggedControlCenterGroupIndex: -1
|
||||||
|
property int controlCenterGroupDropIndex: -1
|
||||||
|
|
||||||
|
function updateControlCenterGroupDropIndex(draggedIndex, localY) {
|
||||||
|
const totalGroups = controlCenterGroups.length;
|
||||||
|
let dropIndex = totalGroups;
|
||||||
|
|
||||||
|
for (let i = 0; i < totalGroups; i++) {
|
||||||
|
const delegate = groupRepeater.itemAt(i);
|
||||||
|
if (!delegate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const midpoint = delegate.y + delegate.height / 2;
|
||||||
|
if (localY < midpoint) {
|
||||||
|
dropIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
controlCenterGroupDropIndex = Math.max(0, Math.min(totalGroups, dropIndex));
|
||||||
|
draggedControlCenterGroupIndex = draggedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishControlCenterDrag() {
|
||||||
|
if (draggedControlCenterGroupIndex < 0) {
|
||||||
|
controlCenterGroupDropIndex = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromIndex = draggedControlCenterGroupIndex;
|
||||||
|
let toIndex = controlCenterGroupDropIndex;
|
||||||
|
|
||||||
|
draggedControlCenterGroupIndex = -1;
|
||||||
|
controlCenterGroupDropIndex = -1;
|
||||||
|
|
||||||
|
if (toIndex < 0 || toIndex > controlCenterGroups.length || toIndex === fromIndex || toIndex === fromIndex + 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const groups = controlCenterGroups.slice();
|
||||||
|
const moved = groups.splice(fromIndex, 1)[0];
|
||||||
|
|
||||||
|
if (toIndex > fromIndex)
|
||||||
|
toIndex -= 1;
|
||||||
|
|
||||||
|
groups.splice(toIndex, 0, moved);
|
||||||
|
controlCenterGroups = groups;
|
||||||
|
const reorderedGroupIds = groups.map(group => group.id);
|
||||||
|
root.controlCenterGroupOrderChanged(sectionId, widgetIndex, reorderedGroupIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelControlCenterDrag() {
|
||||||
|
draggedControlCenterGroupIndex = -1;
|
||||||
|
controlCenterGroupDropIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getControlCenterGroupHeight(group) {
|
||||||
|
const rowCount = group?.rows?.length ?? 0;
|
||||||
|
if (rowCount <= 0)
|
||||||
|
return controlCenterGroupVerticalPadding;
|
||||||
|
|
||||||
|
return rowCount * controlCenterRowHeight + Math.max(0, rowCount - 1) * controlCenterRowSpacing + controlCenterGroupVerticalPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getControlCenterPopupHeight(groups) {
|
||||||
|
const orderedGroups = groups || [];
|
||||||
|
let totalHeight = Theme.spacingS * 2;
|
||||||
|
|
||||||
|
for (let i = 0; i < orderedGroups.length; i++) {
|
||||||
|
totalHeight += getControlCenterGroupHeight(orderedGroups[i]);
|
||||||
|
if (i < orderedGroups.length - 1)
|
||||||
|
totalHeight += controlCenterMenuSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrderedControlCenterGroups() {
|
||||||
|
const baseGroups = defaultControlCenterGroups.slice();
|
||||||
|
const currentWidget = contentItem.getCurrentWidgetData();
|
||||||
|
const savedOrder = currentWidget?.controlCenterGroupOrder;
|
||||||
|
if (!savedOrder || !savedOrder.length)
|
||||||
|
return baseGroups;
|
||||||
|
|
||||||
|
const groupMap = {};
|
||||||
|
for (let i = 0; i < baseGroups.length; i++)
|
||||||
|
groupMap[baseGroups[i].id] = baseGroups[i];
|
||||||
|
|
||||||
|
const orderedGroups = [];
|
||||||
|
for (let i = 0; i < savedOrder.length; i++) {
|
||||||
|
const groupId = savedOrder[i];
|
||||||
|
const group = groupMap[groupId];
|
||||||
|
if (group) {
|
||||||
|
orderedGroups.push(group);
|
||||||
|
delete groupMap[groupId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < baseGroups.length; i++) {
|
||||||
|
const group = baseGroups[i];
|
||||||
|
if (groupMap[group.id])
|
||||||
|
orderedGroups.push(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderedGroups;
|
||||||
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -1069,83 +1293,64 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
|
function getCurrentWidgetData() {
|
||||||
|
const widgets = root.items || [];
|
||||||
|
if (controlCenterContextMenu.widgetIndex >= 0 && controlCenterContextMenu.widgetIndex < widgets.length)
|
||||||
|
return widgets[controlCenterContextMenu.widgetIndex];
|
||||||
|
return controlCenterContextMenu.widgetData;
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: menuColumn
|
id: menuColumn
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
Repeater {
|
Item {
|
||||||
model: [
|
id: controlCenterContentMetrics
|
||||||
{
|
visible: false
|
||||||
icon: "lan",
|
implicitWidth: 16 + Theme.spacingS + 16 + Theme.spacingS + longestControlCenterLabelMetrics.advanceWidth + Theme.spacingM + 40 + Theme.spacingS * 2 + Theme.spacingM
|
||||||
label: I18n.tr("Network"),
|
}
|
||||||
setting: "showNetworkIcon"
|
|
||||||
},
|
TextMetrics {
|
||||||
{
|
id: longestControlCenterLabelMetrics
|
||||||
icon: "vpn_lock",
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
label: I18n.tr("VPN"),
|
text: {
|
||||||
setting: "showVpnIcon"
|
const labels = [
|
||||||
},
|
I18n.tr("Network"),
|
||||||
{
|
I18n.tr("VPN"),
|
||||||
icon: "bluetooth",
|
I18n.tr("Bluetooth"),
|
||||||
label: I18n.tr("Bluetooth"),
|
I18n.tr("Audio"),
|
||||||
setting: "showBluetoothIcon"
|
I18n.tr("Volume"),
|
||||||
},
|
I18n.tr("Microphone"),
|
||||||
{
|
I18n.tr("Microphone Volume"),
|
||||||
icon: "volume_up",
|
I18n.tr("Brightness"),
|
||||||
label: I18n.tr("Audio"),
|
I18n.tr("Brightness Value"),
|
||||||
setting: "showAudioIcon"
|
I18n.tr("Battery"),
|
||||||
},
|
I18n.tr("Printer"),
|
||||||
{
|
I18n.tr("Screen Sharing")
|
||||||
icon: "percent",
|
];
|
||||||
label: I18n.tr("Volume"),
|
let longest = "";
|
||||||
setting: "showAudioPercent"
|
for (let i = 0; i < labels.length; i++) {
|
||||||
},
|
if (labels[i].length > longest.length)
|
||||||
{
|
longest = labels[i];
|
||||||
icon: "mic",
|
}
|
||||||
label: I18n.tr("Microphone"),
|
return longest;
|
||||||
setting: "showMicIcon"
|
}
|
||||||
},
|
}
|
||||||
{
|
|
||||||
icon: "percent",
|
Repeater {
|
||||||
label: I18n.tr("Microphone Volume"),
|
model: controlCenterContextMenu.controlCenterGroups
|
||||||
setting: "showMicPercent"
|
|
||||||
},
|
delegate: Item {
|
||||||
{
|
id: delegateRoot
|
||||||
icon: "brightness_high",
|
|
||||||
label: I18n.tr("Brightness"),
|
|
||||||
setting: "showBrightnessIcon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "percent",
|
|
||||||
label: I18n.tr("Brightness Value"),
|
|
||||||
setting: "showBrightnessPercent"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "battery_full",
|
|
||||||
label: I18n.tr("Battery"),
|
|
||||||
setting: "showBatteryIcon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "print",
|
|
||||||
label: I18n.tr("Printer"),
|
|
||||||
setting: "showPrinterIcon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "screen_record",
|
|
||||||
label: I18n.tr("Screen Sharing"),
|
|
||||||
setting: "showScreenSharingIcon"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
function getCheckedState() {
|
function getCheckedState(settingName) {
|
||||||
var wd = controlCenterContextMenu.widgetData;
|
const wd = controlCenterContextMenu.contentItem.getCurrentWidgetData();
|
||||||
switch (modelData.setting) {
|
switch (settingName) {
|
||||||
case "showNetworkIcon":
|
case "showNetworkIcon":
|
||||||
return wd?.showNetworkIcon ?? SettingsData.controlCenterShowNetworkIcon;
|
return wd?.showNetworkIcon ?? SettingsData.controlCenterShowNetworkIcon;
|
||||||
case "showVpnIcon":
|
case "showVpnIcon":
|
||||||
@@ -1175,57 +1380,197 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property string rootSetting: modelData.rows[0]?.setting ?? ""
|
||||||
|
readonly property bool rootEnabled: rootSetting ? getCheckedState(rootSetting) : true
|
||||||
|
readonly property bool isDragged: controlCenterContextMenu.draggedControlCenterGroupIndex === index
|
||||||
|
readonly property bool showDropIndicatorAbove: controlCenterContextMenu.controlCenterGroupDropIndex === index
|
||||||
|
readonly property bool showDropIndicatorBelow: controlCenterContextMenu.controlCenterGroupDropIndex === controlCenterContextMenu.controlCenterGroups.length && index === controlCenterContextMenu.controlCenterGroups.length - 1
|
||||||
|
|
||||||
width: menuColumn.width
|
width: menuColumn.width
|
||||||
height: 32
|
height: groupBackground.height
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: toggleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
|
||||||
|
|
||||||
Row {
|
Rectangle {
|
||||||
|
id: groupBackground
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: modelData.icon
|
|
||||||
size: 16
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.label
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DankToggle {
|
|
||||||
id: toggle
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.top: parent.top
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
height: groupContent.implicitHeight + Theme.spacingXS * 2
|
||||||
width: 40
|
radius: Theme.cornerRadius
|
||||||
height: 20
|
color: isDragged ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.18) : (groupHoverArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent")
|
||||||
checked: getCheckedState()
|
opacity: isDragged ? 0.75 : 1.0
|
||||||
onToggled: {
|
|
||||||
root.controlCenterSettingChanged(controlCenterContextMenu.sectionId, controlCenterContextMenu.widgetIndex, modelData.setting, toggled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
Rectangle {
|
||||||
id: toggleArea
|
anchors.left: parent.left
|
||||||
anchors.fill: parent
|
anchors.right: parent.right
|
||||||
hoverEnabled: true
|
anchors.top: parent.top
|
||||||
cursorShape: Qt.PointingHandCursor
|
anchors.topMargin: -1
|
||||||
onPressed: {
|
height: 2
|
||||||
toggle.checked = !toggle.checked;
|
radius: 1
|
||||||
root.controlCenterSettingChanged(controlCenterContextMenu.sectionId, controlCenterContextMenu.widgetIndex, modelData.setting, toggle.checked);
|
color: Theme.primary
|
||||||
|
visible: showDropIndicatorAbove
|
||||||
|
z: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: -1
|
||||||
|
height: 2
|
||||||
|
radius: 1
|
||||||
|
color: Theme.primary
|
||||||
|
visible: showDropIndicatorBelow
|
||||||
|
z: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: groupContent
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Theme.spacingXS
|
||||||
|
implicitHeight: groupColumn.implicitHeight
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: groupColumn
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: groupColumnRepeater
|
||||||
|
model: modelData.rows
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
readonly property var rowData: modelData
|
||||||
|
readonly property bool isFirstRow: index === 0
|
||||||
|
readonly property bool rowEnabled: isFirstRow ? true : delegateRoot.rootEnabled
|
||||||
|
readonly property bool computedCheckedState: rowEnabled ? getCheckedState(rowData.setting) : false
|
||||||
|
readonly property bool rowHovered: rowEnabled && (toggleArea.containsMouse || (isFirstRow && groupDragHandleArea.containsMouse))
|
||||||
|
|
||||||
|
width: groupColumn.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
opacity: rowEnabled ? 1.0 : 0.5
|
||||||
|
color: rowHovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.right: toggle.left
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "drag_indicator"
|
||||||
|
size: 16
|
||||||
|
color: groupDragHandleArea.pressed || isDragged ? Theme.primary : Theme.outline
|
||||||
|
visible: isFirstRow
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: groupDragHandleArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
preventStealing: true
|
||||||
|
enabled: isFirstRow
|
||||||
|
cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
||||||
|
|
||||||
|
onPressed: mouse => {
|
||||||
|
mouse.accepted = true;
|
||||||
|
const point = mapToItem(menuColumn, mouse.x, mouse.y);
|
||||||
|
controlCenterContextMenu.updateControlCenterGroupDropIndex(delegateRoot.index, point.y);
|
||||||
|
}
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
if (!pressed)
|
||||||
|
return;
|
||||||
|
mouse.accepted = true;
|
||||||
|
const point = mapToItem(menuColumn, mouse.x, mouse.y);
|
||||||
|
controlCenterContextMenu.updateControlCenterGroupDropIndex(delegateRoot.index, point.y);
|
||||||
|
}
|
||||||
|
onReleased: mouse => {
|
||||||
|
mouse.accepted = true;
|
||||||
|
const point = mapToItem(menuColumn, mouse.x, mouse.y);
|
||||||
|
controlCenterContextMenu.updateControlCenterGroupDropIndex(delegateRoot.index, point.y);
|
||||||
|
controlCenterContextMenu.finishControlCenterDrag();
|
||||||
|
}
|
||||||
|
onCanceled: {
|
||||||
|
controlCenterContextMenu.cancelControlCenterDrag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: rowData.icon
|
||||||
|
size: 16
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: rowData.label
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
id: toggle
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 40
|
||||||
|
height: 20
|
||||||
|
enabled: rowEnabled
|
||||||
|
checked: computedCheckedState
|
||||||
|
|
||||||
|
onToggled: {
|
||||||
|
if (!rowEnabled)
|
||||||
|
return;
|
||||||
|
root.controlCenterSettingChanged(controlCenterContextMenu.sectionId, controlCenterContextMenu.widgetIndex, rowData.setting, toggled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: toggleArea
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 16 + Theme.spacingS * 2
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: rowEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
enabled: rowEnabled && controlCenterContextMenu.draggedControlCenterGroupIndex < 0
|
||||||
|
onPressed: {
|
||||||
|
if (!rowEnabled)
|
||||||
|
return;
|
||||||
|
root.controlCenterSettingChanged(controlCenterContextMenu.sectionId, controlCenterContextMenu.widgetIndex, rowData.setting, !computedCheckedState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: groupHoverArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
enabled: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id: groupRepeater
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
|
|
||||||
// M3 disabled track: on surface 12% opacity
|
// Distinguish disabled checked vs unchecked so unchecked disabled switches don't look enabled
|
||||||
color: !toggle.enabled ? Qt.alpha(Theme.surfaceText, 0.12) : (toggle.checked ? Theme.primary : Theme.surfaceVariantAlpha)
|
color: !toggle.enabled ? (toggle.checked ? Qt.alpha(Theme.surfaceText, 0.12) : "transparent") : (toggle.checked ? Theme.primary : Theme.surfaceVariantAlpha)
|
||||||
opacity: toggle.toggling ? 0.6 : 1
|
opacity: toggle.toggling ? 0.6 : 1
|
||||||
|
|
||||||
// M3 disabled unchecked border: on surface 12% opacity
|
// M3 disabled unchecked border: on surface 12% opacity
|
||||||
@@ -119,8 +119,8 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
// M3 disabled thumb:
|
// M3 disabled thumb:
|
||||||
// checked = solid surface | unchecked = on surface 38%
|
// checked = solid surface | unchecked = outlined off-state thumb
|
||||||
color: !toggle.enabled ? (toggle.checked ? Theme.surface : Qt.alpha(Theme.surfaceText, 0.38)) : (toggle.checked ? Theme.surface : Theme.outline)
|
color: !toggle.enabled ? (toggle.checked ? Theme.surface : "transparent") : (toggle.checked ? Theme.surface : Theme.outline)
|
||||||
border.color: !toggle.enabled ? (toggle.checked ? "transparent" : Qt.alpha(Theme.surfaceText, 0.38)) : Theme.outline
|
border.color: !toggle.enabled ? (toggle.checked ? "transparent" : Qt.alpha(Theme.surfaceText, 0.38)) : Theme.outline
|
||||||
border.width: (toggle.checked && toggle.enabled) ? 1 : 2
|
border.width: (toggle.checked && toggle.enabled) ? 1 : 2
|
||||||
|
|
||||||
@@ -165,8 +165,8 @@ Item {
|
|||||||
// M3 disabled icon: on surface 38%
|
// M3 disabled icon: on surface 38%
|
||||||
color: toggle.enabled ? Theme.surfaceText : Qt.alpha(Theme.surfaceText, 0.38)
|
color: toggle.enabled ? Theme.surfaceText : Qt.alpha(Theme.surfaceText, 0.38)
|
||||||
filled: true
|
filled: true
|
||||||
opacity: toggle.checked ? 1 : 0
|
opacity: (toggle.checked && toggle.enabled) ? 1 : 0
|
||||||
scale: toggle.checked ? 1 : 0.6
|
scale: (toggle.checked && toggle.enabled) ? 1 : 0.6
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
|||||||
Reference in New Issue
Block a user