mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-29 07:52:50 -05:00
desktop widgets: sync position across screens option, clickthrough
option, grouping in settings, repositioning, new IPCs for control fixes #1300 fixes #1301
This commit is contained in:
@@ -14,7 +14,13 @@ Item {
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property var expandedStates: ({})
|
||||
property var groupCollapsedStates: ({})
|
||||
property var parentModal: null
|
||||
property string editingGroupId: ""
|
||||
property string newGroupName: ""
|
||||
|
||||
readonly property var allInstances: SettingsData.desktopWidgetInstances || []
|
||||
readonly property var allGroups: SettingsData.desktopWidgetGroups || []
|
||||
|
||||
DesktopWidgetBrowser {
|
||||
id: widgetBrowser
|
||||
@@ -80,54 +86,500 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: instancesColumn
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: SettingsData.desktopWidgetInstances.length > 0
|
||||
iconName: "folder"
|
||||
title: I18n.tr("Groups")
|
||||
collapsible: true
|
||||
expanded: root.allGroups.length > 0
|
||||
|
||||
Repeater {
|
||||
id: instanceRepeater
|
||||
model: ScriptModel {
|
||||
id: instancesModel
|
||||
objectProp: "id"
|
||||
values: SettingsData.desktopWidgetInstances
|
||||
Column {
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("Organize widgets into collapsible groups")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
DesktopWidgetInstanceCard {
|
||||
required property var modelData
|
||||
required property int index
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
width: parent.width
|
||||
|
||||
readonly property string instanceIdRef: modelData.id
|
||||
readonly property var liveInstanceData: {
|
||||
const instances = SettingsData.desktopWidgetInstances || [];
|
||||
return instances.find(inst => inst.id === instanceIdRef) ?? modelData;
|
||||
DankTextField {
|
||||
id: newGroupField
|
||||
width: parent.width - addGroupBtn.width - Theme.spacingS
|
||||
placeholderText: I18n.tr("New group name...")
|
||||
text: root.newGroupName
|
||||
onTextChanged: root.newGroupName = text
|
||||
onAccepted: {
|
||||
if (!text.trim())
|
||||
return;
|
||||
SettingsData.createDesktopWidgetGroup(text.trim());
|
||||
root.newGroupName = "";
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
|
||||
width: instancesColumn.width
|
||||
instanceData: liveInstanceData
|
||||
isExpanded: root.expandedStates[instanceIdRef] ?? false
|
||||
DankButton {
|
||||
id: addGroupBtn
|
||||
iconName: "add"
|
||||
text: I18n.tr("Add")
|
||||
enabled: root.newGroupName.trim().length > 0
|
||||
onClicked: {
|
||||
SettingsData.createDesktopWidgetGroup(root.newGroupName.trim());
|
||||
root.newGroupName = "";
|
||||
newGroupField.text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExpandedChanged: {
|
||||
if (expanded === (root.expandedStates[instanceIdRef] ?? false))
|
||||
return;
|
||||
var states = Object.assign({}, root.expandedStates);
|
||||
states[instanceIdRef] = expanded;
|
||||
root.expandedStates = states;
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
visible: root.allGroups.length > 0
|
||||
|
||||
Repeater {
|
||||
model: root.allGroups
|
||||
|
||||
Rectangle {
|
||||
id: groupItem
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
width: parent.width
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: groupMouseArea.containsMouse ? Theme.surfaceHover : Theme.surfaceContainer
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "folder"
|
||||
size: Theme.iconSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.editingGroupId === groupItem.modelData.id
|
||||
width: active ? parent.width - Theme.iconSizeSmall - deleteGroupBtn.width - Theme.spacingS * 3 : 0
|
||||
height: active ? 32 : 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
sourceComponent: DankTextField {
|
||||
text: groupItem.modelData.name
|
||||
onAccepted: {
|
||||
if (!text.trim())
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetGroup(groupItem.modelData.id, {
|
||||
name: text.trim()
|
||||
});
|
||||
root.editingGroupId = "";
|
||||
}
|
||||
onEditingFinished: {
|
||||
if (!text.trim())
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetGroup(groupItem.modelData.id, {
|
||||
name: text.trim()
|
||||
});
|
||||
root.editingGroupId = "";
|
||||
}
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: root.editingGroupId !== groupItem.modelData.id
|
||||
text: groupItem.modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
width: parent.width - Theme.iconSizeSmall - deleteGroupBtn.width - Theme.spacingS * 3
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: deleteGroupBtn
|
||||
iconName: "delete"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
SettingsData.removeDesktopWidgetGroup(groupItem.modelData.id);
|
||||
ToastService.showInfo(I18n.tr("Group removed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: groupMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onDoubleClicked: root.editingGroupId = groupItem.modelData.id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.allGroups
|
||||
|
||||
Column {
|
||||
id: groupSection
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
readonly property string groupId: modelData.id
|
||||
readonly property var groupInstances: root.allInstances.filter(inst => inst.group === groupId)
|
||||
|
||||
width: mainColumn.width
|
||||
spacing: Theme.spacingM
|
||||
visible: groupInstances.length > 0
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 44
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainer
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: (root.groupCollapsedStates[groupSection.groupId] ?? false) ? "expand_more" : "expand_less"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: "folder"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: groupSection.modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "(" + groupSection.groupInstances.length + ")"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
onDuplicateRequested: SettingsData.duplicateDesktopWidgetInstance(instanceIdRef)
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
var states = Object.assign({}, root.groupCollapsedStates);
|
||||
states[groupSection.groupId] = !(states[groupSection.groupId] ?? false);
|
||||
root.groupCollapsedStates = states;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDeleteRequested: {
|
||||
SettingsData.removeDesktopWidgetInstance(instanceIdRef);
|
||||
ToastService.showInfo(I18n.tr("Widget removed"));
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: !(root.groupCollapsedStates[groupSection.groupId] ?? false)
|
||||
leftPadding: Theme.spacingM
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
objectProp: "id"
|
||||
values: groupSection.groupInstances
|
||||
}
|
||||
|
||||
Item {
|
||||
id: groupDelegateItem
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
property bool held: groupDragArea.pressed
|
||||
property real originalY: y
|
||||
|
||||
readonly property string instanceIdRef: modelData.id
|
||||
readonly property var liveInstanceData: {
|
||||
const instances = root.allInstances;
|
||||
return instances.find(inst => inst.id === instanceIdRef) ?? modelData;
|
||||
}
|
||||
|
||||
width: groupSection.width - Theme.spacingM
|
||||
height: groupCard.height
|
||||
z: held ? 2 : 1
|
||||
|
||||
DesktopWidgetInstanceCard {
|
||||
id: groupCard
|
||||
width: parent.width
|
||||
headerLeftPadding: 20
|
||||
instanceData: groupDelegateItem.liveInstanceData
|
||||
isExpanded: root.expandedStates[groupDelegateItem.instanceIdRef] ?? false
|
||||
|
||||
onExpandedChanged: {
|
||||
if (expanded === (root.expandedStates[groupDelegateItem.instanceIdRef] ?? false))
|
||||
return;
|
||||
var states = Object.assign({}, root.expandedStates);
|
||||
states[groupDelegateItem.instanceIdRef] = expanded;
|
||||
root.expandedStates = states;
|
||||
}
|
||||
|
||||
onDuplicateRequested: SettingsData.duplicateDesktopWidgetInstance(groupDelegateItem.instanceIdRef)
|
||||
|
||||
onDeleteRequested: {
|
||||
SettingsData.removeDesktopWidgetInstance(groupDelegateItem.instanceIdRef);
|
||||
ToastService.showInfo(I18n.tr("Widget removed"));
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: groupDragArea
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
width: 40
|
||||
height: 50
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.SizeVerCursor
|
||||
drag.target: groupDelegateItem.held ? groupDelegateItem : undefined
|
||||
drag.axis: Drag.YAxis
|
||||
preventStealing: true
|
||||
|
||||
onPressed: {
|
||||
groupDelegateItem.z = 2;
|
||||
groupDelegateItem.originalY = groupDelegateItem.y;
|
||||
}
|
||||
onReleased: {
|
||||
groupDelegateItem.z = 1;
|
||||
if (!drag.active) {
|
||||
groupDelegateItem.y = groupDelegateItem.originalY;
|
||||
return;
|
||||
}
|
||||
const spacing = Theme.spacingM;
|
||||
const itemH = groupDelegateItem.height + spacing;
|
||||
var newIndex = Math.round(groupDelegateItem.y / itemH);
|
||||
newIndex = Math.max(0, Math.min(newIndex, groupSection.groupInstances.length - 1));
|
||||
if (newIndex !== groupDelegateItem.index)
|
||||
SettingsData.reorderDesktopWidgetInstanceInGroup(groupDelegateItem.instanceIdRef, groupSection.groupId, newIndex);
|
||||
groupDelegateItem.y = groupDelegateItem.originalY;
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
x: Theme.spacingL - 2
|
||||
y: Theme.spacingL + (Theme.iconSize / 2) - (size / 2)
|
||||
name: "drag_indicator"
|
||||
size: 18
|
||||
color: Theme.outline
|
||||
opacity: groupDragArea.containsMouse || groupDragArea.pressed ? 1 : 0.5
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
enabled: !groupDragArea.pressed && !groupDragArea.drag.active
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: ungroupedSection
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: ungroupedInstances.length > 0
|
||||
|
||||
readonly property var ungroupedInstances: root.allInstances.filter(inst => {
|
||||
if (!inst.group)
|
||||
return true;
|
||||
return !root.allGroups.some(g => g.id === inst.group);
|
||||
})
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 44
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainer
|
||||
visible: root.allGroups.length > 0
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: (root.groupCollapsedStates["_ungrouped"] ?? false) ? "expand_more" : "expand_less"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: "widgets"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Ungrouped")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "(" + ungroupedSection.ungroupedInstances.length + ")"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
var states = Object.assign({}, root.groupCollapsedStates);
|
||||
states["_ungrouped"] = !(states["_ungrouped"] ?? false);
|
||||
root.groupCollapsedStates = states;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: !(root.groupCollapsedStates["_ungrouped"] ?? false)
|
||||
leftPadding: root.allGroups.length > 0 ? Theme.spacingM : 0
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
objectProp: "id"
|
||||
values: ungroupedSection.ungroupedInstances
|
||||
}
|
||||
|
||||
Item {
|
||||
id: ungroupedDelegateItem
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
property bool held: ungroupedDragArea.pressed
|
||||
property real originalY: y
|
||||
|
||||
readonly property string instanceIdRef: modelData.id
|
||||
readonly property var liveInstanceData: {
|
||||
const instances = root.allInstances;
|
||||
return instances.find(inst => inst.id === instanceIdRef) ?? modelData;
|
||||
}
|
||||
|
||||
width: ungroupedSection.width - (root.allGroups.length > 0 ? Theme.spacingM : 0)
|
||||
height: ungroupedCard.height
|
||||
z: held ? 2 : 1
|
||||
|
||||
DesktopWidgetInstanceCard {
|
||||
id: ungroupedCard
|
||||
width: parent.width
|
||||
headerLeftPadding: 20
|
||||
instanceData: ungroupedDelegateItem.liveInstanceData
|
||||
isExpanded: root.expandedStates[ungroupedDelegateItem.instanceIdRef] ?? false
|
||||
|
||||
onExpandedChanged: {
|
||||
if (expanded === (root.expandedStates[ungroupedDelegateItem.instanceIdRef] ?? false))
|
||||
return;
|
||||
var states = Object.assign({}, root.expandedStates);
|
||||
states[ungroupedDelegateItem.instanceIdRef] = expanded;
|
||||
root.expandedStates = states;
|
||||
}
|
||||
|
||||
onDuplicateRequested: SettingsData.duplicateDesktopWidgetInstance(ungroupedDelegateItem.instanceIdRef)
|
||||
|
||||
onDeleteRequested: {
|
||||
SettingsData.removeDesktopWidgetInstance(ungroupedDelegateItem.instanceIdRef);
|
||||
ToastService.showInfo(I18n.tr("Widget removed"));
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: ungroupedDragArea
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
width: 40
|
||||
height: 50
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.SizeVerCursor
|
||||
drag.target: ungroupedDelegateItem.held ? ungroupedDelegateItem : undefined
|
||||
drag.axis: Drag.YAxis
|
||||
preventStealing: true
|
||||
|
||||
onPressed: {
|
||||
ungroupedDelegateItem.z = 2;
|
||||
ungroupedDelegateItem.originalY = ungroupedDelegateItem.y;
|
||||
}
|
||||
onReleased: {
|
||||
ungroupedDelegateItem.z = 1;
|
||||
if (!drag.active) {
|
||||
ungroupedDelegateItem.y = ungroupedDelegateItem.originalY;
|
||||
return;
|
||||
}
|
||||
const spacing = Theme.spacingM;
|
||||
const itemH = ungroupedDelegateItem.height + spacing;
|
||||
var newIndex = Math.round(ungroupedDelegateItem.y / itemH);
|
||||
newIndex = Math.max(0, Math.min(newIndex, ungroupedSection.ungroupedInstances.length - 1));
|
||||
if (newIndex !== ungroupedDelegateItem.index)
|
||||
SettingsData.reorderDesktopWidgetInstanceInGroup(ungroupedDelegateItem.instanceIdRef, null, newIndex);
|
||||
ungroupedDelegateItem.y = ungroupedDelegateItem.originalY;
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
x: Theme.spacingL - 2
|
||||
y: Theme.spacingL + (Theme.iconSize / 2) - (size / 2)
|
||||
name: "drag_indicator"
|
||||
size: 18
|
||||
color: Theme.outline
|
||||
opacity: ungroupedDragArea.containsMouse || ungroupedDragArea.pressed ? 1 : 0.5
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
enabled: !ungroupedDragArea.pressed && !ungroupedDragArea.drag.active
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: SettingsData.desktopWidgetInstances.length === 0
|
||||
visible: root.allInstances.length === 0
|
||||
text: I18n.tr("No widgets added. Click \"Add Widget\" to get started.")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
|
||||
Reference in New Issue
Block a user