import QtQuick import QtQuick.Shapes import qs.Common import qs.Modules.ControlCenter import qs.Modules.ControlCenter.Widgets import qs.DCommon.Widgets import qs.Widgets // Edit-mode actions hung off the panel; a removable drag turns the strip into the trash. Item { id: root LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.childrenInherit: true property bool editMode: false property bool dragging: false property bool dragRemovable: false property bool canUndo: false property var transientSurfaceTracker: null readonly property bool trashShown: editMode && dragging && dragRemovable property bool overTrash: false property color backgroundColor: CcMetrics.dialogColor property vector4d cornerRadii: Qt.vector4d(Theme.windowRadius, Theme.windowRadius, Theme.windowRadius, Theme.windowRadius) // Shoulders fill outside the panel's corner arcs so a translucent fill never doubles up. property bool joined: false property bool above: false readonly property real leftRadius: above ? cornerRadii.x : cornerRadii.w readonly property real rightRadius: above ? cornerRadii.y : cornerRadii.z readonly property color trashColor: overTrash ? Theme.errorContainer : Theme.withAlpha(Theme.error, Theme.stateLayerDrag) // `above` flips y, which also reverses every arc's sweep. readonly property string outline: { const w = width; const h = height; const lw = Math.min(leftRadius, w / 2); const rw = Math.min(rightRadius, w / 2); const lf = Math.min(lw, h / 2); const rf = Math.min(rw, h / 2); const y = v => above ? h - v : v; const s = flag => above ? 1 - flag : flag; const joinedEdge = joined ? `M 0 ${y(-lw)} A ${lw} ${lw} 0 0 ${s(0)} ${lw} ${y(0)} L ${w - rw} ${y(0)} A ${rw} ${rw} 0 0 ${s(0)} ${w} ${y(-rw)}` : `M 0 ${y(lf)} A ${lf} ${lf} 0 0 ${s(1)} ${lf} ${y(0)} L ${w - rf} ${y(0)} A ${rf} ${rf} 0 0 ${s(1)} ${w} ${y(rf)}`; return `${joinedEdge} L ${w} ${y(h - rf)} A ${rf} ${rf} 0 0 ${s(1)} ${w - rf} ${y(h)} L ${lf} ${y(h)} A ${lf} ${lf} 0 0 ${s(1)} 0 ${y(h - lf)} Z`; } signal addWidgetRequested signal undoRequested signal resetRequested signal clearRequested signal finishRequested signal cancelRequested implicitHeight: CcMetrics.editActionsHeight visible: editMode function actionsY(rowHeight) { if (!joined) return (height - rowHeight) / 2; return above ? height - rowHeight : 0; } // No far or side edge, so a held tile stays held until it moves back into the panel. function trashContains(scenePosition) { if (!trashShown) return false; const y = mapFromItem(null, scenePosition.x, scenePosition.y).y; return above ? y <= height + CcMetrics.sheetPadding : y >= -CcMetrics.sheetPadding; } Shape { anchors.fill: parent preferredRendererType: Shape.CurveRenderer ShapePath { strokeWidth: -1 fillColor: root.backgroundColor PathSvg { path: root.outline } } } Shape { anchors.fill: parent preferredRendererType: Shape.CurveRenderer opacity: root.trashShown ? 1 : 0 visible: opacity > 0 ShapePath { strokeWidth: -1 fillGradient: LinearGradient { x1: 0 y1: 0 x2: 0 y2: root.height GradientStop { position: 0 color: root.above ? root.trashColor : "transparent" } GradientStop { position: 1 color: root.above ? "transparent" : root.trashColor } } PathSvg { path: root.outline } } Behavior on opacity { enabled: CcMetrics.animationsEnabled NumberAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } Row { anchors.left: parent.left anchors.leftMargin: CcMetrics.sheetPadding y: root.actionsY(height) spacing: Theme.spacingS visible: !root.trashShown DButton { buttonHeight: Theme.iconButtonSize iconName: "add" text: I18n.tr("Add widget") backgroundColor: Theme.secondaryContainer textColor: Theme.onSecondaryContainer onClicked: root.addWidgetRequested() } DActionButton { id: moreButton buttonSize: Theme.iconButtonSize iconName: "more_horiz" iconSize: CcMetrics.iconBoxIconSize iconColor: Theme.onSecondaryContainer backgroundColor: Theme.secondaryContainer tooltipText: I18n.tr("More") onClicked: editMenu.openAt(moreButton) } } Row { anchors.right: parent.right anchors.rightMargin: CcMetrics.sheetPadding y: root.actionsY(height) spacing: Theme.spacingS visible: !root.trashShown DActionButton { buttonSize: Theme.iconButtonSize iconName: "close" iconSize: CcMetrics.iconBoxIconSize iconColor: Theme.onSurface backgroundColor: Theme.chipSurface tooltipText: I18n.tr("Discard") onClicked: root.cancelRequested() } DActionButton { buttonSize: Theme.iconButtonSize iconName: "check" iconSize: CcMetrics.iconBoxIconSize iconColor: Theme.onPrimary backgroundColor: Theme.primary tooltipText: I18n.tr("Finish") onClicked: root.finishRequested() } } DIcon { anchors.centerIn: parent name: "delete" size: CcMetrics.iconBoxIconSize filled: root.overTrash color: Theme.error visible: root.trashShown Accessible.role: Accessible.Graphic Accessible.name: I18n.tr("Remove") } CcMenu { id: editMenu transientSurfaceTracker: root.transientSurfaceTracker items: [ { "iconName": "undo", "label": I18n.tr("Undo"), "enabled": root.canUndo, "action": () => root.undoRequested() }, { "iconName": "settings_backup_restore", "label": I18n.tr("Reset to default"), "action": () => root.resetRequested() }, { "iconName": "clear_all", "label": I18n.tr("Clear All"), "destructive": true, "action": () => root.clearRequested() } ] } }