mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-28 14:07:47 -04:00
* Island: Added option to choose the control center icon to be either Battery gauge or Connectivity * Add Control Center to settings search index * island: keep connectivity dim opacity off the blink target DankBlink animates opacity and forces it back to 1.0 when it stops, which broke the icon's binding and left the disabled state fully opaque.
466 lines
17 KiB
QML
466 lines
17 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import "../../Common/QmlUtils.js" as QmlUtils
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property string settingKey: "islandHomeLayout"
|
|
required property string barId
|
|
readonly property var groupIds: SettingsData._islandHomeGroupIds
|
|
readonly property var layout: {
|
|
SettingsData.barConfigs;
|
|
return SettingsData.getIslandHomeLayout(SettingsData.getBarConfig(root.barId));
|
|
}
|
|
readonly property bool isHighlighted: settingKey !== "" && SettingsSearchService.highlightSection === settingKey
|
|
|
|
readonly property var presentation: ({
|
|
"media": {
|
|
"icon": "music_note",
|
|
"text": I18n.tr("Media / Launcher", "island settings: media or launcher slot row"),
|
|
"description": I18n.tr("Search when idle, visualizer when media is playing", "island settings: media slot description")
|
|
},
|
|
"clock": {
|
|
"icon": "schedule",
|
|
"text": I18n.tr("Clock", "island settings: pinned clock row in the home layout"),
|
|
"description": I18n.tr("Current time and date display")
|
|
},
|
|
"weather": {
|
|
"icon": "wb_sunny",
|
|
"text": I18n.tr("Weather", "island settings: weather slot row"),
|
|
"description": SettingsData.weatherEnabled ? I18n.tr("Weather icon and temperature open the weather activity", "island settings: weather slot description") : I18n.tr("Enable weather in Time & Weather to show this shortcut", "island settings: weather slot disabled hint")
|
|
},
|
|
"status": {
|
|
"icon": "settings",
|
|
"text": I18n.tr("Control Center", "island settings: battery or control center slot row"),
|
|
"description": I18n.tr("Choose between battery gauge or connectivity icons", "island settings: control center slot description")
|
|
},
|
|
"volume": {
|
|
"icon": "volume_up",
|
|
"text": I18n.tr("Volume", "island settings: volume slot row"),
|
|
"description": I18n.tr("Current output volume on the home face", "island settings: volume slot description")
|
|
},
|
|
"brightness": {
|
|
"icon": "brightness_6",
|
|
"text": I18n.tr("Brightness", "island settings: brightness slot row"),
|
|
"description": I18n.tr("Current display brightness on the home face", "island settings: brightness slot description")
|
|
},
|
|
"notifications": {
|
|
"icon": "notifications",
|
|
"text": I18n.tr("Notifications", "island settings: notification badge slot row"),
|
|
"description": I18n.tr("Unread notification count beside the clock", "island settings: notification badge description")
|
|
}
|
|
})
|
|
|
|
readonly property real rowHeight: 72
|
|
readonly property real rowSpacing: Theme.spacingS
|
|
readonly property real dividerGap: 40
|
|
|
|
property var enabledOrder: []
|
|
property var disabledOrder: []
|
|
property string draggingId: ""
|
|
property var dragStartOrder: []
|
|
property string highlightedId: ""
|
|
property bool showHidden: false
|
|
|
|
readonly property bool hasHidden: disabledOrder.length > 0
|
|
readonly property real dividerY: enabledOrder.length * (rowHeight + rowSpacing)
|
|
readonly property real totalHeight: {
|
|
const base = enabledOrder.length * (rowHeight + rowSpacing);
|
|
if (!hasHidden)
|
|
return Math.max(0, base - rowSpacing);
|
|
if (!showHidden)
|
|
return base + dividerGap;
|
|
return base + dividerGap + disabledOrder.length * (rowHeight + rowSpacing) - rowSpacing;
|
|
}
|
|
|
|
width: parent?.width ?? 0
|
|
height: totalHeight
|
|
|
|
function isEnabled(id) {
|
|
const entry = layout.find(g => g.id === id);
|
|
return entry ? entry.enabled : false;
|
|
}
|
|
|
|
function rebuild() {
|
|
enabledOrder = layout.filter(g => g.enabled).map(g => g.id);
|
|
disabledOrder = layout.filter(g => !g.enabled).map(g => g.id);
|
|
}
|
|
|
|
function slotYForId(id) {
|
|
const enabledIndex = enabledOrder.indexOf(id);
|
|
if (enabledIndex >= 0)
|
|
return enabledIndex * (rowHeight + rowSpacing);
|
|
return dividerY + dividerGap + Math.max(0, disabledOrder.indexOf(id)) * (rowHeight + rowSpacing);
|
|
}
|
|
|
|
function beginDrag(id) {
|
|
draggingId = id;
|
|
dragStartOrder = enabledOrder.slice();
|
|
}
|
|
|
|
function updateDragTarget(centerY) {
|
|
if (draggingId === "")
|
|
return;
|
|
const pos = Math.max(0, Math.min(Math.floor(centerY / (rowHeight + rowSpacing)), enabledOrder.length - 1));
|
|
const arr = enabledOrder.slice();
|
|
const from = arr.indexOf(draggingId);
|
|
if (from < 0 || from === pos)
|
|
return;
|
|
arr.splice(from, 1);
|
|
arr.splice(pos, 0, draggingId);
|
|
enabledOrder = arr;
|
|
}
|
|
|
|
function commit() {
|
|
SettingsData.setIslandHomeLayoutOrder(root.barId, enabledOrder.concat(disabledOrder));
|
|
}
|
|
|
|
function endDrag() {
|
|
if (draggingId === "")
|
|
return;
|
|
const changed = JSON.stringify(enabledOrder) !== JSON.stringify(dragStartOrder);
|
|
draggingId = "";
|
|
if (changed)
|
|
commit();
|
|
}
|
|
|
|
function moveEnabled(id, delta) {
|
|
const pos = enabledOrder.indexOf(id);
|
|
const next = pos + delta;
|
|
if (pos < 0 || next < 0 || next >= enabledOrder.length)
|
|
return;
|
|
const arr = enabledOrder.slice();
|
|
arr.splice(pos, 1);
|
|
arr.splice(next, 0, id);
|
|
enabledOrder = arr;
|
|
commit();
|
|
}
|
|
|
|
onLayoutChanged: rebuild()
|
|
Component.onCompleted: {
|
|
rebuild();
|
|
Qt.callLater(() => {
|
|
if (!root.parent || !root.settingKey)
|
|
return;
|
|
const flickable = QmlUtils.findParentFlickable(root.parent);
|
|
if (flickable)
|
|
SettingsSearchService.registerCard(root.settingKey, root, flickable);
|
|
});
|
|
}
|
|
Component.onDestruction: {
|
|
if (settingKey)
|
|
SettingsSearchService.unregisterCard(settingKey);
|
|
}
|
|
|
|
function stepHighlight(dir, move) {
|
|
const order = enabledOrder.concat(disabledOrder);
|
|
if (order.length === 0)
|
|
return;
|
|
if (move && highlightedId !== "" && isEnabled(highlightedId)) {
|
|
moveEnabled(highlightedId, dir);
|
|
return;
|
|
}
|
|
if (highlightedId === "") {
|
|
highlightedId = dir > 0 ? order[0] : order[order.length - 1];
|
|
return;
|
|
}
|
|
const idx = Math.max(0, Math.min(order.length - 1, order.indexOf(highlightedId) + dir));
|
|
highlightedId = order[idx];
|
|
}
|
|
|
|
Keys.onPressed: event => {
|
|
const ctrl = (event.modifiers & Qt.ControlModifier) !== 0;
|
|
switch (event.key) {
|
|
case Qt.Key_Up:
|
|
stepHighlight(-1, ctrl);
|
|
event.accepted = true;
|
|
return;
|
|
case Qt.Key_Down:
|
|
stepHighlight(1, ctrl);
|
|
event.accepted = true;
|
|
return;
|
|
case Qt.Key_Space:
|
|
case Qt.Key_Return:
|
|
if (highlightedId === "")
|
|
return;
|
|
SettingsData.setIslandHomeGroupEnabled(root.barId, highlightedId, !isEnabled(highlightedId));
|
|
event.accepted = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: Theme.cornerRadius
|
|
color: Theme.withAlpha(Theme.primary, root.isHighlighted ? 0.2 : 0)
|
|
visible: root.isHighlighted
|
|
}
|
|
|
|
Behavior on height {
|
|
NumberAnimation {
|
|
duration: Theme.expressiveDurations.normal
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: hiddenDivider
|
|
|
|
width: parent.width
|
|
height: root.dividerGap
|
|
y: root.dividerY
|
|
opacity: root.hasHidden ? 1 : 0
|
|
visible: opacity > 0.01
|
|
|
|
Behavior on y {
|
|
NumberAnimation {
|
|
duration: Theme.expressiveDurations.normal
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
|
}
|
|
}
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
}
|
|
}
|
|
|
|
Row {
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: Theme.spacingS
|
|
|
|
DankIcon {
|
|
name: "visibility_off"
|
|
size: 14
|
|
color: Theme.outline
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Hidden (%1)", "island settings: hidden groups divider, %1 is count").arg(root.disabledOrder.length)
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Font.Medium
|
|
color: Theme.outline
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
|
|
DankIcon {
|
|
name: root.showHidden ? "expand_less" : "expand_more"
|
|
size: 14
|
|
color: Theme.outline
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: root.showHidden = !root.showHidden
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
model: root.groupIds
|
|
|
|
delegate: Item {
|
|
id: rowItem
|
|
|
|
required property string modelData
|
|
|
|
readonly property var present: root.presentation[modelData]
|
|
readonly property bool isClock: modelData === "clock"
|
|
readonly property bool isEnabled: root.isEnabled(modelData)
|
|
readonly property bool dragging: root.draggingId === modelData
|
|
readonly property bool highlighted: root.highlightedId === modelData
|
|
readonly property bool draggable: isEnabled
|
|
readonly property real surfaceAlphaScale: isClock ? 0.5 : (isEnabled ? 0.7 : 0.4)
|
|
|
|
width: root.width
|
|
height: root.rowHeight
|
|
z: dragging ? 100 : (highlighted ? 3 : 1)
|
|
visible: isEnabled || root.showHidden
|
|
|
|
Binding {
|
|
target: rowItem
|
|
property: "y"
|
|
value: root.slotYForId(rowItem.modelData)
|
|
when: !rowItem.dragging
|
|
restoreMode: Binding.RestoreNone
|
|
}
|
|
|
|
onYChanged: {
|
|
if (dragging)
|
|
root.updateDragTarget(y + height / 2);
|
|
}
|
|
|
|
Behavior on y {
|
|
enabled: !rowItem.dragging
|
|
NumberAnimation {
|
|
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: Theme.expressiveCurves.expressiveFastSpatial
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: surface
|
|
|
|
anchors.fill: parent
|
|
radius: Theme.cornerRadius
|
|
color: surfaceColor.value
|
|
border.width: rowItem.dragging || rowItem.highlighted ? 2 : 1
|
|
border.color: rowItem.dragging || rowItem.highlighted ? Theme.primary : (rowItem.isClock ? Theme.outlineMedium : Theme.outlineHeavy)
|
|
|
|
DankColorAnimation {
|
|
id: surfaceColor
|
|
|
|
to: {
|
|
if (rowItem.dragging)
|
|
return Theme.secondaryContainer;
|
|
return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.floatingWindowForegroundLayers ? Theme.floatingWindowForegroundTransparency * rowItem.surfaceAlphaScale : 0);
|
|
}
|
|
duration: Theme.shortDuration
|
|
}
|
|
|
|
Behavior on border.color {
|
|
ColorAnimation {
|
|
duration: Theme.shortDuration
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: parent.radius
|
|
color: Theme.primary
|
|
opacity: dragArea.containsMouse && !rowItem.dragging ? 0.06 : 0
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
}
|
|
}
|
|
}
|
|
|
|
DankIcon {
|
|
name: "drag_indicator"
|
|
size: Theme.iconSize - 4
|
|
color: rowItem.dragging ? Theme.primary : Theme.outline
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Theme.spacingM
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
opacity: !rowItem.draggable ? 0 : (dragArea.containsMouse || rowItem.dragging || rowItem.highlighted ? 1 : 0.45)
|
|
visible: opacity > 0.01
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
}
|
|
}
|
|
}
|
|
|
|
DankIcon {
|
|
id: groupIcon
|
|
|
|
name: rowItem.present.icon
|
|
size: Theme.iconSize
|
|
color: rowItem.isEnabled ? Theme.primary : Theme.outline
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Theme.spacingM * 2 + Theme.iconSize - 4
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
Behavior on color {
|
|
ColorAnimation {
|
|
duration: Theme.shortDuration
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
anchors.left: groupIcon.right
|
|
anchors.leftMargin: Theme.spacingM
|
|
anchors.right: visibilityButton.left
|
|
anchors.rightMargin: Theme.spacingM
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: Theme.spacingXXS
|
|
|
|
StyledText {
|
|
text: rowItem.present.text
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: rowItem.isEnabled ? Theme.surfaceText : Theme.outline
|
|
elide: Text.ElideRight
|
|
width: parent.width
|
|
}
|
|
|
|
StyledText {
|
|
text: rowItem.present.description
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: rowItem.isEnabled ? Theme.outline : Theme.outlineVariant
|
|
elide: Text.ElideRight
|
|
width: parent.width
|
|
visible: text.length > 0
|
|
}
|
|
}
|
|
|
|
DankIcon {
|
|
name: "lock"
|
|
size: 16
|
|
color: Theme.outline
|
|
opacity: 0.6
|
|
anchors.horizontalCenter: visibilityButton.horizontalCenter
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: rowItem.isClock
|
|
}
|
|
|
|
DankActionButton {
|
|
id: visibilityButton
|
|
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: Theme.spacingS
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: !rowItem.isClock
|
|
buttonSize: 36
|
|
iconName: rowItem.isEnabled ? "visibility" : "visibility_off"
|
|
iconSize: 18
|
|
iconColor: rowItem.isEnabled ? Theme.primary : Theme.outline
|
|
tooltipText: rowItem.isEnabled ? I18n.tr("Hide", "island settings: hide home group") : I18n.tr("Show", "island settings: show home group")
|
|
onClicked: {
|
|
root.forceActiveFocus();
|
|
root.highlightedId = rowItem.modelData;
|
|
SettingsData.setIslandHomeGroupEnabled(root.barId, rowItem.modelData, !rowItem.isEnabled);
|
|
}
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: dragArea
|
|
|
|
anchors.fill: parent
|
|
anchors.rightMargin: 48
|
|
hoverEnabled: true
|
|
enabled: rowItem.draggable
|
|
cursorShape: rowItem.dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
|
drag.target: rowItem
|
|
drag.axis: Drag.YAxis
|
|
drag.minimumY: -rowItem.height
|
|
drag.maximumY: root.height
|
|
drag.smoothed: false
|
|
onPressed: {
|
|
root.forceActiveFocus();
|
|
root.highlightedId = rowItem.modelData;
|
|
root.beginDrag(rowItem.modelData);
|
|
}
|
|
onReleased: root.endDrag()
|
|
}
|
|
}
|
|
}
|
|
}
|