1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-16 16:15:23 -04:00

Compare commits

..

7 Commits

Author SHA1 Message Date
purian23 8d94117a69 Cleanup 2026-06-12 10:56:16 -04:00
purian23 92569d8b4d Refactor connected chrome rendering & remove legacy components 2026-06-12 10:56:16 -04:00
purian23 fdee09b583 refactor: enhance plugin visibility w/bar reveal state 2026-06-12 10:56:16 -04:00
purian23 b60af507d7 refactor: implement keyboard focus management 2026-06-12 10:56:16 -04:00
purian23 2cc12b70d2 Update frameBlur performance 2026-06-12 10:56:15 -04:00
purian23 2df1dfe0bd Refactor shadow handling & improve connected chrome rendering 2026-06-12 10:56:15 -04:00
purian23 abf084eea2 refactor(framemode): connected surfaces 2026-06-12 10:56:15 -04:00
11 changed files with 40 additions and 165 deletions
-10
View File
@@ -108,7 +108,6 @@ Singleton {
} }
property bool clipboardEnterToPaste: false property bool clipboardEnterToPaste: false
property var clipboardVisibleEntryActions: ["pin", "edit", "delete"]
property var launcherPluginVisibility: ({}) property var launcherPluginVisibility: ({})
@@ -1651,15 +1650,6 @@ Singleton {
}; };
} }
function effectiveBarConfigForRender(config, usesFrameBarChrome) {
if (!config || !connectedFrameModeActive || usesFrameBarChrome)
return config;
const backup = connectedFrameBarStyleBackups[config.id];
if (!backup)
return config;
return Object.assign({}, config, backup);
}
// Single entry point for connected-mode settings state. // Single entry point for connected-mode settings state.
// !active → restore backups // !active → restore backups
function _reconcileConnectedFrameBarStyles() { function _reconcileConnectedFrameBarStyles() {
@@ -572,7 +572,6 @@ var SPEC = {
builtInPluginSettings: { def: {} }, builtInPluginSettings: { def: {} },
clipboardEnterToPaste: { def: false }, clipboardEnterToPaste: { def: false },
clipboardVisibleEntryActions: { def: ["pin", "edit", "delete"] },
launcherPluginVisibility: { def: {} }, launcherPluginVisibility: { def: {} },
launcherPluginOrder: { def: [] }, launcherPluginOrder: { def: [] },
@@ -7,6 +7,7 @@ Item {
id: clipboardContent id: clipboardContent
required property var modal required property var modal
required property var clearConfirmDialog
property alias searchField: searchField property alias searchField: searchField
property alias clipboardListView: clipboardListView property alias clipboardListView: clipboardListView
@@ -32,7 +33,14 @@ Item {
pinnedCount: modal.pinnedCount pinnedCount: modal.pinnedCount
onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints
onTabChanged: tabName => modal.activeTab = tabName onTabChanged: tabName => modal.activeTab = tabName
onClearAllClicked: modal.confirmClearAll() onClearAllClicked: {
const hasPinned = modal.pinnedCount > 0;
const message = hasPinned ? I18n.tr("This will delete all unpinned entries. %1 pinned entries will be kept.").arg(modal.pinnedCount) : I18n.tr("This will permanently delete all clipboard history.");
clearConfirmDialog.show(I18n.tr("Clear History?"), message, function () {
modal.clearAll();
modal.hide();
}, function () {});
}
onCloseClicked: modal.hide() onCloseClicked: modal.hide()
} }
+7 -35
View File
@@ -22,14 +22,7 @@ Rectangle {
readonly property string entryType: modal ? modal.getEntryType(entry) : "text" readonly property string entryType: modal ? modal.getEntryType(entry) : "text"
readonly property string entryPreview: modal ? modal.getEntryPreview(entry) : "" readonly property string entryPreview: modal ? modal.getEntryPreview(entry) : ""
readonly property var pinnedDuplicateEntry: !entry.pinned ? ClipboardService.getPinnedEntryByHash(entry.hash) : null readonly property var pinnedDuplicateEntry: !entry.pinned ? ClipboardService.getPinnedEntryByHash(entry.hash) : null
readonly property bool hasPinnedDuplicate: pinnedDuplicateEntry !== null readonly property bool effectivePinned: entry.pinned || pinnedDuplicateEntry !== null
readonly property bool effectivePinned: entry.pinned || hasPinnedDuplicate
readonly property var visibleEntryActions: SettingsData.clipboardVisibleEntryActions || ["pin", "edit", "delete"]
readonly property bool showPinAction: visibleEntryActions.includes("pin")
readonly property bool showEditAction: visibleEntryActions.includes("edit")
readonly property bool showDeleteAction: visibleEntryActions.includes("delete")
readonly property bool showPinnedIndicator: !showPinAction && effectivePinned
readonly property bool showAnyAction: showPinAction || showEditAction || showDeleteAction || showPinnedIndicator
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
@@ -70,31 +63,12 @@ Rectangle {
anchors.rightMargin: Theme.spacingS anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS spacing: Theme.spacingXS
visible: root.showAnyAction
DankActionButton { DankActionButton {
iconName: "push_pin" iconName: "push_pin"
iconSize: Theme.iconSize - 6 iconSize: Theme.iconSize - 6
iconColor: Theme.primary iconColor: effectivePinned ? Theme.primary : Theme.surfaceText
backgroundColor: Theme.primarySelected backgroundColor: effectivePinned ? Theme.primarySelected : "transparent"
visible: root.showPinnedIndicator
onClicked: {
if (entry.pinned) {
unpinRequested(entry);
return;
}
if (pinnedDuplicateEntry) {
unpinRequested(pinnedDuplicateEntry);
}
}
}
DankActionButton {
iconName: "push_pin"
iconSize: Theme.iconSize - 6
iconColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primary : Theme.surfaceText
backgroundColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primarySelected : "transparent"
visible: root.showPinAction
onClicked: { onClicked: {
if (entry.pinned) { if (entry.pinned) {
unpinRequested(entry); unpinRequested(entry);
@@ -112,7 +86,6 @@ Rectangle {
iconName: "edit" iconName: "edit"
iconSize: Theme.iconSize - 6 iconSize: Theme.iconSize - 6
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
visible: root.showEditAction
onClicked: { onClicked: {
if (entryType === "image") { if (entryType === "image") {
@@ -126,7 +99,6 @@ Rectangle {
iconName: "close" iconName: "close"
iconSize: Theme.iconSize - 6 iconSize: Theme.iconSize - 6
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
visible: root.showDeleteAction
onClicked: deleteRequested() onClicked: deleteRequested()
} }
} }
@@ -134,8 +106,8 @@ Rectangle {
Item { Item {
anchors.left: indexBadge.right anchors.left: indexBadge.right
anchors.leftMargin: Theme.spacingM anchors.leftMargin: Theme.spacingM
anchors.right: root.showAnyAction ? actionButtons.left : parent.right anchors.right: actionButtons.left
anchors.rightMargin: root.showAnyAction ? Theme.spacingM : Theme.spacingS anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// height: contentColumn.implicitHeight // height: contentColumn.implicitHeight
height: ClipboardConstants.itemHeight height: ClipboardConstants.itemHeight
@@ -196,8 +168,8 @@ Rectangle {
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.left: parent.left anchors.left: parent.left
anchors.right: root.showAnyAction ? actionButtons.left : parent.right anchors.right: actionButtons.left
anchors.rightMargin: root.showAnyAction ? Theme.spacingS : 0 anchors.rightMargin: Theme.spacingS
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
hoverEnabled: true hoverEnabled: true
@@ -82,15 +82,6 @@ FocusScope {
ClipboardService.clearAll(); ClipboardService.clearAll();
} }
function confirmClearAll() {
const hasPinned = pinnedCount > 0;
const message = hasPinned ? I18n.tr("This will delete all unpinned entries. %1 pinned entries will be kept.").arg(pinnedCount) : I18n.tr("This will permanently delete all clipboard history.");
clearConfirmDialog.show(I18n.tr("Clear History?"), message, function () {
clearAll();
hide();
}, function () {});
}
function getEntryPreview(entry) { function getEntryPreview(entry) {
return ClipboardService.getEntryPreview(entry); return ClipboardService.getEntryPreview(entry);
} }
@@ -144,6 +135,7 @@ FocusScope {
id: historyContent id: historyContent
anchors.fill: parent anchors.fill: parent
modal: root modal: root
clearConfirmDialog: root.clearConfirmDialog
} }
} }
@@ -77,35 +77,22 @@ DankModal {
id: clearConfirmDialog id: clearConfirmDialog
confirmButtonText: I18n.tr("Clear All") confirmButtonText: I18n.tr("Clear All")
confirmButtonColor: Theme.primary confirmButtonColor: Theme.primary
onShouldBeVisibleChanged: { onVisibleChanged: {
if (shouldBeVisible) { if (visible) {
clipboardHistoryModal.shouldHaveFocus = false; clipboardHistoryModal.shouldHaveFocus = false;
selectedButton = 0;
keyboardNavigation = true;
return; return;
} }
Qt.callLater(function () { Qt.callLater(function () {
if (!clipboardHistoryModal.shouldBeVisible) { if (!clipboardHistoryModal.shouldBeVisible) {
return; return;
} }
clipboardHistoryModal.shouldHaveFocus = Qt.binding(() => clipboardHistoryModal.shouldBeVisible); clipboardHistoryModal.shouldHaveFocus = true;
clipboardHistoryModal.modalFocusScope.forceActiveFocus(); clipboardHistoryModal.modalFocusScope.forceActiveFocus();
if (clipboardHistoryModal.contentLoader.item?.searchField) { if (clipboardHistoryModal.contentLoader.item?.searchField) {
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus(); clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
} }
}); });
} }
Connections {
target: clearConfirmDialog.modalFocusScope.Keys
function onPressed(event) {
if (!clearConfirmDialog.shouldBeVisible || event.key !== Qt.Key_Backtab) {
return;
}
clearConfirmDialog.selectedButton = clearConfirmDialog.selectedButton === -1 ? 1 : (clearConfirmDialog.selectedButton - 1 + 2) % 2;
clearConfirmDialog.keyboardNavigation = true;
event.accepted = true;
}
}
} }
content: Component { content: Component {
@@ -1,7 +1,6 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Modals.Clipboard import qs.Modals.Clipboard
import qs.Modals.Common import qs.Modals.Common
@@ -96,35 +95,6 @@ DankPopout {
id: clearConfirmDialog id: clearConfirmDialog
confirmButtonText: I18n.tr("Clear All") confirmButtonText: I18n.tr("Clear All")
confirmButtonColor: Theme.primary confirmButtonColor: Theme.primary
onShouldBeVisibleChanged: {
if (shouldBeVisible) {
root.customKeyboardFocus = WlrKeyboardFocus.None;
selectedButton = 0;
keyboardNavigation = true;
return;
}
root.customKeyboardFocus = null;
Qt.callLater(function () {
if (!root.shouldBeVisible || !root.contentLoader.item) {
return;
}
root.contentLoader.item.forceActiveFocus();
if (root.contentLoader.item.searchField) {
root.contentLoader.item.searchField.forceActiveFocus();
}
});
}
Connections {
target: clearConfirmDialog.modalFocusScope.Keys
function onPressed(event) {
if (!clearConfirmDialog.shouldBeVisible || event.key !== Qt.Key_Backtab) {
return;
}
clearConfirmDialog.selectedButton = clearConfirmDialog.selectedButton === -1 ? 1 : (clearConfirmDialog.selectedButton - 1 + 2) % 2;
clearConfirmDialog.keyboardNavigation = true;
event.accepted = true;
}
}
} }
content: Component { content: Component {
@@ -125,6 +125,8 @@ QtObject {
if (!ClipboardService.keyboardNavigationActive) { if (!ClipboardService.keyboardNavigationActive) {
ClipboardService.keyboardNavigationActive = true; ClipboardService.keyboardNavigationActive = true;
ClipboardService.selectedIndex = 0; ClipboardService.selectedIndex = 0;
} else if (ClipboardService.selectedIndex === 0) {
ClipboardService.keyboardNavigationActive = false;
} else { } else {
selectPrevious(); selectPrevious();
} }
@@ -153,6 +155,8 @@ QtObject {
if (!ClipboardService.keyboardNavigationActive) { if (!ClipboardService.keyboardNavigationActive) {
ClipboardService.keyboardNavigationActive = true; ClipboardService.keyboardNavigationActive = true;
ClipboardService.selectedIndex = 0; ClipboardService.selectedIndex = 0;
} else if (ClipboardService.selectedIndex === 0) {
ClipboardService.keyboardNavigationActive = false;
} else { } else {
selectPrevious(); selectPrevious();
} }
@@ -180,7 +184,8 @@ QtObject {
if (event.modifiers & Qt.ShiftModifier) { if (event.modifiers & Qt.ShiftModifier) {
switch (event.key) { switch (event.key) {
case Qt.Key_Delete: case Qt.Key_Delete:
modal.confirmClearAll(); modal.clearAll();
modal.hide();
event.accepted = true; event.accepted = true;
return; return;
case Qt.Key_Return: case Qt.Key_Return:
+14 -16
View File
@@ -286,6 +286,9 @@ PanelWindow {
readonly property bool isVertical: axis.isVertical readonly property bool isVertical: axis.isVertical
property bool gothCornersEnabled: barConfig?.gothCornersEnabled ?? false
property real wingtipsRadius: barConfig?.gothCornerRadiusOverride ? (barConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius
readonly property real _wingR: Math.max(0, wingtipsRadius)
readonly property color _surfaceContainer: Theme.surfaceContainer readonly property color _surfaceContainer: Theme.surfaceContainer
readonly property string _barId: barConfig?.id ?? "default" readonly property string _barId: barConfig?.id ?? "default"
property real _backgroundAlpha: barConfig?.transparency ?? 1.0 property real _backgroundAlpha: barConfig?.transparency ?? 1.0
@@ -297,30 +300,25 @@ PanelWindow {
} }
readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen) readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen)
property string screenName: modelData.name
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(screenName)
readonly property bool usesFrameBarChrome: CompositorService.frameWindowVisibleForScreen(screenName)
readonly property var renderBarConfig: SettingsData.effectiveBarConfigForRender(barConfig, usesFrameBarChrome)
property bool gothCornersEnabled: renderBarConfig?.gothCornersEnabled ?? false
property real wingtipsRadius: renderBarConfig?.gothCornerRadiusOverride ? (renderBarConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius
readonly property real _wingR: Math.max(0, wingtipsRadius)
// Shadow buffer: extra window space for shadow to render beyond bar bounds // Shadow buffer: extra window space for shadow to render beyond bar bounds
readonly property bool _shadowActive: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || (renderBarConfig?.shadowIntensity ?? 0) > 0 readonly property bool _shadowActive: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || (barConfig?.shadowIntensity ?? 0) > 0
readonly property real _shadowBuffer: { readonly property real _shadowBuffer: {
if (!_shadowActive) if (!_shadowActive)
return 0; return 0;
const hasOverride = (renderBarConfig?.shadowIntensity ?? 0) > 0; const hasOverride = (barConfig?.shadowIntensity ?? 0) > 0;
if (hasOverride) { if (hasOverride) {
const blur = (renderBarConfig.shadowIntensity ?? 0) * 0.2; const blur = (barConfig.shadowIntensity ?? 0) * 0.2;
const offset = blur * 0.5; const offset = blur * 0.5;
return Theme.snap(Math.max(16, blur + offset + 8), _dpr); return Theme.snap(Math.max(16, blur + offset + 8), _dpr);
} }
return Theme.snap(Theme.elevationRenderPadding(Theme.elevationLevel2, "top", 4, 8, 16), _dpr); return Theme.snap(Theme.elevationRenderPadding(Theme.elevationLevel2, "top", 4, 8, 16), _dpr);
} }
property string screenName: modelData.name
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(screenName)
readonly property bool usesFrameBarChrome: CompositorService.frameWindowVisibleForScreen(screenName)
// Flatten/spacing collapse for maximized windows is only for frame-integrated layout. // Flatten/spacing collapse for maximized windows is only for frame-integrated layout.
// When the bar draws its own pill, keep rounded corners and spacing like the dock. // When the bar draws its own pill, keep rounded corners and spacing like the dock.
readonly property bool flattenForMaximizedWindow: !SettingsData.frameEnabled || usesFrameBarChrome readonly property bool flattenForMaximizedWindow: !SettingsData.frameEnabled || usesFrameBarChrome
@@ -556,8 +554,8 @@ PanelWindow {
} }
screen: modelData screen: modelData
implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((renderBarConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0 implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
implicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((renderBarConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0 implicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
color: "transparent" color: "transparent"
Component.onCompleted: { Component.onCompleted: {
@@ -954,7 +952,7 @@ PanelWindow {
id: barBackground id: barBackground
barWindow: barWindow barWindow: barWindow
axis: axis axis: axis
barConfig: barWindow.renderBarConfig barConfig: barWindow.barConfig
} }
MouseArea { MouseArea {
@@ -152,9 +152,6 @@ Item {
} }
] ]
readonly property var entryActionKeys: ["pin", "edit", "delete"]
readonly property var entryActionLabels: [I18n.tr("Pin"), I18n.tr("Edit"), I18n.tr("Delete")]
function getMaxHistoryText(value) { function getMaxHistoryText(value) {
if (value <= 0) if (value <= 0)
return "∞"; return "∞";
@@ -190,29 +187,6 @@ Item {
return value.toString(); return value.toString();
} }
function visibleEntryActionKeys() {
return SettingsData.clipboardVisibleEntryActions || ["pin", "edit", "delete"];
}
function visibleEntryActionLabels() {
const visibleKeys = visibleEntryActionKeys();
return entryActionKeys.map((key, index) => visibleKeys.includes(key) ? entryActionLabels[index] : null).filter(label => label !== null);
}
function setVisibleEntryAction(index, selected) {
const actionKey = entryActionKeys[index];
if (!actionKey)
return;
let actions = visibleEntryActionKeys().slice();
if (selected && !actions.includes(actionKey)) {
actions.push(actionKey);
} else if (!selected && actions.includes(actionKey)) {
actions = actions.filter(action => action !== actionKey);
}
SettingsData.set("clipboardVisibleEntryActions", actions);
}
function loadConfig() { function loadConfig() {
configLoaded = false; configLoaded = false;
configError = false; configError = false;
@@ -463,24 +437,6 @@ Item {
checked: SettingsData.clipboardEnterToPaste checked: SettingsData.clipboardEnterToPaste
onToggled: checked => SettingsData.set("clipboardEnterToPaste", checked) onToggled: checked => SettingsData.set("clipboardEnterToPaste", checked)
} }
SettingsButtonGroupRow {
tab: "clipboard"
tags: ["clipboard", "actions", "buttons", "hide", "density", "pin", "edit", "delete"]
settingKey: "clipboardVisibleEntryActions"
text: I18n.tr("Visible Entry Actions")
description: I18n.tr("Choose which action buttons appear on clipboard entries")
selectionMode: "multi"
model: root.entryActionLabels
currentSelection: root.visibleEntryActionLabels()
checkEnabled: false
buttonHeight: 28
minButtonWidth: 56
buttonPadding: Theme.spacingS
textSize: Theme.fontSizeSmall
spacing: 1
onSelectionChanged: (index, selected) => root.setVisibleEntryAction(index, selected)
}
} }
SettingsCard { SettingsCard {
@@ -64,8 +64,6 @@ Item {
property alias model: buttonGroup.model property alias model: buttonGroup.model
property alias currentIndex: buttonGroup.currentIndex property alias currentIndex: buttonGroup.currentIndex
property alias initialSelection: buttonGroup.initialSelection
property alias currentSelection: buttonGroup.currentSelection
property alias selectionMode: buttonGroup.selectionMode property alias selectionMode: buttonGroup.selectionMode
property alias buttonHeight: buttonGroup.buttonHeight property alias buttonHeight: buttonGroup.buttonHeight
property alias minButtonWidth: buttonGroup.minButtonWidth property alias minButtonWidth: buttonGroup.minButtonWidth