mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-29 22:47:47 -04:00
refactor(cc / blur): restore card background on quick controls & unify blur popouts on Dash/CC
This commit is contained in:
1 parent
3fb27d93de
commit
97ec64ff97
20 files changed
+189
-360
No files matched your search
@@ -116,7 +116,6 @@ Singleton {
|
||||
readonly property int overlayZ: 10000
|
||||
readonly property real popupEnterScale: 0.92
|
||||
readonly property color dialogColor: Theme.foregroundColor(Theme.hostSurface)
|
||||
readonly property int backdropBlurRadius: 32
|
||||
readonly property bool hideCoveredContent: BlurService.enabled && Theme.connectedSurfaceBlurEnabled && dialogColor.a < 1
|
||||
|
||||
readonly property color tileActiveColor: Theme.ccTileActiveBg
|
||||
|
||||
@@ -17,6 +17,7 @@ FocusScope {
|
||||
property var model: null
|
||||
property string screenName: ""
|
||||
property string screenModel: ""
|
||||
property var transientSurfaceTracker: null
|
||||
property real topInset: 0
|
||||
property real minimumContentHeight: 0
|
||||
property vector4d cornerRadii: Qt.vector4d(Theme.windowRadius, Theme.windowRadius, Theme.windowRadius, Theme.windowRadius)
|
||||
@@ -295,11 +296,6 @@ FocusScope {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: menuOverlay
|
||||
anchors.fill: panel
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -347,7 +343,7 @@ FocusScope {
|
||||
Component {
|
||||
id: networkComponent
|
||||
NetworkDetail {
|
||||
menuParent: menuOverlay
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
transitioning: root.transitioning
|
||||
}
|
||||
}
|
||||
@@ -355,7 +351,7 @@ FocusScope {
|
||||
Component {
|
||||
id: bluetoothComponent
|
||||
BluetoothDetail {
|
||||
menuParent: menuOverlay
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell.Widgets
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Modules.ControlCenter
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
@@ -16,12 +15,9 @@ import qs.Widgets
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property Item backdrop: null
|
||||
property int widgetIndex: -1
|
||||
property real anchorX: 0
|
||||
property real anchorY: 0
|
||||
property real anchorWidth: 0
|
||||
property real anchorHeight: 0
|
||||
property var transientSurfaceTracker: null
|
||||
property Item _anchor: null
|
||||
|
||||
readonly property var widgetData: {
|
||||
if (widgetIndex < 0)
|
||||
@@ -37,21 +33,38 @@ Item {
|
||||
readonly property bool isQuickActions: widgetId === "quickActions"
|
||||
readonly property var quickActions: isQuickActions ? WidgetUtils.quickActions(widgetData) : []
|
||||
|
||||
visible: widgetIndex >= 0
|
||||
z: CcMetrics.overlayZ
|
||||
visible: widgetIndex >= 0 || contextMenu.renderActive
|
||||
|
||||
function open(index, data, anchorItem) {
|
||||
const pos = anchorItem.mapToItem(root, 0, 0);
|
||||
anchorX = pos.x;
|
||||
anchorY = pos.y;
|
||||
anchorWidth = anchorItem.width;
|
||||
anchorHeight = anchorItem.height;
|
||||
widgetIndex = index;
|
||||
focusScope.forceActiveFocus();
|
||||
_anchor = anchorItem;
|
||||
// Placement needs the final menu height, which settles after widgetIndex propagates.
|
||||
Qt.callLater(() => {
|
||||
const window = root.QsWindow.window;
|
||||
const screen = window?.screen;
|
||||
if (root.widgetIndex !== index || !anchorItem || !screen)
|
||||
return;
|
||||
const pos = anchorItem.mapToGlobal(0, 0);
|
||||
const x = pos.x - screen.x;
|
||||
const y = pos.y - screen.y;
|
||||
const menuX = I18n.isRtl ? x : x + anchorItem.width - contextMenu.effectiveMenuWidth;
|
||||
const aboveY = () => y - contextMenu.effectiveMenuHeight - Theme.spacingS;
|
||||
if (aboveY() < Theme.spacingS) {
|
||||
contextMenu.open(screen, menuX, y + anchorItem.height + Theme.spacingS, false);
|
||||
return;
|
||||
}
|
||||
contextMenu.open(screen, menuX, aboveY(), false);
|
||||
contextMenu.anchorY = Qt.binding(aboveY);
|
||||
});
|
||||
}
|
||||
|
||||
function close() {
|
||||
widgetIndex = -1;
|
||||
if (!contextMenu.renderActive) {
|
||||
widgetIndex = -1;
|
||||
_anchor = null;
|
||||
return;
|
||||
}
|
||||
contextMenu.hide();
|
||||
}
|
||||
|
||||
function toggleAction(id, enabled) {
|
||||
@@ -70,120 +83,25 @@ Item {
|
||||
SettingsData.set("controlCenterWidgets", widgets);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.visible
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onClicked: root.close()
|
||||
onWheel: wheel => wheel.accepted = true
|
||||
}
|
||||
DankContextMenu {
|
||||
id: contextMenu
|
||||
layerNamespace: "dms:control-center-widget-options"
|
||||
minMenuWidth: CcMetrics.configMenuWidth
|
||||
customContentWidth: CcMetrics.configMenuWidth
|
||||
keyboardNavigable: true
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
|
||||
FocusScope {
|
||||
id: focusScope
|
||||
anchors.fill: parent
|
||||
focus: root.visible
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
root.close();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: panel
|
||||
|
||||
readonly property real preferredY: root.anchorY - height - Theme.spacingS < Theme.spacingS ? root.anchorY + root.anchorHeight + Theme.spacingS : root.anchorY - height - Theme.spacingS
|
||||
|
||||
width: CcMetrics.configMenuWidth
|
||||
height: Math.min(menu.implicitHeight, root.height - Theme.spacingS * 4) + Theme.spacingS * 2
|
||||
radius: Theme.windowRadius
|
||||
color: CcMetrics.dialogColor
|
||||
border.width: Theme.layerOutlineWidth
|
||||
border.color: Theme.outlineMedium
|
||||
x: Math.max(Theme.spacingS, Math.min(root.anchorX + root.anchorWidth - width, root.width - width - Theme.spacingS))
|
||||
y: Math.max(Theme.spacingS, Math.min(preferredY, root.height - height - Theme.spacingS))
|
||||
opacity: root.visible ? 1 : 0
|
||||
scale: root.visible ? 1 : CcMetrics.popupEnterScale
|
||||
transformOrigin: Item.TopRight
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: CcMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
onOpenStateChanged: {
|
||||
if (openState)
|
||||
return;
|
||||
root.widgetIndex = -1;
|
||||
if (root._anchor?.visible && root._anchor.enabled)
|
||||
root._anchor.forceActiveFocus();
|
||||
root._anchor = null;
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
enabled: CcMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveFastSpatial
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
// The popout is not a real subsurface, so fake compositor blur the same way the detail dialog does, but from the sheet content it covers.
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
active: root.visible && root.backdrop !== null && CcMetrics.hideCoveredContent
|
||||
|
||||
sourceComponent: ClippingRectangle {
|
||||
id: clipArea
|
||||
|
||||
// Children land in an internal content item, so `parent` never reaches this pad.
|
||||
readonly property real pad: CcMetrics.backdropBlurRadius
|
||||
|
||||
radius: panel.radius
|
||||
color: "transparent"
|
||||
|
||||
ShaderEffectSource {
|
||||
id: backdropSource
|
||||
|
||||
x: -clipArea.pad
|
||||
y: -clipArea.pad
|
||||
width: clipArea.width + clipArea.pad * 2
|
||||
height: clipArea.height + clipArea.pad * 2
|
||||
visible: false
|
||||
sourceItem: root.backdrop
|
||||
// mapFromItem is not reactive, so the capture is rebuilt from the panel's own geometry.
|
||||
sourceRect: {
|
||||
const origin = root.backdrop.mapFromItem(root, panel.x - clipArea.pad, panel.y - clipArea.pad);
|
||||
return Qt.rect(origin.x, origin.y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: backdropSource
|
||||
source: backdropSource
|
||||
blurEnabled: true
|
||||
blur: 1
|
||||
blurMax: CcMetrics.backdropBlurRadius
|
||||
autoPaddingEnabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onClicked: mouse => mouse.accepted = true
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
clip: true
|
||||
contentWidth: width
|
||||
contentHeight: menu.implicitHeight
|
||||
interactive: contentHeight > height
|
||||
|
||||
customContent: Component {
|
||||
CcGroup {
|
||||
id: menu
|
||||
width: parent.width
|
||||
|
||||
CcListRow {
|
||||
visible: root.isPlugin
|
||||
iconName: "settings"
|
||||
@@ -239,12 +157,19 @@ Item {
|
||||
}
|
||||
|
||||
CcToggleRow {
|
||||
visible: root.isUser
|
||||
visible: root.isUser || root.isQuickActions
|
||||
text: I18n.tr("Background")
|
||||
checked: root.widgetData?.background !== false
|
||||
checked: root.isUser ? root.widgetData?.background !== false : root.widgetData?.background === true
|
||||
onToggled: checked => root.persistOption("background", checked)
|
||||
}
|
||||
|
||||
CcToggleRow {
|
||||
visible: root.isQuickActions && root.widgetData?.background === true
|
||||
text: I18n.tr("Button backgrounds")
|
||||
checked: root.widgetData?.buttonBackgrounds === true
|
||||
onToggled: checked => root.persistOption("buttonBackgrounds", checked)
|
||||
}
|
||||
|
||||
CcToggleRow {
|
||||
visible: root.isQuickActions
|
||||
text: I18n.tr("Highlight power", "toggle that gives the control center power button the error color")
|
||||
@@ -268,6 +193,7 @@ Item {
|
||||
|
||||
compactMode: true
|
||||
dropdownWidth: parent.width
|
||||
transientSurfaceTracker: contextMenu.transientSurfaceTracker
|
||||
currentValue: presets.find(p => p.minutes === (root.widgetData?.durationMinutes ?? 0))?.label ?? ""
|
||||
options: presets.map(p => p.label)
|
||||
onValueChanged: value => {
|
||||
|
||||
@@ -334,6 +334,7 @@ FocusScope {
|
||||
model: widgetModel
|
||||
screenName: root.host.triggerScreen?.name || ""
|
||||
screenModel: root.host.triggerScreen?.model || ""
|
||||
transientSurfaceTracker: root.host.transientSurfaceTracker
|
||||
onCodecSelectorRequested: device => root.showCodecSelector(device)
|
||||
onPortSelectorRequested: node => root.showPortSelector(node)
|
||||
onBackRequested: root.goBack()
|
||||
@@ -376,11 +377,9 @@ FocusScope {
|
||||
Loader {
|
||||
id: configOverlayLoader
|
||||
|
||||
anchors.fill: parent
|
||||
z: CcMetrics.overlayZ
|
||||
active: false
|
||||
sourceComponent: WidgetConfigOverlay {
|
||||
backdrop: contentFlickable
|
||||
transientSurfaceTracker: root.host.transientSurfaceTracker
|
||||
onVisibleChanged: {
|
||||
if (visible)
|
||||
return;
|
||||
|
||||
@@ -16,7 +16,7 @@ Item {
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property Item menuParent: root
|
||||
property var transientSurfaceTracker: null
|
||||
|
||||
readonly property string title: I18n.tr("Bluetooth")
|
||||
readonly property var adapter: BluetoothService.adapter
|
||||
@@ -362,7 +362,7 @@ Item {
|
||||
|
||||
CcMenu {
|
||||
id: deviceMenu
|
||||
parent: root.menuParent
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
@@ -18,7 +18,7 @@ Item {
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property Item menuParent: root
|
||||
property var transientSurfaceTracker: null
|
||||
|
||||
readonly property string title: I18n.tr("Network")
|
||||
|
||||
@@ -653,12 +653,12 @@ Item {
|
||||
|
||||
CcMenu {
|
||||
id: wifiMenu
|
||||
parent: root.menuParent
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
}
|
||||
|
||||
CcMenu {
|
||||
id: wiredMenu
|
||||
parent: root.menuParent
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Modules.ControlCenter
|
||||
import qs.Widgets
|
||||
@@ -9,221 +10,60 @@ Item {
|
||||
id: root
|
||||
|
||||
property var items: []
|
||||
readonly property bool open: _open
|
||||
property bool _open: false
|
||||
property var _anchor: null
|
||||
property int _focusIndex: -1
|
||||
|
||||
property var transientSurfaceTracker: null
|
||||
property Item _anchor: null
|
||||
readonly property bool open: menu.openState
|
||||
readonly property var visibleItems: (items || []).filter(item => item.visible !== false)
|
||||
|
||||
signal closed
|
||||
|
||||
anchors.fill: parent
|
||||
visible: _open || panel.opacity > 0
|
||||
z: CcMetrics.overlayZ
|
||||
|
||||
function _measure() {
|
||||
let widest = 0;
|
||||
let hasIcon = false;
|
||||
for (const item of visibleItems) {
|
||||
labelMetrics.text = item.label || "";
|
||||
widest = Math.max(widest, labelMetrics.advanceWidth);
|
||||
hasIcon = hasIcon || !!item.iconName;
|
||||
}
|
||||
const iconSpan = hasIcon ? Theme.iconSizeMedium + Theme.spacingM : 0;
|
||||
panel.width = Math.max(CcMetrics.menuMinWidth, widest + iconSpan + Theme.spacingM * 2 + Theme.spacingS * 2);
|
||||
}
|
||||
|
||||
function openAt(anchor) {
|
||||
const window = root.QsWindow.window;
|
||||
const screen = window?.screen;
|
||||
if (!anchor || !screen)
|
||||
return;
|
||||
|
||||
_anchor = anchor;
|
||||
_focusIndex = -1;
|
||||
_measure();
|
||||
_reposition();
|
||||
_open = true;
|
||||
focusScope.forceActiveFocus();
|
||||
const pos = anchor.mapToGlobal(0, 0);
|
||||
const x = pos.x - screen.x;
|
||||
const y = pos.y - screen.y;
|
||||
const menuX = I18n.isRtl ? x : x + anchor.width - menu.effectiveMenuWidth;
|
||||
const below = y + anchor.height + Theme.spacingXS;
|
||||
const menuY = below + menu.effectiveMenuHeight > screen.height - Theme.spacingS
|
||||
? y - menu.effectiveMenuHeight - Theme.spacingXS : below;
|
||||
menu.open(screen, menuX, menuY, false);
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (!_open)
|
||||
return;
|
||||
_open = false;
|
||||
closed();
|
||||
if (_anchor)
|
||||
_anchor.forceActiveFocus();
|
||||
menu.hide();
|
||||
}
|
||||
|
||||
function _reposition() {
|
||||
if (!_anchor)
|
||||
return;
|
||||
const pos = _anchor.mapToItem(root, 0, 0);
|
||||
const maxX = root.width - panel.width - Theme.spacingS;
|
||||
const preferredX = I18n.isRtl ? pos.x : pos.x + _anchor.width - panel.width;
|
||||
panel.x = Math.max(Theme.spacingS, Math.min(preferredX, maxX));
|
||||
let y = pos.y + _anchor.height + Theme.spacingXS;
|
||||
if (y + panel.height > root.height - Theme.spacingS)
|
||||
y = pos.y - panel.height - Theme.spacingXS;
|
||||
panel.y = Math.max(Theme.spacingS, y);
|
||||
}
|
||||
DankContextMenu {
|
||||
id: menu
|
||||
layerNamespace: "dms:control-center-menu"
|
||||
minMenuWidth: CcMetrics.menuMinWidth
|
||||
keyboardNavigable: true
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
menuItems: root.visibleItems.map(item => ({
|
||||
type: "item",
|
||||
icon: item.iconName || "",
|
||||
text: item.label || "",
|
||||
enabled: item.enabled !== false,
|
||||
isDestructive: item.destructive === true,
|
||||
action: () => {
|
||||
menu.hide();
|
||||
if (typeof item.action === "function")
|
||||
item.action();
|
||||
}
|
||||
}))
|
||||
|
||||
function _activate(index) {
|
||||
const item = visibleItems[index];
|
||||
if (!item || item.enabled === false)
|
||||
return;
|
||||
close();
|
||||
if (typeof item.action === "function")
|
||||
item.action();
|
||||
}
|
||||
|
||||
function _moveFocus(delta) {
|
||||
const count = visibleItems.length;
|
||||
if (count === 0)
|
||||
return;
|
||||
let next = _focusIndex;
|
||||
for (let i = 0; i < count; i++) {
|
||||
next = (next + delta + count) % count;
|
||||
if (visibleItems[next].enabled !== false)
|
||||
break;
|
||||
}
|
||||
_focusIndex = next;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root._open
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onClicked: root.close()
|
||||
onWheel: wheel => wheel.accepted = true
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: focusScope
|
||||
anchors.fill: parent
|
||||
focus: root._open
|
||||
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
root.close();
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
root._moveFocus(1);
|
||||
break;
|
||||
case Qt.Key_Up:
|
||||
root._moveFocus(-1);
|
||||
break;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Space:
|
||||
root._activate(root._focusIndex);
|
||||
break;
|
||||
default:
|
||||
onOpenStateChanged: {
|
||||
if (openState)
|
||||
return;
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
StyledTextMetrics {
|
||||
id: labelMetrics
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: panel
|
||||
width: CcMetrics.menuMinWidth
|
||||
height: root.visibleItems.length * Theme.menuItemHeight + Theme.spacingS * 2
|
||||
radius: Theme.windowRadius
|
||||
color: Theme.foregroundColor(Theme.chipSurface, Theme.isFloatingWindow(root))
|
||||
border.width: Theme.layerOutlineWidth
|
||||
border.color: Theme.outlineMedium
|
||||
opacity: root._open ? 1 : 0
|
||||
scale: root._open ? 1 : CcMetrics.popupEnterScale
|
||||
transformOrigin: I18n.isRtl ? Item.TopLeft : Item.TopRight
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: CcMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
enabled: CcMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveFastSpatial
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onClicked: mouse => mouse.accepted = true
|
||||
onWheel: wheel => wheel.accepted = true
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacingS
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
|
||||
Repeater {
|
||||
model: root.visibleItems
|
||||
|
||||
Rectangle {
|
||||
id: row
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
readonly property bool itemEnabled: modelData.enabled !== false
|
||||
readonly property color contentColor: {
|
||||
if (!itemEnabled)
|
||||
return Theme.onSurface_38;
|
||||
return modelData.destructive ? Theme.error : Theme.surfaceText;
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
height: Theme.menuItemHeight
|
||||
radius: Theme.cornerRadiusS
|
||||
color: root._focusIndex === index && !rowLayer.containsMouse ? Theme.withAlpha(contentColor, Theme.stateLayerFocus) : "transparent"
|
||||
|
||||
Row {
|
||||
id: rowContent
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: row.modelData.iconName || ""
|
||||
size: Theme.iconSizeMedium
|
||||
color: row.contentColor
|
||||
visible: name !== ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: row.modelData.label || ""
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: row.contentColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: rowLayer
|
||||
disabled: !row.itemEnabled
|
||||
stateColor: row.contentColor
|
||||
cornerRadius: row.radius
|
||||
onClicked: root._activate(row.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
root.closed();
|
||||
if (root._anchor?.visible && root._anchor.enabled)
|
||||
root._anchor.forceActiveFocus();
|
||||
root._anchor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ Item {
|
||||
property bool shown: false
|
||||
property real panelWidth: CcMetrics.dialogWidth
|
||||
property vector4d cornerRadii: Qt.vector4d(Theme.windowRadius, Theme.windowRadius, Theme.windowRadius, Theme.windowRadius)
|
||||
property Item backdrop: null
|
||||
readonly property real presence: panel.opacity
|
||||
default property alias content: contentSlot.data
|
||||
|
||||
@@ -112,6 +113,17 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// In-window sheets get no compositor blur, so blur the covered content behind the panel only.
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
active: root.backdrop !== null && root.visible && CcMetrics.hideCoveredContent
|
||||
sourceComponent: BackdropBlur {
|
||||
radius: panel.radius
|
||||
sourceItem: root.backdrop
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
@@ -21,7 +21,9 @@ Item {
|
||||
readonly property Item passthrough: actionGrid
|
||||
readonly property bool editMode: host?.editMode ?? false
|
||||
readonly property var actions: WidgetUtils.enabledQuickActions(widgetData)
|
||||
readonly property real bodyRadius: Theme.cornerRadiusL
|
||||
readonly property bool background: widgetData?.background === true
|
||||
readonly property bool buttonBackgrounds: widgetData?.buttonBackgrounds === true
|
||||
readonly property real bodyRadius: background ? Theme.cornerRadiusXL : Theme.cornerRadiusL
|
||||
readonly property int actionColumns: Math.max(1, Math.min(actions.length, Math.floor((width + CcMetrics.actionGap) / (CcMetrics.actionSize + CcMetrics.actionGap))))
|
||||
|
||||
width: parent?.width ?? 0
|
||||
@@ -46,6 +48,15 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: root.bodyRadius
|
||||
color: Theme.foregroundColor(Theme.cardSurface)
|
||||
border.width: Theme.layerOutlineWidth
|
||||
border.color: Theme.outlineMedium
|
||||
visible: root.background
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: actionGrid
|
||||
|
||||
@@ -64,10 +75,10 @@ Item {
|
||||
|
||||
buttonSize: CcMetrics.actionSize
|
||||
iconSize: CcMetrics.actionIconSize
|
||||
circular: false
|
||||
circular: root.background
|
||||
iconName: WidgetUtils.quickActionIcon(actionId)
|
||||
iconColor: editing ? Theme.onPrimary : destructive ? Theme.error : Theme.onSurface
|
||||
backgroundColor: editing ? Theme.primary : Theme.foregroundColor(Theme.cardSurface)
|
||||
backgroundColor: editing ? Theme.primary : root.background ? (root.buttonBackgrounds ? Theme.foregroundColor(Theme.chipSurface) : "transparent") : Theme.foregroundColor(Theme.cardSurface)
|
||||
tooltipText: I18n.tr(WidgetUtils.quickActionLabel(actionId))
|
||||
checkable: actionId === "edit"
|
||||
checked: editing
|
||||
|
||||
@@ -562,6 +562,7 @@ DankPopout {
|
||||
|
||||
DashOptionsSheet {
|
||||
id: tabOptions
|
||||
backdrop: contentColumn
|
||||
onDismissed: mainContainer.focusInitial()
|
||||
}
|
||||
|
||||
@@ -656,7 +657,7 @@ DankPopout {
|
||||
height: Math.min(parent.height, implicitHeight)
|
||||
visible: root.editMode
|
||||
vertical: root.verticalNavigation
|
||||
overlayParent: mainContainer
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
entryId: root.activeTabId
|
||||
tabItem: pages.currentItem
|
||||
editMode: root.editMode
|
||||
|
||||
@@ -11,7 +11,7 @@ Item {
|
||||
id: root
|
||||
|
||||
required property string entryId
|
||||
required property Item overlayParent
|
||||
property var transientSurfaceTracker: null
|
||||
property var tabItem: null
|
||||
property bool vertical: false
|
||||
property bool editMode: false
|
||||
@@ -56,7 +56,7 @@ Item {
|
||||
|
||||
CcMenu {
|
||||
id: customMenu
|
||||
parent: root.overlayParent
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
items: root.tabItem?.menuActions ?? []
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ Item {
|
||||
property bool live: Window.window?.visible ?? false
|
||||
property int columns: width < Theme.smallBreakpoint ? 2 : 4
|
||||
property real cardRadius: Theme.cornerRadiusXL
|
||||
property var transientSurfaceTracker: null
|
||||
readonly property var widgets: WidgetUtils.resolve(definitions, DashRegistry.widgetLayout(entryId))
|
||||
readonly property var addable: definitions.filter(d => !widgets.some(w => w.id === d.id))
|
||||
readonly property Item focusTarget: grid
|
||||
@@ -200,7 +201,7 @@ Item {
|
||||
|
||||
CcMenu {
|
||||
id: addMenu
|
||||
parent: root.Window.window?.contentItem ?? root
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
items: root.addable.map(spec => ({
|
||||
label: spec.text,
|
||||
iconName: spec.icon,
|
||||
@@ -214,7 +215,7 @@ Item {
|
||||
|
||||
CcMenu {
|
||||
id: widgetMenu
|
||||
parent: root.Window.window?.contentItem ?? root
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
property string widgetId: ""
|
||||
readonly property int widgetIndex: root.widgets.findIndex(w => w.id === widgetId)
|
||||
readonly property var widget: root.widgets[widgetIndex] ?? {}
|
||||
|
||||
@@ -111,6 +111,7 @@ FocusScope {
|
||||
|
||||
CcMenu {
|
||||
id: addMenu
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
|
||||
items: root.addable.map(candidate => ({
|
||||
"label": candidate.entry.card.text,
|
||||
@@ -121,11 +122,13 @@ FocusScope {
|
||||
|
||||
DashOptionsSheet {
|
||||
id: optionsSheet
|
||||
backdrop: grid
|
||||
onDismissed: root.navFocusRequested(false)
|
||||
}
|
||||
|
||||
CcSheetDialog {
|
||||
id: detailSheet
|
||||
backdrop: grid
|
||||
|
||||
property var eventData: null
|
||||
|
||||
@@ -168,6 +171,7 @@ FocusScope {
|
||||
|
||||
CcSheetDialog {
|
||||
id: editorSheet
|
||||
backdrop: grid
|
||||
|
||||
property var eventData: null
|
||||
property date initialDate: new Date()
|
||||
|
||||
@@ -15,6 +15,7 @@ Item {
|
||||
property bool live: Window.window?.visible ?? false
|
||||
property bool editMode: false
|
||||
property bool weatherRefHeld: false
|
||||
property var transientSurfaceTracker: null
|
||||
readonly property var options: DashRegistry.resolvedOptions(entryId)
|
||||
readonly property bool available: WeatherService.weather.available
|
||||
readonly property var addable: widgets.addable
|
||||
@@ -69,6 +70,7 @@ Item {
|
||||
id: widgets
|
||||
anchors.fill: parent
|
||||
entryId: root.entryId
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
live: root.live && root.available
|
||||
editMode: root.editMode
|
||||
visible: root.available || root.editMode
|
||||
|
||||
@@ -70,6 +70,7 @@ FocusScope {
|
||||
readonly property var triggerScreen: root.effectiveScreen
|
||||
readonly property var colorPickerModal: PopoutService.colorPickerModal
|
||||
readonly property var powerMenuModalLoader: PopoutService.powerMenuModalLoader
|
||||
readonly property var transientSurfaceTracker: root.controller.transientSurfaces
|
||||
readonly property real alignedX: root.alignedX
|
||||
readonly property real alignedY: root.alignedY
|
||||
readonly property real alignedWidth: root.alignedWidth
|
||||
|
||||
@@ -174,7 +174,7 @@ FocusScope {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Math.min(parent.width, implicitWidth)
|
||||
height: parent.height
|
||||
overlayParent: root
|
||||
transientSurfaceTracker: root.controller.transientSurfaces
|
||||
entryId: root.entryId
|
||||
tabItem: root.tab
|
||||
editMode: root.editMode
|
||||
@@ -256,6 +256,7 @@ FocusScope {
|
||||
|
||||
DashOptionsSheet {
|
||||
id: tabOptions
|
||||
backdrop: pages
|
||||
onDismissed: root.focusFace()
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ DashTabFace {
|
||||
WeatherTab {
|
||||
live: root.live
|
||||
editMode: root.editMode
|
||||
transientSurfaceTracker: root.controller.transientSurfaces
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ Item {
|
||||
property var pluginService: null
|
||||
property var popoutService: null
|
||||
property var dashHost: null
|
||||
property var transientSurfaceTracker: null
|
||||
property string entryId: ""
|
||||
property Item focusTarget: null
|
||||
property bool editMode: false
|
||||
@@ -31,6 +32,13 @@ Item {
|
||||
onPluginServiceChanged: loadPluginData()
|
||||
onPluginIdChanged: loadPluginData()
|
||||
|
||||
Binding {
|
||||
target: root.widgetGrid
|
||||
property: "transientSurfaceTracker"
|
||||
value: root.transientSurfaceTracker
|
||||
when: root.widgetGrid !== null
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.pluginService
|
||||
enabled: root.pluginService !== null
|
||||
|
||||
@@ -36,6 +36,7 @@ Item {
|
||||
visible: root.blurActive
|
||||
blurEnabled: root.blurActive
|
||||
blurMax: root.blurMax
|
||||
autoPaddingEnabled: false
|
||||
blur: root.blurAmount
|
||||
maskEnabled: true
|
||||
maskSource: maskRect
|
||||
|
||||
@@ -15,6 +15,8 @@ Item {
|
||||
height: 0
|
||||
|
||||
property var menuItems: []
|
||||
property Component customContent: null
|
||||
property real customContentWidth: 0
|
||||
property string layerNamespace: "dms:context-menu"
|
||||
property real menuMargin: Theme.spacingS
|
||||
property real minMenuWidth: Theme.fieldDefaultWidth
|
||||
@@ -41,9 +43,9 @@ Item {
|
||||
}
|
||||
return longest;
|
||||
}
|
||||
readonly property real naturalMenuWidth: Math.max(minMenuWidth, menuTextMetrics.width + Theme.iconSize + Theme.spacingS * 5)
|
||||
readonly property real naturalMenuWidth: customContent ? Math.max(minMenuWidth, customContentWidth) : Math.max(minMenuWidth, menuTextMetrics.width + Theme.iconSize + Theme.spacingS * 5)
|
||||
readonly property real effectiveMenuWidth: Math.max(0, Math.min(maxMenuWidth, naturalMenuWidth))
|
||||
readonly property real naturalMenuHeight: menuItemsHeight() + Theme.spacingS * 2
|
||||
readonly property real naturalMenuHeight: (customContent ? customContentLoader.implicitHeight : menuItemsHeight()) + Theme.spacingS * 2
|
||||
readonly property real effectiveMenuHeight: Math.min(maxMenuHeight, naturalMenuHeight)
|
||||
readonly property bool menuScrolls: naturalMenuHeight > effectiveMenuHeight + 0.5
|
||||
readonly property int visibleItemCount: menuItems.filter(menuItem => menuItem.type === "item").length
|
||||
@@ -113,6 +115,8 @@ Item {
|
||||
}
|
||||
|
||||
function activate(menuItem) {
|
||||
if (menuItem?.enabled === false)
|
||||
return;
|
||||
if (typeof menuItem?.action === "function")
|
||||
menuItem.action();
|
||||
hide();
|
||||
@@ -122,7 +126,11 @@ Item {
|
||||
if (visibleItemCount === 0)
|
||||
return;
|
||||
keyboardNavigation = true;
|
||||
selectedMenuIndex = (selectedMenuIndex + 1) % visibleItemCount;
|
||||
for (let step = 0; step < visibleItemCount; step++) {
|
||||
selectedMenuIndex = (selectedMenuIndex + 1) % visibleItemCount;
|
||||
if (menuItems[selectedDelegateIndex()]?.enabled !== false)
|
||||
break;
|
||||
}
|
||||
ensureSelectedVisible();
|
||||
}
|
||||
|
||||
@@ -130,7 +138,11 @@ Item {
|
||||
if (visibleItemCount === 0)
|
||||
return;
|
||||
keyboardNavigation = true;
|
||||
selectedMenuIndex = (selectedMenuIndex - 1 + visibleItemCount) % visibleItemCount;
|
||||
for (let step = 0; step < visibleItemCount; step++) {
|
||||
selectedMenuIndex = (selectedMenuIndex - 1 + visibleItemCount) % visibleItemCount;
|
||||
if (menuItems[selectedDelegateIndex()]?.enabled !== false)
|
||||
break;
|
||||
}
|
||||
ensureSelectedVisible();
|
||||
}
|
||||
|
||||
@@ -295,9 +307,17 @@ Item {
|
||||
width: menuFlickable.width
|
||||
spacing: Theme.groupedListGap
|
||||
|
||||
Loader {
|
||||
id: customContentLoader
|
||||
width: menuColumn.width
|
||||
active: root.customContent !== null
|
||||
visible: active
|
||||
sourceComponent: root.customContent
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: menuRepeater
|
||||
model: root.menuItems
|
||||
model: root.customContent ? [] : root.menuItems
|
||||
|
||||
Item {
|
||||
id: menuItemDelegate
|
||||
@@ -322,7 +342,10 @@ Item {
|
||||
id: menuRow
|
||||
readonly property bool selected: root.keyboardNavigation && root.selectedMenuIndex === menuItemDelegate.itemIndex
|
||||
readonly property bool destructive: menuItemDelegate.modelData?.isDestructive ?? false
|
||||
readonly property bool itemEnabled: menuItemDelegate.modelData?.enabled !== false
|
||||
readonly property color contentColor: {
|
||||
if (!itemEnabled)
|
||||
return Theme.onSurface_38;
|
||||
if (destructive)
|
||||
return Theme.error;
|
||||
return selected ? Theme.onSelectedContainer : Theme.onSurface;
|
||||
@@ -331,6 +354,8 @@ Item {
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadiusS
|
||||
color: {
|
||||
if (!itemEnabled)
|
||||
return "transparent";
|
||||
if (destructive)
|
||||
return selected ? Theme.errorSelected : itemMouseArea.containsMouse ? Theme.errorHover : "transparent";
|
||||
return selected ? Theme.selectedContainer : itemMouseArea.pressed ? Theme.withAlpha(Theme.onSurface, Theme.stateLayerPressed) : itemMouseArea.containsMouse ? Theme.withAlpha(Theme.onSurface, Theme.stateLayerHover) : "transparent";
|
||||
@@ -371,6 +396,7 @@ Item {
|
||||
MouseArea {
|
||||
id: itemMouseArea
|
||||
anchors.fill: parent
|
||||
enabled: menuRow.itemEnabled
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: {
|
||||
|
||||
Reference in new issue
Block a user