refactor(cc / blur): restore card background on quick controls & unify blur popouts on Dash/CC

This commit is contained in:
purian23 committed 2026-09-29 01:39:58 -04:00
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
+1
View File
@@ -36,6 +36,7 @@ Item {
visible: root.blurActive
blurEnabled: root.blurActive
blurMax: root.blurMax
autoPaddingEnabled: false
blur: root.blurAmount
maskEnabled: true
maskSource: maskRect
+31 -5
View File
@@ -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: {