1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-10 15:52:58 -04:00

animations/ripple: clean up effect and apply more universally

This commit is contained in:
bbedward
2026-02-10 12:48:12 -05:00
parent 5a0bb260b4
commit 3d0ee9d72b
49 changed files with 980 additions and 805 deletions

View File

@@ -1,29 +1,23 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Modules.Plugins
import qs.Services
import qs.Widgets
Item {
BasePill {
id: root
enableBackgroundHover: false
enableCursor: false
section: "left"
property var widgetData: null
property var barConfig: null
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "left"
property var parentScreen
property var hoveredItem: null
property var topBar: null
property real widgetThickness: 30
property real barThickness: 48
property real barSpacing: 4
property bool isAutoHideBar: false
readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS
property Item windowRoot: (Window.window ? Window.window.contentItem : null)
property int draggedIndex: -1
@@ -66,7 +60,7 @@ Item {
readonly property real barY: barBounds.y
readonly property real minTooltipY: {
if (!parentScreen || !isVertical) {
if (!parentScreen || !isVerticalOrientation) {
return 0;
}
@@ -322,7 +316,9 @@ Item {
}
function applyOverflow(baseResult) {
const { items } = baseResult;
const {
items
} = baseResult;
const maxPinned = root.maxVisibleApps;
const maxRunning = root.maxVisibleRunningApps;
@@ -403,110 +399,19 @@ Item {
Component.onCompleted: updateModel()
readonly property int calculatedSize: {
const count = dockItems.length;
if (count === 0)
return 0;
if (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) {
return count * 24 + (count - 1) * Theme.spacingXS + horizontalPadding * 2;
} else {
return count * (24 + Theme.spacingXS + 120) + (count - 1) * Theme.spacingXS + horizontalPadding * 2;
}
}
readonly property real realCalculatedSize: {
let total = horizontalPadding * 2;
const compact = (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode);
for (let i = 0; i < dockItems.length; i++) {
const item = dockItems[i];
const isInOverflow = item.isInOverflow === true;
if (isInOverflow && !root.overflowExpanded)
continue;
let itemSize = 0;
if (item.type === "separator") {
itemSize = 8;
} else {
itemSize = compact ? 24 : (24 + Theme.spacingXS + 120);
}
total += itemSize;
if (i < dockItems.length - 1)
total += Theme.spacingXS;
}
return total;
}
width: dockItems.length > 0 ? (isVertical ? barThickness : realCalculatedSize) : 0
height: dockItems.length > 0 ? (isVertical ? realCalculatedSize : barThickness) : 0
visible: dockItems.length > 0
Item {
id: visualBackground
width: root.isVertical ? root.widgetThickness : root.realCalculatedSize
height: root.isVertical ? root.realCalculatedSize : root.widgetThickness
anchors.centerIn: parent
clip: false
content: Component {
Item {
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
Rectangle {
id: outline
anchors.centerIn: parent
width: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.width + borderWidth * 2;
}
height: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.height + borderWidth * 2;
}
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: "transparent"
border.width: (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0
border.color: {
if (!(barConfig?.widgetOutlineEnabled ?? false)) {
return "transparent";
}
const colorOption = barConfig?.widgetOutlineColor || "primary";
const opacity = barConfig?.widgetOutlineOpacity ?? 1.0;
switch (colorOption) {
case "surfaceText":
return Theme.withAlpha(Theme.surfaceText, opacity);
case "secondary":
return Theme.withAlpha(Theme.secondary, opacity);
case "primary":
return Theme.withAlpha(Theme.primary, opacity);
default:
return Theme.withAlpha(Theme.primary, opacity);
}
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVerticalOrientation ? columnLayout : rowLayout
}
}
Rectangle {
id: background
anchors.fill: parent
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: {
if (dockItems.length === 0)
return "transparent";
if ((barConfig?.noBackground ?? false))
return "transparent";
const baseColor = Theme.widgetBaseBackgroundColor;
const transparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0;
if (Theme.widgetBackgroundHasAlpha) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * transparency);
}
return Theme.withAlpha(baseColor, transparency);
}
}
}
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVertical ? columnLayout : rowLayout
}
Component {
@@ -556,8 +461,8 @@ Item {
readonly property bool isInOverflow: modelData.isInOverflow === true
readonly property real visualSize: isSeparator ? 8 : ((widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? 24 : (24 + Theme.spacingXS + 120))
readonly property real visualWidth: root.isVertical ? root.barThickness : visualSize
readonly property real visualHeight: root.isVertical ? visualSize : root.barThickness
readonly property real visualWidth: root.isVerticalOrientation ? root.barThickness : visualSize
readonly property real visualHeight: root.isVerticalOrientation ? visualSize : root.barThickness
visible: !isInOverflow || root.overflowExpanded
opacity: (isInOverflow && !root.overflowExpanded) ? 0 : 1
@@ -604,8 +509,8 @@ Item {
}
transform: Translate {
x: root.isVertical ? 0 : delegateItem.shiftOffset
y: root.isVertical ? delegateItem.shiftOffset : 0
x: root.isVerticalOrientation ? 0 : delegateItem.shiftOffset
y: root.isVerticalOrientation ? delegateItem.shiftOffset : 0
Behavior on x {
enabled: !root.suppressShiftAnimation
@@ -625,8 +530,8 @@ Item {
Rectangle {
visible: isSeparator
width: root.isVertical ? root.barThickness * 0.6 : 2
height: root.isVertical ? 2 : root.barThickness * 0.6
width: root.isVerticalOrientation ? root.barThickness * 0.6 : 2
height: root.isVerticalOrientation ? 2 : root.barThickness * 0.6
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
radius: 1
anchors.centerIn: parent
@@ -640,7 +545,7 @@ Item {
iconSize: 24
overflowCount: modelData.overflowCount || 0
overflowExpanded: root.overflowExpanded
isVertical: root.isVertical
isVertical: root.isVerticalOrientation
showBadge: root.showOverflowBadge
z: 10
onClicked: {
@@ -709,14 +614,14 @@ Item {
}
transform: Translate {
x: (dragHandler.dragging && !root.isVertical) ? dragHandler.dragAxisOffset : 0
y: (dragHandler.dragging && root.isVertical) ? dragHandler.dragAxisOffset : 0
x: (dragHandler.dragging && !root.isVerticalOrientation) ? dragHandler.dragAxisOffset : 0
y: (dragHandler.dragging && root.isVerticalOrientation) ? dragHandler.dragAxisOffset : 0
}
Rectangle {
id: visualContent
width: root.isVertical ? 24 : delegateItem.visualSize
height: root.isVertical ? delegateItem.visualSize : 24
width: root.isVerticalOrientation ? 24 : delegateItem.visualSize
height: root.isVerticalOrientation ? delegateItem.visualSize : 24
anchors.centerIn: parent
radius: Theme.cornerRadius
color: {
@@ -735,11 +640,11 @@ Item {
AppIconRenderer {
id: coreIcon
readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
anchors.left: (root.isVertical || isCompact) ? undefined : parent.left
anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS
anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined
anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0
anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined
anchors.left: (root.isVerticalOrientation || isCompact) ? undefined : parent.left
anchors.leftMargin: (root.isVerticalOrientation || isCompact) ? 0 : Theme.spacingXS
anchors.top: (root.isVerticalOrientation && !isCompact) ? parent.top : undefined
anchors.topMargin: (root.isVerticalOrientation && !isCompact) ? Theme.spacingXS : 0
anchors.centerIn: (root.isVerticalOrientation || isCompact) ? parent : undefined
iconSize: appItem.effectiveIconSize
materialIconSizeAdjustment: 0
@@ -770,11 +675,11 @@ Item {
IconImage {
id: iconImg
readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
anchors.left: (root.isVertical || isCompact) ? undefined : parent.left
anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS
anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined
anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0
anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined
anchors.left: (root.isVerticalOrientation || isCompact) ? undefined : parent.left
anchors.leftMargin: (root.isVerticalOrientation || isCompact) ? 0 : Theme.spacingXS
anchors.top: (root.isVerticalOrientation && !isCompact) ? parent.top : undefined
anchors.topMargin: (root.isVerticalOrientation && !isCompact) ? Theme.spacingXS : 0
anchors.centerIn: (root.isVerticalOrientation || isCompact) ? parent : undefined
width: appItem.effectiveIconSize
height: appItem.effectiveIconSize
@@ -815,11 +720,11 @@ Item {
DankIcon {
readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
anchors.left: (root.isVertical || isCompact) ? undefined : parent.left
anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS
anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined
anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0
anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined
anchors.left: (root.isVerticalOrientation || isCompact) ? undefined : parent.left
anchors.leftMargin: (root.isVerticalOrientation || isCompact) ? 0 : Theme.spacingXS
anchors.top: (root.isVerticalOrientation && !isCompact) ? parent.top : undefined
anchors.topMargin: (root.isVerticalOrientation && !isCompact) ? Theme.spacingXS : 0
anchors.centerIn: (root.isVerticalOrientation || isCompact) ? parent : undefined
size: appItem.effectiveIconSize
name: "sports_esports"
@@ -882,7 +787,7 @@ Item {
}
StyledText {
visible: !root.isVertical && !(widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
visible: !root.isVerticalOrientation && !(widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
anchors.left: iconImg.right
anchors.leftMargin: Theme.spacingXS
anchors.right: parent.right
@@ -897,16 +802,16 @@ Item {
Rectangle {
visible: modelData.isRunning && !(widgetData?.appsDockHideIndicators !== undefined ? widgetData.appsDockHideIndicators : SettingsData.appsDockHideIndicators)
width: root.isVertical ? 2 : 20
height: root.isVertical ? 20 : 2
width: root.isVerticalOrientation ? 2 : 20
height: root.isVerticalOrientation ? 20 : 2
radius: 1
color: appItem.isFocused ? Theme.primary : Theme.surfaceText
opacity: appItem.isFocused ? 1 : 0.5
anchors.bottom: root.isVertical ? undefined : parent.bottom
anchors.right: root.isVertical ? parent.right : undefined
anchors.horizontalCenter: root.isVertical ? undefined : parent.horizontalCenter
anchors.verticalCenter: root.isVertical ? parent.verticalCenter : undefined
anchors.bottom: root.isVerticalOrientation ? undefined : parent.bottom
anchors.right: root.isVerticalOrientation ? parent.right : undefined
anchors.horizontalCenter: root.isVerticalOrientation ? undefined : parent.horizontalCenter
anchors.verticalCenter: root.isVerticalOrientation ? parent.verticalCenter : undefined
anchors.margins: 0
z: 5
@@ -1009,10 +914,10 @@ Item {
if (!dragHandler.dragging)
return;
const axisOffset = root.isVertical ? (mouse.y - dragHandler.dragStartPos.y) : (mouse.x - dragHandler.dragStartPos.x);
const axisOffset = root.isVerticalOrientation ? (mouse.y - dragHandler.dragStartPos.y) : (mouse.x - dragHandler.dragStartPos.x);
dragHandler.dragAxisOffset = axisOffset;
const itemSize = (root.isVertical ? delegateItem.height : delegateItem.width) + Theme.spacingXS;
const itemSize = (root.isVerticalOrientation ? delegateItem.height : delegateItem.width) + Theme.spacingXS;
const slotOffset = Math.round(axisOffset / itemSize);
const newTargetIndex = Math.max(0, Math.min(root.pinnedAppCount - 1, index + slotOffset));
@@ -1028,7 +933,7 @@ Item {
tooltipLoader.active = true;
if (tooltipLoader.item) {
if (root.isVertical) {
if (root.isVerticalOrientation) {
const globalPos = delegateItem.mapToGlobal(0, delegateItem.height / 2);
const screenX = root.parentScreen ? root.parentScreen.x : 0;
const screenY = root.parentScreen ? root.parentScreen.y : 0;

View File

@@ -1,7 +1,6 @@
import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Widgets
import qs.Common
import qs.Services
@@ -213,12 +212,19 @@ PanelWindow {
}
}
DankRipple {
id: windowRipple
rippleColor: Theme.surfaceText
cornerRadius: Theme.cornerRadius
}
MouseArea {
id: windowArea
anchors.fill: parent
anchors.rightMargin: 24
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => windowRipple.trigger(mouse.x, mouse.y)
onClicked: {
if (modelData && modelData.activate) {
modelData.activate();
@@ -285,11 +291,18 @@ PanelWindow {
}
}
DankRipple {
id: actionRipple
rippleColor: Theme.surfaceText
cornerRadius: Theme.cornerRadius
}
MouseArea {
id: actionArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => actionRipple.trigger(mouse.x, mouse.y)
onClicked: {
if (modelData) {
SessionService.launchDesktopAction(root.desktopEntry, modelData);
@@ -333,11 +346,18 @@ PanelWindow {
wrapMode: Text.NoWrap
}
DankRipple {
id: pinRipple
rippleColor: Theme.surfaceText
cornerRadius: Theme.cornerRadius
}
MouseArea {
id: pinArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y)
onClicked: {
if (!root.appData) {
return;
@@ -386,11 +406,18 @@ PanelWindow {
wrapMode: Text.NoWrap
}
DankRipple {
id: nvidiaRipple
rippleColor: Theme.surfaceText
cornerRadius: Theme.cornerRadius
}
MouseArea {
id: nvidiaArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => nvidiaRipple.trigger(mouse.x, mouse.y)
onClicked: {
if (root.desktopEntry) {
SessionService.launchDesktopEntry(root.desktopEntry, true);
@@ -426,11 +453,18 @@ PanelWindow {
wrapMode: Text.NoWrap
}
DankRipple {
id: closeRipple
rippleColor: Theme.error
cornerRadius: Theme.cornerRadius
}
MouseArea {
id: closeArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => closeRipple.trigger(mouse.x, mouse.y)
onClicked: {
if (root.appData?.type === "window") {
root.appData?.toplevel?.close();

View File

@@ -115,7 +115,8 @@ BasePill {
height: battery.height + battery.topMargin + battery.bottomMargin
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
onPressed: mouse => {
battery.triggerRipple(this, mouse.x, mouse.y);
toggleBatteryPopup();
}
}

View File

@@ -65,6 +65,7 @@ BasePill {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onPressed: mouse => root.triggerRipple(this, mouse.x, mouse.y)
onClicked: function (mouse) {
switch (mouse.button) {
case Qt.RightButton:

View File

@@ -11,6 +11,8 @@ BasePill {
property bool compactMode: false
signal clockClicked
onClicked: clockClicked()
content: Component {
Item {
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : clockRow.implicitWidth
@@ -326,15 +328,4 @@ BasePill {
}
}
}
MouseArea {
x: -root.leftMargin
y: -root.topMargin
width: root.width + root.leftMargin + root.rightMargin
height: root.height + root.topMargin + root.bottomMargin
cursorShape: Qt.PointingHandCursor
onPressed: {
root.clockClicked();
}
}
}

View File

@@ -29,7 +29,8 @@ BasePill {
z: 1
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onPressed: {
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
root.colorPickerRequested();
}
}

View File

@@ -138,7 +138,8 @@ BasePill {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
DgopService.setSortBy("cpu");
cpuClicked();
}

View File

@@ -138,7 +138,8 @@ BasePill {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
DgopService.setSortBy("cpu");
cpuTempClicked();
}

View File

@@ -206,7 +206,8 @@ BasePill {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
DgopService.setSortBy("cpu");
gpuTempClicked();
}

View File

@@ -26,6 +26,9 @@ BasePill {
z: 1
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
}
onClicked: {
SessionService.toggleIdleInhibit();
}

View File

@@ -12,31 +12,101 @@ BasePill {
property var widgetData: null
property bool compactMode: widgetData?.keyboardLayoutNameCompactMode !== undefined ? widgetData.keyboardLayoutNameCompactMode : SettingsData.keyboardLayoutNameCompactMode
readonly property var langCodes: ({
"afrikaans": "af", "albanian": "sq", "amharic": "am", "arabic": "ar",
"armenian": "hy", "azerbaijani": "az", "basque": "eu", "belarusian": "be",
"bengali": "bn", "bosnian": "bs", "bulgarian": "bg", "burmese": "my",
"catalan": "ca", "chinese": "zh", "croatian": "hr", "czech": "cs",
"danish": "da", "dutch": "nl", "english": "en", "esperanto": "eo",
"estonian": "et", "filipino": "fil", "finnish": "fi", "french": "fr",
"galician": "gl", "georgian": "ka", "german": "de", "greek": "el",
"gujarati": "gu", "hausa": "ha", "hebrew": "he", "hindi": "hi",
"hungarian": "hu", "icelandic": "is", "igbo": "ig", "indonesian": "id",
"irish": "ga", "italian": "it", "japanese": "ja", "javanese": "jv",
"kannada": "kn", "kazakh": "kk", "khmer": "km", "korean": "ko",
"kurdish": "ku", "kyrgyz": "ky", "lao": "lo", "latvian": "lv",
"lithuanian": "lt", "luxembourgish": "lb", "macedonian": "mk", "malay": "ms",
"malayalam": "ml", "maltese": "mt", "maori": "mi", "marathi": "mr",
"mongolian": "mn", "nepali": "ne", "norwegian": "no", "pashto": "ps",
"persian": "fa", "iranian": "fa", "farsi": "fa", "polish": "pl",
"portuguese": "pt", "punjabi": "pa", "romanian": "ro", "russian": "ru",
"serbian": "sr", "sindhi": "sd", "sinhala": "si", "slovak": "sk",
"slovenian": "sl", "somali": "so", "spanish": "es", "swahili": "sw",
"swedish": "sv", "tajik": "tg", "tamil": "ta", "tatar": "tt",
"telugu": "te", "thai": "th", "tibetan": "bo", "turkish": "tr",
"turkmen": "tk", "ukrainian": "uk", "urdu": "ur", "uyghur": "ug",
"uzbek": "uz", "vietnamese": "vi", "welsh": "cy", "yiddish": "yi",
"yoruba": "yo", "zulu": "zu"
})
"afrikaans": "af",
"albanian": "sq",
"amharic": "am",
"arabic": "ar",
"armenian": "hy",
"azerbaijani": "az",
"basque": "eu",
"belarusian": "be",
"bengali": "bn",
"bosnian": "bs",
"bulgarian": "bg",
"burmese": "my",
"catalan": "ca",
"chinese": "zh",
"croatian": "hr",
"czech": "cs",
"danish": "da",
"dutch": "nl",
"english": "en",
"esperanto": "eo",
"estonian": "et",
"filipino": "fil",
"finnish": "fi",
"french": "fr",
"galician": "gl",
"georgian": "ka",
"german": "de",
"greek": "el",
"gujarati": "gu",
"hausa": "ha",
"hebrew": "he",
"hindi": "hi",
"hungarian": "hu",
"icelandic": "is",
"igbo": "ig",
"indonesian": "id",
"irish": "ga",
"italian": "it",
"japanese": "ja",
"javanese": "jv",
"kannada": "kn",
"kazakh": "kk",
"khmer": "km",
"korean": "ko",
"kurdish": "ku",
"kyrgyz": "ky",
"lao": "lo",
"latvian": "lv",
"lithuanian": "lt",
"luxembourgish": "lb",
"macedonian": "mk",
"malay": "ms",
"malayalam": "ml",
"maltese": "mt",
"maori": "mi",
"marathi": "mr",
"mongolian": "mn",
"nepali": "ne",
"norwegian": "no",
"pashto": "ps",
"persian": "fa",
"iranian": "fa",
"farsi": "fa",
"polish": "pl",
"portuguese": "pt",
"punjabi": "pa",
"romanian": "ro",
"russian": "ru",
"serbian": "sr",
"sindhi": "sd",
"sinhala": "si",
"slovak": "sk",
"slovenian": "sl",
"somali": "so",
"spanish": "es",
"swahili": "sw",
"swedish": "sv",
"tajik": "tg",
"tamil": "ta",
"tatar": "tt",
"telugu": "te",
"thai": "th",
"tibetan": "bo",
"turkish": "tr",
"turkmen": "tk",
"ukrainian": "uk",
"urdu": "ur",
"uyghur": "ug",
"uzbek": "uz",
"vietnamese": "vi",
"welsh": "cy",
"yiddish": "yi",
"yoruba": "yo",
"zulu": "zu"
})
readonly property var validVariants: ["US", "UK", "GB", "AZERTY", "QWERTY", "Dvorak", "Colemak", "Mac", "Intl", "International"]
property string currentLayout: {
if (CompositorService.isNiri) {
@@ -119,6 +189,9 @@ BasePill {
z: 1
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
}
onClicked: {
if (CompositorService.isNiri) {
NiriService.cycleKeyboardLayout();

View File

@@ -171,6 +171,9 @@ BasePill {
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
}
onClicked: {
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
const globalPos = parent.mapToItem(null, 0, 0);
@@ -326,7 +329,8 @@ BasePill {
anchors.fill: parent
enabled: root.playerAvailable
cursorShape: Qt.PointingHandCursor
onPressed: {
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
const globalPos = mapToItem(null, 0, 0);
const currentScreen = root.parentScreen || Screen;

View File

@@ -130,6 +130,9 @@ BasePill {
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
}
onClicked: function (mouse) {
if (mouse.button === Qt.RightButton) {
openContextMenu();

View File

@@ -1,247 +1,171 @@
import QtQuick
import qs.Common
import qs.Modules.Plugins
import qs.Services
import qs.Widgets
Item {
BasePill {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "right"
property var popupTarget: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
property var barConfig: null
section: "right"
property bool showMicIcon: SettingsData.privacyShowMicIcon
property bool showCameraIcon: SettingsData.privacyShowCameraIcon
property bool showScreenSharingIcon: SettingsData.privacyShowScreenShareIcon
readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS
readonly property bool hasActivePrivacy: showMicIcon || showCameraIcon || showScreenSharingIcon || PrivacyService.anyPrivacyActive
readonly property int activeCount: (showMicIcon ? 1 : PrivacyService.microphoneActive) + (showCameraIcon ? 1 : PrivacyService.cameraActive) + (showScreenSharingIcon ? 1 : PrivacyService.screensharingActive)
readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
readonly property real contentHeight: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
readonly property real visualWidth: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0)
readonly property real visualHeight: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : widgetThickness
width: isVertical ? barThickness : visualWidth
height: isVertical ? visualHeight : barThickness
visible: hasActivePrivacy
opacity: hasActivePrivacy ? 1 : 0
enabled: hasActivePrivacy
Item {
id: visualContent
width: root.visualWidth
height: root.visualHeight
anchors.centerIn: parent
content: Component {
Item {
implicitWidth: root.hasActivePrivacy ? root.contentWidth : 0
implicitHeight: root.hasActivePrivacy ? root.contentHeight : 0
Rectangle {
id: outline
anchors.centerIn: parent
width: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.width + borderWidth * 2;
}
height: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.height + borderWidth * 2;
}
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: "transparent"
border.width: {
if (barConfig?.widgetOutlineEnabled ?? false) {
return barConfig?.widgetOutlineThickness ?? 1;
}
return 0;
}
border.color: {
if (!(barConfig?.widgetOutlineEnabled ?? false)) {
return "transparent";
}
const colorOption = barConfig?.widgetOutlineColor || "primary";
const opacity = barConfig?.widgetOutlineOpacity ?? 1.0;
switch (colorOption) {
case "surfaceText":
return Theme.withAlpha(Theme.surfaceText, opacity);
case "secondary":
return Theme.withAlpha(Theme.secondary, opacity);
case "primary":
return Theme.withAlpha(Theme.primary, opacity);
default:
return Theme.withAlpha(Theme.primary, opacity);
}
}
}
Column {
anchors.centerIn: parent
spacing: Theme.spacingXS
visible: root.isVerticalOrientation && root.hasActivePrivacy
Rectangle {
id: background
anchors.fill: parent
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: {
if (barConfig?.noBackground ?? false) {
return "transparent";
}
Item {
width: 18
height: 18
visible: PrivacyService.microphoneActive
anchors.horizontalCenter: parent.horizontalCenter
const rawTransparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0;
const isHovered = privacyArea.containsMouse;
const transparency = isHovered ? Math.max(0.3, rawTransparency) : rawTransparency;
const baseColor = isHovered ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Theme.withAlpha(baseColor, transparency);
}
}
Column {
anchors.centerIn: parent
spacing: Theme.spacingXS
visible: root.isVertical && root.hasActivePrivacy
Item {
width: 18
height: 18
visible: PrivacyService.microphoneActive
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
name: {
const sourceAudio = AudioService.source?.audio;
const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0;
if (muted)
return "mic_off";
return "mic";
DankIcon {
name: {
const sourceAudio = AudioService.source?.audio;
const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0;
if (muted)
return "mic_off";
return "mic";
}
size: Theme.iconSizeSmall
color: Theme.error
filled: true
anchors.centerIn: parent
}
size: Theme.iconSizeSmall
color: Theme.error
filled: true
anchors.centerIn: parent
}
}
Item {
width: 18
height: 18
visible: PrivacyService.cameraActive
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
name: "camera_video"
size: Theme.iconSizeSmall
color: Theme.widgetTextColor
filled: true
anchors.centerIn: parent
}
Rectangle {
width: 6
height: 6
radius: 3
color: Theme.error
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -2
anchors.topMargin: -1
}
}
Item {
width: 18
height: 18
visible: PrivacyService.screensharingActive
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
name: "screen_share"
size: Theme.iconSizeSmall
color: Theme.warning
filled: true
anchors.centerIn: parent
}
}
}
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
visible: !root.isVertical && root.hasActivePrivacy
Item {
width: 18
height: 18
visible: showMicIcon || PrivacyService.microphoneActive
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: {
const sourceAudio = AudioService.source?.audio;
const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0;
if (muted)
return "mic_off";
return "mic";
}
size: Theme.iconSizeSmall
color: PrivacyService.microphoneActive ? Theme.error : Theme.surfaceText
filled: true
anchors.centerIn: parent
}
}
Item {
width: 18
height: 18
visible: showCameraIcon || PrivacyService.cameraActive
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: "camera_video"
size: Theme.iconSizeSmall
color: PrivacyService.cameraActive ? Theme.error : Theme.surfaceText
filled: true
anchors.centerIn: parent
}
Rectangle {
width: 6
height: 6
radius: 3
color: Theme.error
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -2
anchors.topMargin: -1
Item {
width: 18
height: 18
visible: PrivacyService.cameraActive
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
name: "camera_video"
size: Theme.iconSizeSmall
color: Theme.widgetTextColor
filled: true
anchors.centerIn: parent
}
Rectangle {
width: 6
height: 6
radius: 3
color: Theme.error
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -2
anchors.topMargin: -1
}
}
Item {
width: 18
height: 18
visible: PrivacyService.screensharingActive
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
name: "screen_share"
size: Theme.iconSizeSmall
color: Theme.warning
filled: true
anchors.centerIn: parent
}
}
}
Item {
width: 18
height: 18
visible: showScreenSharingIcon || PrivacyService.screensharingActive
anchors.verticalCenter: parent.verticalCenter
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
visible: !root.isVerticalOrientation && root.hasActivePrivacy
DankIcon {
name: "screen_share"
size: Theme.iconSizeSmall
color: PrivacyService.screensharingActive ? Theme.warning : Theme.surfaceText
filled: true
anchors.centerIn: parent
Item {
width: 18
height: 18
visible: root.showMicIcon || PrivacyService.microphoneActive
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: {
const sourceAudio = AudioService.source?.audio;
const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0;
if (muted)
return "mic_off";
return "mic";
}
size: Theme.iconSizeSmall
color: PrivacyService.microphoneActive ? Theme.error : Theme.surfaceText
filled: true
anchors.centerIn: parent
}
}
Item {
width: 18
height: 18
visible: root.showCameraIcon || PrivacyService.cameraActive
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: "camera_video"
size: Theme.iconSizeSmall
color: PrivacyService.cameraActive ? Theme.error : Theme.surfaceText
filled: true
anchors.centerIn: parent
}
Rectangle {
width: 6
height: 6
radius: 3
color: Theme.error
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -2
anchors.topMargin: -1
visible: PrivacyService.cameraActive
}
}
Item {
width: 18
height: 18
visible: root.showScreenSharingIcon || PrivacyService.screensharingActive
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: "screen_share"
size: Theme.iconSizeSmall
color: PrivacyService.screensharingActive ? Theme.warning : Theme.surfaceText
filled: true
anchors.centerIn: parent
}
}
}
}
}
MouseArea {
id: privacyArea
z: -1
anchors.fill: parent
hoverEnabled: hasActivePrivacy
enabled: hasActivePrivacy
cursorShape: Qt.PointingHandCursor
onClicked: {}
}
Rectangle {
id: tooltip
width: tooltipText.contentWidth + Theme.spacingM * 2
@@ -251,7 +175,7 @@ Item {
border.color: Theme.outlineMedium
border.width: 1
visible: false
opacity: privacyArea.containsMouse && hasActivePrivacy ? 1 : 0
opacity: root.isMouseHovered && hasActivePrivacy ? 1 : 0
z: 100
x: (parent.width - width) / 2
y: -height - Theme.spacingXS
@@ -287,7 +211,7 @@ Item {
}
Behavior on width {
enabled: hasActivePrivacy && visible && !isVertical
enabled: hasActivePrivacy && visible && !isVerticalOrientation
NumberAnimation {
duration: Theme.mediumDuration
@@ -296,7 +220,7 @@ Item {
}
Behavior on height {
enabled: hasActivePrivacy && visible && isVertical
enabled: hasActivePrivacy && visible && isVerticalOrientation
NumberAnimation {
duration: Theme.mediumDuration

View File

@@ -161,7 +161,8 @@ BasePill {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
DgopService.setSortBy("memory");
ramClicked();
}

View File

@@ -5,25 +5,21 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Modules.Plugins
import qs.Services
import qs.Widgets
Item {
BasePill {
id: root
enableBackgroundHover: false
enableCursor: false
section: "left"
property var widgetData: null
property var barConfig: null
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "left"
property var parentScreen
property var hoveredItem: null
property var topBar: null
property real widgetThickness: 30
property real barThickness: 48
property real barSpacing: 4
property bool isAutoHideBar: false
readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS
property Item windowRoot: (Window.window ? Window.window.contentItem : null)
readonly property real effectiveBarThickness: {
@@ -52,7 +48,7 @@ Item {
readonly property real barY: barBounds.y
readonly property real minTooltipY: {
if (!parentScreen || !isVertical) {
if (!parentScreen || !isVerticalOrientation) {
return 0;
}
@@ -139,108 +135,42 @@ Item {
}
}
readonly property int windowCount: _groupByApp ? (groupedWindows?.length || 0) : (sortedToplevels?.length || 0)
readonly property int calculatedSize: {
if (windowCount === 0) {
return 0;
}
if (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) {
return windowCount * 24 + (windowCount - 1) * Theme.spacingXS + horizontalPadding * 2;
} else {
return windowCount * (24 + Theme.spacingXS + 120) + (windowCount - 1) * Theme.spacingXS + horizontalPadding * 2;
}
}
width: windowCount > 0 ? (isVertical ? barThickness : calculatedSize) : 0
height: windowCount > 0 ? (isVertical ? calculatedSize : barThickness) : 0
visible: windowCount > 0
Item {
id: visualBackground
width: root.isVertical ? root.widgetThickness : root.calculatedSize
height: root.isVertical ? root.calculatedSize : root.widgetThickness
anchors.centerIn: parent
clip: false
property real scrollAccumulator: 0
property real touchpadThreshold: 500
Rectangle {
id: outline
anchors.centerIn: parent
width: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.width + borderWidth * 2;
}
height: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.height + borderWidth * 2;
}
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: "transparent"
border.width: {
if (barConfig?.widgetOutlineEnabled ?? false) {
return barConfig?.widgetOutlineThickness ?? 1;
}
return 0;
}
border.color: {
if (!(barConfig?.widgetOutlineEnabled ?? false)) {
return "transparent";
}
const colorOption = barConfig?.widgetOutlineColor || "primary";
const opacity = barConfig?.widgetOutlineOpacity ?? 1.0;
switch (colorOption) {
case "surfaceText":
return Theme.withAlpha(Theme.surfaceText, opacity);
case "secondary":
return Theme.withAlpha(Theme.secondary, opacity);
case "primary":
return Theme.withAlpha(Theme.primary, opacity);
default:
return Theme.withAlpha(Theme.primary, opacity);
onWheel: function (wheelEvent) {
const deltaY = wheelEvent.angleDelta.y;
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
const windows = root.sortedToplevels;
if (windows.length < 2)
return;
if (isMouseWheel) {
let currentIndex = -1;
for (var i = 0; i < windows.length; i++) {
if (windows[i].activated) {
currentIndex = i;
break;
}
}
}
Rectangle {
id: background
anchors.fill: parent
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: {
if (windowCount === 0) {
return "transparent";
}
if ((barConfig?.noBackground ?? false)) {
return "transparent";
}
const baseColor = Theme.widgetBaseBackgroundColor;
const transparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0;
if (Theme.widgetBackgroundHasAlpha) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * transparency);
}
return Theme.withAlpha(baseColor, transparency);
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
property real scrollAccumulator: 0
property real touchpadThreshold: 500
onWheel: wheel => {
const deltaY = wheel.angleDelta.y;
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
const windows = root.sortedToplevels;
if (windows.length < 2) {
return;
let nextIndex;
if (deltaY < 0) {
nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, windows.length - 1);
} else {
nextIndex = currentIndex === -1 ? windows.length - 1 : Math.max(currentIndex - 1, 0);
}
if (isMouseWheel) {
// Direct mouse wheel action
const nextWindow = windows[nextIndex];
if (nextWindow)
nextWindow.activate();
} else {
scrollAccumulator += deltaY;
if (Math.abs(scrollAccumulator) >= touchpadThreshold) {
let currentIndex = -1;
for (var i = 0; i < windows.length; i++) {
if (windows[i].activated) {
@@ -250,69 +180,32 @@ Item {
}
let nextIndex;
if (deltaY < 0) {
if (currentIndex === -1) {
nextIndex = 0;
} else {
nextIndex = Math.min(currentIndex + 1, windows.length - 1);
}
if (scrollAccumulator < 0) {
nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, windows.length - 1);
} else {
if (currentIndex === -1) {
nextIndex = windows.length - 1;
} else {
nextIndex = Math.max(currentIndex - 1, 0);
}
nextIndex = currentIndex === -1 ? windows.length - 1 : Math.max(currentIndex - 1, 0);
}
const nextWindow = windows[nextIndex];
if (nextWindow) {
if (nextWindow)
nextWindow.activate();
}
} else {
// Touchpad - accumulate small deltas
scrollAccumulator += deltaY;
if (Math.abs(scrollAccumulator) >= touchpadThreshold) {
let currentIndex = -1;
for (var i = 0; i < windows.length; i++) {
if (windows[i].activated) {
currentIndex = i;
break;
}
}
let nextIndex;
if (scrollAccumulator < 0) {
if (currentIndex === -1) {
nextIndex = 0;
} else {
nextIndex = Math.min(currentIndex + 1, windows.length - 1);
}
} else {
if (currentIndex === -1) {
nextIndex = windows.length - 1;
} else {
nextIndex = Math.max(currentIndex - 1, 0);
}
}
const nextWindow = windows[nextIndex];
if (nextWindow) {
nextWindow.activate();
}
scrollAccumulator = 0;
}
scrollAccumulator = 0;
}
wheel.accepted = true;
}
}
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVertical ? columnLayout : rowLayout
content: Component {
Item {
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVerticalOrientation ? columnLayout : rowLayout
}
}
}
Component {
@@ -466,6 +359,7 @@ Item {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onPressed: mouse => root.triggerRipple(this, mouse.x, mouse.y)
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
if (isGrouped && windowCount > 1) {
@@ -495,7 +389,7 @@ Item {
windowContextMenuLoader.item.triggerBarPosition = root.axis.edge === "left" ? 2 : (root.axis.edge === "right" ? 3 : (root.axis.edge === "top" ? 0 : 1));
windowContextMenuLoader.item.triggerBarThickness = root.barThickness;
windowContextMenuLoader.item.triggerBarSpacing = root.barSpacing;
if (root.isVertical) {
if (root.isVerticalOrientation) {
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2);
const screenX = root.parentScreen ? root.parentScreen.x : 0;
const screenY = root.parentScreen ? root.parentScreen.y : 0;
@@ -526,7 +420,7 @@ Item {
root.hoveredItem = delegateItem;
tooltipLoader.active = true;
if (tooltipLoader.item) {
if (root.isVertical) {
if (root.isVerticalOrientation) {
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2);
const screenX = root.parentScreen ? root.parentScreen.x : 0;
const screenY = root.parentScreen ? root.parentScreen.y : 0;
@@ -711,6 +605,7 @@ Item {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: mouse => root.triggerRipple(this, mouse.x, mouse.y)
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
if (isGrouped && windowCount > 1) {
@@ -740,7 +635,7 @@ Item {
windowContextMenuLoader.item.triggerBarPosition = root.axis.edge === "left" ? 2 : (root.axis.edge === "right" ? 3 : (root.axis.edge === "top" ? 0 : 1));
windowContextMenuLoader.item.triggerBarThickness = root.barThickness;
windowContextMenuLoader.item.triggerBarSpacing = root.barSpacing;
if (root.isVertical) {
if (root.isVerticalOrientation) {
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2);
const screenX = root.parentScreen ? root.parentScreen.x : 0;
const screenY = root.parentScreen ? root.parentScreen.y : 0;
@@ -765,7 +660,7 @@ Item {
root.hoveredItem = delegateItem;
tooltipLoader.active = true;
if (tooltipLoader.item) {
if (root.isVertical) {
if (root.isVerticalOrientation) {
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2);
const screenX = root.parentScreen ? root.parentScreen.x : 0;
const screenY = root.parentScreen ? root.parentScreen.y : 0;

View File

@@ -6,23 +6,19 @@ import Quickshell.Services.SystemTray
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Modules.Plugins
import qs.Services
import qs.Widgets
Item {
BasePill {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
enableBackgroundHover: false
enableCursor: false
property var parentWindow: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
property real barSpacing: 4
property bool isAtBottom: false
property var barConfig: null
property bool isAutoHideBar: false
readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS
readonly property var hiddenTrayIds: {
const envValue = Quickshell.env("DMS_HIDE_TRAYIDS") || "";
return envValue ? envValue.split(",").map(id => id.trim().toLowerCase()) : [];
@@ -102,21 +98,10 @@ Item {
property int dropTargetIndex: -1
property bool suppressShiftAnimation: false
readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length
readonly property int calculatedSize: {
if (allTrayItems.length === 0)
return 0;
const itemCount = mainBarItems.length + (hasHiddenItems ? 1 : 0);
return itemCount * 24 + horizontalPadding * 2;
}
readonly property real visualWidth: isVertical ? widgetThickness : calculatedSize
readonly property real visualHeight: isVertical ? calculatedSize : widgetThickness
width: isVertical ? barThickness : visualWidth
height: isVertical ? visualHeight : barThickness
visible: allTrayItems.length > 0
readonly property real minTooltipY: {
if (!parentScreen || !isVertical) {
if (!parentScreen || !isVerticalOrientation) {
return 0;
}
@@ -135,77 +120,17 @@ Item {
property bool menuOpen: false
property var currentTrayMenu: null
Item {
id: visualBackground
width: root.visualWidth
height: root.visualHeight
anchors.centerIn: parent
content: Component {
Item {
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
Rectangle {
id: outline
anchors.centerIn: parent
width: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.width + borderWidth * 2;
}
height: {
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
return parent.height + borderWidth * 2;
}
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: "transparent"
border.width: {
if (barConfig?.widgetOutlineEnabled ?? false) {
return barConfig?.widgetOutlineThickness ?? 1;
}
return 0;
}
border.color: {
if (!(barConfig?.widgetOutlineEnabled ?? false)) {
return "transparent";
}
const colorOption = barConfig?.widgetOutlineColor || "primary";
const opacity = barConfig?.widgetOutlineOpacity ?? 1.0;
switch (colorOption) {
case "surfaceText":
return Theme.withAlpha(Theme.surfaceText, opacity);
case "secondary":
return Theme.withAlpha(Theme.secondary, opacity);
case "primary":
return Theme.withAlpha(Theme.primary, opacity);
default:
return Theme.withAlpha(Theme.primary, opacity);
}
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVerticalOrientation ? columnComp : rowComp
}
}
Rectangle {
id: background
anchors.fill: parent
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
color: {
if (allTrayItems.length === 0) {
return "transparent";
}
if ((barConfig?.noBackground ?? false)) {
return "transparent";
}
const baseColor = Theme.widgetBaseBackgroundColor;
const transparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0;
if (Theme.widgetBackgroundHasAlpha) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * transparency);
}
return Theme.withAlpha(baseColor, transparency);
}
}
}
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVertical ? columnComp : rowComp
}
Component {
@@ -334,6 +259,11 @@ Item {
font.pixelSize: 10
color: Theme.widgetTextColor
}
DankRipple {
id: itemRipple
cornerRadius: Theme.cornerRadius
}
}
MouseArea {
@@ -344,6 +274,8 @@ Item {
cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
onPressed: mouse => {
const pos = mapToItem(visualContent, mouse.x, mouse.y);
itemRipple.trigger(pos.x, pos.y);
if (mouse.button === Qt.LeftButton) {
dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y);
longPressTimer.start();
@@ -379,7 +311,7 @@ Item {
if (!delegateRoot.trayItem.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
}
onPositionChanged: mouse => {
@@ -412,7 +344,7 @@ Item {
if (!delegateRoot.trayItem?.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
}
}
}
@@ -438,11 +370,19 @@ Item {
color: Theme.widgetTextColor
}
DankRipple {
id: caretRipple
cornerRadius: Theme.cornerRadius
}
MouseArea {
id: caretArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => {
caretRipple.trigger(mouse.x, mouse.y);
}
onClicked: root.menuOpen = !root.menuOpen
}
}
@@ -576,6 +516,11 @@ Item {
font.pixelSize: 10
color: Theme.widgetTextColor
}
DankRipple {
id: itemRipple
cornerRadius: Theme.cornerRadius
}
}
MouseArea {
@@ -586,6 +531,8 @@ Item {
cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
onPressed: mouse => {
const pos = mapToItem(visualContent, mouse.x, mouse.y);
itemRipple.trigger(pos.x, pos.y);
if (mouse.button === Qt.LeftButton) {
dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y);
longPressTimer.start();
@@ -621,7 +568,7 @@ Item {
if (!delegateRoot.trayItem.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
}
onPositionChanged: mouse => {
@@ -654,7 +601,7 @@ Item {
if (!delegateRoot.trayItem?.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
}
}
}
@@ -687,11 +634,19 @@ Item {
color: Theme.widgetTextColor
}
DankRipple {
id: caretRippleVert
cornerRadius: Theme.cornerRadius
}
MouseArea {
id: caretAreaVert
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => {
caretRippleVert.trigger(mouse.x, mouse.y);
}
onClicked: root.menuOpen = !root.menuOpen
}
}
@@ -862,7 +817,7 @@ Item {
const relativeX = globalPos.x - screenX;
const relativeY = globalPos.y - screenY;
if (root.isVertical) {
if (root.isVerticalOrientation) {
const edge = root.axis?.edge;
let targetX = edge === "left" ? root.barThickness + root.barSpacing + Theme.popupDistance : screen.width - (root.barThickness + root.barSpacing + Theme.popupDistance);
const adjustedY = relativeY + root.height / 2 + root.minTooltipY;
@@ -900,7 +855,7 @@ Item {
height: alignedHeight
x: Theme.snap((() => {
if (root.isVertical) {
if (root.isVerticalOrientation) {
const edge = root.axis?.edge;
if (edge === "left") {
const targetX = overflowMenu.anchorPos.x;
@@ -918,7 +873,7 @@ Item {
})(), overflowMenu.dpr)
y: Theme.snap((() => {
if (root.isVertical) {
if (root.isVerticalOrientation) {
const top = Math.max(overflowMenu.barY, 10);
const bottom = overflowMenu.height - alignedHeight - 10;
const want = overflowMenu.anchorPos.y - alignedHeight / 2;
@@ -1074,7 +1029,7 @@ Item {
if (!trayItem.hasMenu)
return;
root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVertical, root.axis);
root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
}
}
}

View File

@@ -133,7 +133,8 @@ BasePill {
z: 1
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onPressed: {
onPressed: mouse => {
root.triggerRipple(this, mouse.x, mouse.y);
if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = root.visualContent.mapToItem(null, 0, 0);
const currentScreen = parentScreen || Screen;

View File

@@ -71,6 +71,7 @@ BasePill {
acceptedButtons: Qt.LeftButton | Qt.RightButton
enabled: !DMSNetworkService.isBusy
onPressed: event => {
root.triggerRipple(this, event.x, event.y);
switch (event.button) {
case Qt.RightButton:
DMSNetworkService.toggleVpn();