diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 18f4f14f..880e3a67 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -266,6 +266,12 @@ Singleton { property int barMaxVisibleApps: 0 property int barMaxVisibleRunningApps: 0 property bool barShowOverflowBadge: true + property bool appsDockHideIndicators: false + property bool appsDockColorizeActive: false + property string appsDockActiveColorMode: "primary" + property bool appsDockEnlargeOnHover: false + property int appsDockEnlargePercentage: 125 + property int appsDockIconSizePercentage: 100 property bool keyboardLayoutNameCompactMode: false property bool runningAppsCurrentWorkspace: false property bool runningAppsGroupByApp: false diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index 072c7088..80e82abf 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -123,6 +123,12 @@ var SPEC = { barMaxVisibleApps: { def: 0 }, barMaxVisibleRunningApps: { def: 0 }, barShowOverflowBadge: { def: true }, + appsDockHideIndicators: { def: false }, + appsDockColorizeActive: { def: false }, + appsDockActiveColorMode: { def: "primary" }, + appsDockEnlargeOnHover: { def: false }, + appsDockEnlargePercentage: { def: 125 }, + appsDockIconSizePercentage: { def: 100 }, keyboardLayoutNameCompactMode: { def: false }, runningAppsCurrentWorkspace: { def: false }, runningAppsGroupByApp: { def: false }, diff --git a/quickshell/Modules/DankBar/Widgets/AppsDock.qml b/quickshell/Modules/DankBar/Widgets/AppsDock.qml index 09283a71..1589527d 100644 --- a/quickshell/Modules/DankBar/Widgets/AppsDock.qml +++ b/quickshell/Modules/DankBar/Widgets/AppsDock.qml @@ -688,6 +688,26 @@ Item { return appName + (windowTitle ? " • " + windowTitle : ""); } + readonly property bool enlargeEnabled: (widgetData?.appsDockEnlargeOnHover !== undefined ? widgetData.appsDockEnlargeOnHover : SettingsData.appsDockEnlargeOnHover) + readonly property real enlargeScale: enlargeEnabled && mouseArea.containsMouse ? (widgetData?.appsDockEnlargePercentage !== undefined ? widgetData.appsDockEnlargePercentage : SettingsData.appsDockEnlargePercentage) / 100.0 : 1.0 + readonly property real baseIconSizeMultiplier: (widgetData?.appsDockIconSizePercentage !== undefined ? widgetData.appsDockIconSizePercentage : SettingsData.appsDockIconSizePercentage) / 100.0 + readonly property real effectiveIconSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) * baseIconSizeMultiplier + + readonly property color activeOverlayColor: { + switch (SettingsData.appsDockActiveColorMode) { + case "secondary": + return Theme.secondary; + case "primaryContainer": + return Theme.primaryContainer; + case "error": + return Theme.error; + case "success": + return Theme.success; + default: + return Theme.primary; + } + } + transform: Translate { x: (dragHandler.dragging && !root.isVertical) ? dragHandler.dragAxisOffset : 0 y: (dragHandler.dragging && root.isVertical) ? dragHandler.dragAxisOffset : 0 @@ -700,8 +720,10 @@ Item { anchors.centerIn: parent radius: Theme.cornerRadius color: { - if (appItem.isFocused) { - return mouseArea.containsMouse ? Theme.primarySelected : Theme.withAlpha(Theme.primary, 0.2); + const colorizeEnabled = (widgetData?.appsDockColorizeActive !== undefined ? widgetData.appsDockColorizeActive : SettingsData.appsDockColorizeActive); + + if (appItem.isFocused && colorizeEnabled) { + return mouseArea.containsMouse ? Theme.withAlpha(Qt.lighter(appItem.activeOverlayColor, 1.3), 0.4) : Theme.withAlpha(appItem.activeOverlayColor, 0.3); } return mouseArea.containsMouse ? Theme.widgetBaseHoverColor : "transparent"; } @@ -719,7 +741,7 @@ Item { anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0 anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined - iconSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + iconSize: appItem.effectiveIconSize materialIconSizeAdjustment: 0 iconValue: { if (!modelData || !modelData.isCoreApp || !modelData.coreAppData) @@ -734,6 +756,15 @@ Item { fallbackText: "?" visible: iconValue !== "" z: 2 + + transformOrigin: Item.Center + scale: appItem.enlargeScale + Behavior on scale { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } } IconImage { @@ -745,8 +776,8 @@ Item { anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0 anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined - width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) - height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + width: appItem.effectiveIconSize + height: appItem.effectiveIconSize source: { root._desktopEntriesUpdateTrigger; root._appIdSubstitutionsTrigger; @@ -771,6 +802,15 @@ Item { colorizationColor: Theme.primary } z: 2 + + transformOrigin: Item.Center + scale: appItem.enlargeScale + Behavior on scale { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } } DankIcon { @@ -781,10 +821,19 @@ Item { anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0 anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined - size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + size: appItem.effectiveIconSize name: "sports_esports" color: Theme.widgetTextColor visible: !iconImg.visible && !coreIcon.visible && Paths.isSteamApp(appItem.appId) + + transformOrigin: Item.Center + scale: appItem.enlargeScale + Behavior on scale { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } } Text { @@ -801,6 +850,15 @@ Item { } font.pixelSize: 10 color: Theme.widgetTextColor + + transformOrigin: Item.Center + scale: appItem.enlargeScale + Behavior on scale { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } } Rectangle { @@ -812,7 +870,7 @@ Item { height: 14 radius: 7 color: Theme.primary - visible: modelData.type === "grouped" && appItem.windowCount > 1 + visible: modelData.type === "grouped" && appItem.windowCount > 1 && (widgetData?.barShowOverflowBadge !== undefined ? widgetData.barShowOverflowBadge : SettingsData.barShowOverflowBadge) z: 10 StyledText { @@ -838,7 +896,7 @@ Item { } Rectangle { - visible: modelData.isRunning + visible: modelData.isRunning && !(widgetData?.appsDockHideIndicators !== undefined ? widgetData.appsDockHideIndicators : SettingsData.appsDockHideIndicators) width: root.isVertical ? 2 : 20 height: root.isVertical ? 20 : 2 radius: 1 diff --git a/quickshell/Modules/Settings/WidgetsTabSection.qml b/quickshell/Modules/Settings/WidgetsTabSection.qml index 016bca1b..1de664b9 100644 --- a/quickshell/Modules/Settings/WidgetsTabSection.qml +++ b/quickshell/Modules/Settings/WidgetsTabSection.qml @@ -510,45 +510,35 @@ Column { } DankActionButton { - id: overflowMenuButton - buttonSize: 28 + id: appsDockMenuButton + buttonSize: 32 visible: modelData.id === "appsDock" - iconName: "unfold_more" - iconSize: 16 - iconColor: { - const maxApps = modelData.barMaxVisibleApps !== undefined ? modelData.barMaxVisibleApps : SettingsData.barMaxVisibleApps; - const maxRunning = modelData.barMaxVisibleRunningApps !== undefined ? modelData.barMaxVisibleRunningApps : SettingsData.barMaxVisibleRunningApps; - return (maxApps > 0 || maxRunning > 0) ? Theme.primary : Theme.outline; - } + iconName: "more_vert" + iconSize: 18 + iconColor: Theme.outline onClicked: { - overflowContextMenu.widgetData = modelData; - overflowContextMenu.sectionId = root.sectionId; - overflowContextMenu.widgetIndex = index; + appsDockContextMenu.widgetData = modelData; + appsDockContextMenu.sectionId = root.sectionId; + appsDockContextMenu.widgetIndex = index; - var buttonPos = overflowMenuButton.mapToItem(root, 0, 0); - var popupWidth = overflowContextMenu.width; - var popupHeight = overflowContextMenu.height; + var buttonPos = appsDockMenuButton.mapToItem(root, 0, 0); + var popupWidth = appsDockContextMenu.width; + var popupHeight = appsDockContextMenu.height; var xPos = buttonPos.x - popupWidth - Theme.spacingS; if (xPos < 0) - xPos = buttonPos.x + overflowMenuButton.width + Theme.spacingS; + xPos = buttonPos.x + appsDockMenuButton.width + Theme.spacingS; - var yPos = buttonPos.y - popupHeight / 2 + overflowMenuButton.height / 2; + var yPos = buttonPos.y - popupHeight / 2 + appsDockMenuButton.height / 2; if (yPos < 0) { yPos = Theme.spacingS; } else if (yPos + popupHeight > root.height) { yPos = root.height - popupHeight - Theme.spacingS; } - overflowContextMenu.x = xPos; - overflowContextMenu.y = yPos; - overflowContextMenu.open(); - } - onEntered: { - sharedTooltip.show(I18n.tr("Overflow"), overflowMenuButton, 0, 0, "bottom"); - } - onExited: { - sharedTooltip.hide(); + appsDockContextMenu.x = xPos; + appsDockContextMenu.y = yPos; + appsDockContextMenu.open(); } } @@ -1430,7 +1420,7 @@ Column { } Popup { - id: overflowContextMenu + id: appsDockContextMenu property var widgetData: null property string sectionId: "" @@ -1439,8 +1429,8 @@ Column { // Dynamically get current widget data from the items list readonly property var currentWidgetData: (widgetIndex >= 0 && widgetIndex < root.items.length) ? root.items[widgetIndex] : widgetData - width: 280 - height: overflowMenuColumn.implicitHeight + Theme.spacingS * 2 + width: 320 + height: appsDockMenuColumn.implicitHeight + Theme.spacingS * 2 padding: 0 modal: true focus: true @@ -1455,11 +1445,19 @@ Column { contentItem: Item { Column { - id: overflowMenuColumn + id: appsDockMenuColumn anchors.fill: parent anchors.margins: Theme.spacingS spacing: Theme.spacingS + StyledText { + text: I18n.tr("Apps Dock Settings") + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + leftPadding: Theme.spacingS + } + StyledText { text: I18n.tr("Overflow") font.pixelSize: Theme.fontSizeSmall @@ -1494,15 +1492,15 @@ Column { iconSize: 14 iconColor: Theme.outline onClicked: { - var current = overflowContextMenu.currentWidgetData?.barMaxVisibleApps ?? SettingsData.barMaxVisibleApps; + var current = appsDockContextMenu.currentWidgetData?.barMaxVisibleApps ?? SettingsData.barMaxVisibleApps; var newVal = Math.max(0, current - 1); - root.overflowSettingChanged(overflowContextMenu.sectionId, overflowContextMenu.widgetIndex, "barMaxVisibleApps", newVal); + root.overflowSettingChanged(appsDockContextMenu.sectionId, appsDockContextMenu.widgetIndex, "barMaxVisibleApps", newVal); } } StyledText { text: { - var val = overflowContextMenu.currentWidgetData?.barMaxVisibleApps ?? SettingsData.barMaxVisibleApps; + var val = appsDockContextMenu.currentWidgetData?.barMaxVisibleApps ?? SettingsData.barMaxVisibleApps; return val === 0 ? I18n.tr("All") : val; } font.pixelSize: Theme.fontSizeSmall @@ -1518,9 +1516,9 @@ Column { iconSize: 14 iconColor: Theme.outline onClicked: { - var current = overflowContextMenu.currentWidgetData?.barMaxVisibleApps ?? SettingsData.barMaxVisibleApps; + var current = appsDockContextMenu.currentWidgetData?.barMaxVisibleApps ?? SettingsData.barMaxVisibleApps; var newVal = current + 1; - root.overflowSettingChanged(overflowContextMenu.sectionId, overflowContextMenu.widgetIndex, "barMaxVisibleApps", newVal); + root.overflowSettingChanged(appsDockContextMenu.sectionId, appsDockContextMenu.widgetIndex, "barMaxVisibleApps", newVal); } } } @@ -1548,15 +1546,15 @@ Column { iconSize: 14 iconColor: Theme.outline onClicked: { - var current = overflowContextMenu.currentWidgetData?.barMaxVisibleRunningApps ?? SettingsData.barMaxVisibleRunningApps; + var current = appsDockContextMenu.currentWidgetData?.barMaxVisibleRunningApps ?? SettingsData.barMaxVisibleRunningApps; var newVal = Math.max(0, current - 1); - root.overflowSettingChanged(overflowContextMenu.sectionId, overflowContextMenu.widgetIndex, "barMaxVisibleRunningApps", newVal); + root.overflowSettingChanged(appsDockContextMenu.sectionId, appsDockContextMenu.widgetIndex, "barMaxVisibleRunningApps", newVal); } } StyledText { text: { - var val = overflowContextMenu.currentWidgetData?.barMaxVisibleRunningApps ?? SettingsData.barMaxVisibleRunningApps; + var val = appsDockContextMenu.currentWidgetData?.barMaxVisibleRunningApps ?? SettingsData.barMaxVisibleRunningApps; return val === 0 ? I18n.tr("All") : val; } font.pixelSize: Theme.fontSizeSmall @@ -1572,9 +1570,9 @@ Column { iconSize: 14 iconColor: Theme.outline onClicked: { - var current = overflowContextMenu.currentWidgetData?.barMaxVisibleRunningApps ?? SettingsData.barMaxVisibleRunningApps; + var current = appsDockContextMenu.currentWidgetData?.barMaxVisibleRunningApps ?? SettingsData.barMaxVisibleRunningApps; var newVal = current + 1; - root.overflowSettingChanged(overflowContextMenu.sectionId, overflowContextMenu.widgetIndex, "barMaxVisibleRunningApps", newVal); + root.overflowSettingChanged(appsDockContextMenu.sectionId, appsDockContextMenu.widgetIndex, "barMaxVisibleRunningApps", newVal); } } } @@ -1622,9 +1620,9 @@ Column { anchors.verticalCenter: parent.verticalCenter width: 40 height: 20 - checked: overflowContextMenu.currentWidgetData?.barShowOverflowBadge ?? SettingsData.barShowOverflowBadge + checked: appsDockContextMenu.currentWidgetData?.barShowOverflowBadge ?? SettingsData.barShowOverflowBadge onToggled: { - root.overflowSettingChanged(overflowContextMenu.sectionId, overflowContextMenu.widgetIndex, "barShowOverflowBadge", toggled); + root.overflowSettingChanged(appsDockContextMenu.sectionId, appsDockContextMenu.widgetIndex, "barShowOverflowBadge", toggled); } } @@ -1635,7 +1633,331 @@ Column { cursorShape: Qt.PointingHandCursor onPressed: { badgeToggle.checked = !badgeToggle.checked; - root.overflowSettingChanged(overflowContextMenu.sectionId, overflowContextMenu.widgetIndex, "barShowOverflowBadge", badgeToggle.checked); + root.overflowSettingChanged(appsDockContextMenu.sectionId, appsDockContextMenu.widgetIndex, "barShowOverflowBadge", badgeToggle.checked); + } + } + } + + Rectangle { + width: parent.width + height: 1 + color: Theme.outline + opacity: 0.15 + } + + StyledText { + text: I18n.tr("Visual Effects") + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + leftPadding: Theme.spacingS + topPadding: Theme.spacingXS + } + + Rectangle { + width: parent.width + height: 32 + radius: Theme.cornerRadius + color: hideIndicatorsArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + + Row { + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "visibility_off" + size: 16 + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: I18n.tr("Hide Indicators") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Normal + anchors.verticalCenter: parent.verticalCenter + } + } + + DankToggle { + id: hideIndicatorsToggle + anchors.right: parent.right + anchors.rightMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + width: 40 + height: 20 + checked: SettingsData.appsDockHideIndicators + onToggled: { + SettingsData.set("appsDockHideIndicators", toggled); + } + } + + MouseArea { + id: hideIndicatorsArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onPressed: { + hideIndicatorsToggle.checked = !hideIndicatorsToggle.checked; + SettingsData.set("appsDockHideIndicators", hideIndicatorsToggle.checked); + } + } + } + + Rectangle { + width: parent.width + height: 32 + radius: Theme.cornerRadius + color: colorizeActiveArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + + Row { + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "palette" + size: 16 + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: I18n.tr("Colorize Active") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Normal + anchors.verticalCenter: parent.verticalCenter + } + } + + DankToggle { + id: colorizeActiveToggle + anchors.right: parent.right + anchors.rightMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + width: 40 + height: 20 + checked: SettingsData.appsDockColorizeActive + onToggled: { + SettingsData.set("appsDockColorizeActive", toggled); + } + } + + MouseArea { + id: colorizeActiveArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onPressed: { + colorizeActiveToggle.checked = !colorizeActiveToggle.checked; + SettingsData.set("appsDockColorizeActive", colorizeActiveToggle.checked); + } + } + } + + Row { + width: parent.width + spacing: Theme.spacingS + visible: SettingsData.appsDockColorizeActive + leftPadding: Theme.spacingL + Theme.spacingS + + StyledText { + text: I18n.tr("Active Color") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + width: 90 + } + + DankButtonGroup { + anchors.verticalCenter: parent.verticalCenter + model: ["pri", "sec", "pc", "err", "ok"] + buttonHeight: 22 + minButtonWidth: 32 + buttonPadding: 4 + checkIconSize: 10 + textSize: 9 + spacing: 1 + currentIndex: { + switch (SettingsData.appsDockActiveColorMode) { + case "secondary": + return 1; + case "primaryContainer": + return 2; + case "error": + return 3; + case "success": + return 4; + default: + return 0; + } + } + onSelectionChanged: (index, selected) => { + if (!selected) + return; + const modes = ["primary", "secondary", "primaryContainer", "error", "success"]; + SettingsData.set("appsDockActiveColorMode", modes[index]); + } + } + } + + Rectangle { + width: parent.width + height: 32 + radius: Theme.cornerRadius + color: enlargeOnHoverArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + + Row { + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "zoom_in" + size: 16 + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: I18n.tr("Enlarge on Hover") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Normal + anchors.verticalCenter: parent.verticalCenter + } + } + + DankToggle { + id: enlargeOnHoverToggle + anchors.right: parent.right + anchors.rightMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + width: 40 + height: 20 + checked: SettingsData.appsDockEnlargeOnHover + onToggled: { + SettingsData.set("appsDockEnlargeOnHover", toggled); + } + } + + MouseArea { + id: enlargeOnHoverArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onPressed: { + enlargeOnHoverToggle.checked = !enlargeOnHoverToggle.checked; + SettingsData.set("appsDockEnlargeOnHover", enlargeOnHoverToggle.checked); + } + } + } + + Row { + width: parent.width + spacing: Theme.spacingS + visible: SettingsData.appsDockEnlargeOnHover + + StyledText { + text: I18n.tr("Enlargement %") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + width: 120 + } + + Row { + spacing: Theme.spacingXS + anchors.verticalCenter: parent.verticalCenter + + DankActionButton { + buttonSize: 24 + iconName: "remove" + iconSize: 14 + iconColor: Theme.outline + onClicked: { + var current = SettingsData.appsDockEnlargePercentage; + var newVal = Math.max(100, current - 5); + SettingsData.set("appsDockEnlargePercentage", newVal); + } + } + + StyledText { + text: SettingsData.appsDockEnlargePercentage + "%" + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignHCenter + width: 50 + } + + DankActionButton { + buttonSize: 24 + iconName: "add" + iconSize: 14 + iconColor: Theme.outline + onClicked: { + var current = SettingsData.appsDockEnlargePercentage; + var newVal = Math.min(150, current + 5); + SettingsData.set("appsDockEnlargePercentage", newVal); + } + } + } + } + + Row { + width: parent.width + spacing: Theme.spacingS + + StyledText { + text: I18n.tr("Icon Size %") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + width: 120 + } + + Row { + spacing: Theme.spacingXS + anchors.verticalCenter: parent.verticalCenter + + DankActionButton { + buttonSize: 24 + iconName: "remove" + iconSize: 14 + iconColor: Theme.outline + onClicked: { + var current = SettingsData.appsDockIconSizePercentage; + var newVal = Math.max(50, current - 5); + SettingsData.set("appsDockIconSizePercentage", newVal); + } + } + + StyledText { + text: SettingsData.appsDockIconSizePercentage + "%" + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignHCenter + width: 50 + } + + DankActionButton { + buttonSize: 24 + iconName: "add" + iconSize: 14 + iconColor: Theme.outline + onClicked: { + var current = SettingsData.appsDockIconSizePercentage; + var newVal = Math.min(200, current + 5); + SettingsData.set("appsDockIconSizePercentage", newVal); + } } } }