mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-06 13:38:28 -04:00
Squashed commit of the following:
commit990d86d481Author: bbedward <bbedward@gmail.com> Date: Sat Jul 18 10:43:22 2026 -0400 flake: update-common commit526cb157fdAuthor: bbedward <bbedward@gmail.com> Date: Thu Jul 16 17:56:40 2026 -0400 i18n: update sync scrirpt for dank-qml-common commit92ba96d9f9Author: bbedward <bbedward@gmail.com> Date: Thu Jul 16 09:24:37 2026 -0400 qs: integrate with dank-qml-common
This commit is contained in:
@@ -1,271 +1,3 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.DankCommon.Widgets as DankCommon
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
function checkParentDisablesTransparency() {
|
||||
let p = parent;
|
||||
while (p) {
|
||||
if (p.disablePopupTransparency === true)
|
||||
return true;
|
||||
p = p.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
property var model: []
|
||||
property int currentIndex: -1
|
||||
property string selectionMode: "single"
|
||||
property bool multiSelect: selectionMode === "multi"
|
||||
property var initialSelection: []
|
||||
property var currentSelection: initialSelection
|
||||
property bool checkEnabled: true
|
||||
property string size: "medium"
|
||||
property int buttonHeight: size === "small" ? 32 : 40
|
||||
property int minButtonWidth: size === "small" ? 56 : 64
|
||||
property int buttonPadding: size === "small" ? Theme.spacingM : Theme.spacingL
|
||||
property int checkIconSize: size === "small" ? Theme.iconSizeSmall - 2 : Theme.iconSizeSmall
|
||||
property int textSize: size === "small" ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||
property bool userInteracted: false
|
||||
property bool usePopupTransparency: !checkParentDisablesTransparency()
|
||||
property real maximumWidth: -1
|
||||
readonly property real _segmentCap: {
|
||||
const count = model?.length ?? 0;
|
||||
if (maximumWidth <= 0 || count === 0)
|
||||
return -1;
|
||||
return (maximumWidth - spacing * (count - 1)) / count - 4;
|
||||
}
|
||||
|
||||
signal selectionChanged(int index, bool selected)
|
||||
signal animationCompleted
|
||||
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Timer {
|
||||
id: animationTimer
|
||||
interval: Theme.shortDuration
|
||||
onTriggered: {
|
||||
root.userInteracted = false;
|
||||
root.animationCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
function isSelected(index) {
|
||||
if (multiSelect) {
|
||||
return repeater.itemAt(index)?.selected || false;
|
||||
}
|
||||
return index === currentIndex;
|
||||
}
|
||||
|
||||
function selectItem(index) {
|
||||
userInteracted = true;
|
||||
if (multiSelect) {
|
||||
const modelValue = model[index];
|
||||
let newSelection = [...currentSelection];
|
||||
const isCurrentlySelected = newSelection.includes(modelValue);
|
||||
|
||||
if (isCurrentlySelected) {
|
||||
newSelection = newSelection.filter(item => item !== modelValue);
|
||||
} else {
|
||||
newSelection.push(modelValue);
|
||||
}
|
||||
|
||||
currentSelection = newSelection;
|
||||
selectionChanged(index, !isCurrentlySelected);
|
||||
animationTimer.restart();
|
||||
} else {
|
||||
const oldIndex = currentIndex;
|
||||
selectionChanged(index, true);
|
||||
if (oldIndex !== index && oldIndex >= 0) {
|
||||
selectionChanged(oldIndex, false);
|
||||
}
|
||||
animationTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: ScriptModel {
|
||||
values: root.model
|
||||
}
|
||||
|
||||
delegate: Rectangle {
|
||||
id: segment
|
||||
|
||||
property bool selected: multiSelect ? root.currentSelection.includes(modelData) : (index === root.currentIndex)
|
||||
property bool hovered: mouseArea.containsMouse
|
||||
property bool pressed: mouseArea.pressed
|
||||
property bool isFirst: index === 0
|
||||
property bool isLast: index === repeater.count - 1
|
||||
property bool visualFirst: I18n.isRtl ? isLast : isFirst
|
||||
property bool visualLast: I18n.isRtl ? isFirst : isLast
|
||||
property bool prevSelected: index > 0 ? root.isSelected(index - 1) : false
|
||||
property bool nextSelected: index < repeater.count - 1 ? root.isSelected(index + 1) : false
|
||||
|
||||
readonly property real contentNaturalWidth: (checkIcon.visible ? checkIcon.width + contentRow.spacing : 0) + buttonText.implicitWidth
|
||||
|
||||
width: {
|
||||
const natural = Math.max(contentNaturalWidth + root.buttonPadding * 2, root.minButtonWidth);
|
||||
const capped = root._segmentCap > 0 ? Math.min(natural, Math.max(root._segmentCap, root.minButtonWidth)) : natural;
|
||||
return capped + (selected ? 4 : 0);
|
||||
}
|
||||
height: root.buttonHeight
|
||||
|
||||
color: selected ? Theme.buttonBg : (root.usePopupTransparency ? Theme.withAlpha(Theme.surfaceVariant, Theme.popupTransparency) : Theme.surfaceVariant)
|
||||
border.color: "transparent"
|
||||
border.width: 0
|
||||
|
||||
topLeftRadius: (visualFirst || selected) ? Theme.cornerRadius : Math.min(4, Theme.cornerRadius)
|
||||
bottomLeftRadius: (visualFirst || selected) ? Theme.cornerRadius : Math.min(4, Theme.cornerRadius)
|
||||
topRightRadius: (visualLast || selected) ? Theme.cornerRadius : Math.min(4, Theme.cornerRadius)
|
||||
bottomRightRadius: (visualLast || selected) ? Theme.cornerRadius : Math.min(4, Theme.cornerRadius)
|
||||
|
||||
Behavior on width {
|
||||
enabled: root.userInteracted
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on topLeftRadius {
|
||||
enabled: root.userInteracted
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on topRightRadius {
|
||||
enabled: root.userInteracted
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on bottomLeftRadius {
|
||||
enabled: root.userInteracted
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on bottomRightRadius {
|
||||
enabled: root.userInteracted
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
enabled: root.userInteracted
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: stateLayer
|
||||
anchors.fill: parent
|
||||
topLeftRadius: parent.topLeftRadius
|
||||
bottomLeftRadius: parent.bottomLeftRadius
|
||||
topRightRadius: parent.topRightRadius
|
||||
bottomRightRadius: parent.bottomRightRadius
|
||||
color: {
|
||||
if (pressed)
|
||||
return selected ? Theme.buttonPressed : Theme.surfaceTextHover;
|
||||
if (hovered)
|
||||
return selected ? Theme.buttonHover : Theme.surfaceTextHover;
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shorterDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankRipple {
|
||||
id: segmentRipple
|
||||
cornerRadius: Theme.cornerRadius
|
||||
rippleColor: segment.selected ? Theme.buttonText : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: contentRow.implicitWidth
|
||||
implicitHeight: contentRow.implicitHeight
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
id: checkIcon
|
||||
name: "check"
|
||||
size: root.checkIconSize
|
||||
color: segment.selected ? Theme.buttonText : Theme.surfaceVariantText
|
||||
visible: root.checkEnabled && segment.selected
|
||||
opacity: segment.selected ? 1 : 0
|
||||
scale: segment.selected ? 1 : 0.6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: root.userInteracted
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
enabled: root.userInteracted
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: buttonText
|
||||
|
||||
readonly property real capAvailable: {
|
||||
if (root._segmentCap <= 0)
|
||||
return -1;
|
||||
const cap = Math.max(root._segmentCap, root.minButtonWidth);
|
||||
return Math.max(0, cap - root.buttonPadding * 2 - (checkIcon.visible ? checkIcon.width + contentRow.spacing : 0));
|
||||
}
|
||||
|
||||
text: typeof modelData === "string" ? modelData : modelData.text || ""
|
||||
font.pixelSize: root.textSize
|
||||
font.weight: segment.selected ? Font.Medium : Font.Normal
|
||||
color: segment.selected ? Theme.buttonText : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: capAvailable < 0 ? implicitWidth : Math.min(implicitWidth, capAvailable)
|
||||
maximumLineCount: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: mouse => segmentRipple.trigger(mouse.x, mouse.y)
|
||||
onClicked: root.selectItem(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DankCommon.DankButtonGroup {}
|
||||
|
||||
Reference in New Issue
Block a user