mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
Add catpuccin theme + material 3 components
- Update DankToggle to be more representative of m3 design - Add DankButtonGroup to represent a m3 button group
This commit is contained in:
185
Widgets/DankButtonGroup.qml
Normal file
185
Widgets/DankButtonGroup.qml
Normal file
@@ -0,0 +1,185 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
property var model: []
|
||||
property int currentIndex: -1
|
||||
property string selectionMode: "single"
|
||||
property bool multiSelect: selectionMode === "multi"
|
||||
|
||||
signal selectionChanged(int index, bool selected)
|
||||
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
function isSelected(index) {
|
||||
if (multiSelect) {
|
||||
return repeater.itemAt(index)?.selected || false
|
||||
}
|
||||
return index === currentIndex
|
||||
}
|
||||
|
||||
function selectItem(index) {
|
||||
if (multiSelect) {
|
||||
const item = repeater.itemAt(index)
|
||||
if (item) {
|
||||
item.selected = !item.selected
|
||||
selectionChanged(index, item.selected)
|
||||
}
|
||||
} else {
|
||||
const oldIndex = currentIndex
|
||||
currentIndex = index
|
||||
selectionChanged(index, true)
|
||||
if (oldIndex !== index && oldIndex >= 0) {
|
||||
selectionChanged(oldIndex, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: root.model
|
||||
|
||||
delegate: Rectangle {
|
||||
id: segment
|
||||
|
||||
property bool selected: multiSelect ? false : (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 prevSelected: index > 0 ? root.isSelected(index - 1) : false
|
||||
property bool nextSelected: index < repeater.count - 1 ? root.isSelected(index + 1) : false
|
||||
|
||||
width: Math.max(contentItem.implicitWidth + Theme.spacingL * 2, 64) + (selected ? 4 : 0)
|
||||
height: 40
|
||||
|
||||
color: selected ? Theme.primaryContainer : Theme.primary
|
||||
border.color: "transparent"
|
||||
border.width: 0
|
||||
|
||||
topLeftRadius: (isFirst || selected) ? Theme.cornerRadius : 4
|
||||
bottomLeftRadius: (isFirst || selected) ? Theme.cornerRadius : 4
|
||||
topRightRadius: (isLast || selected) ? Theme.cornerRadius : 4
|
||||
bottomRightRadius: (isLast || selected) ? Theme.cornerRadius : 4
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on topLeftRadius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on topRightRadius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on bottomLeftRadius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on bottomRightRadius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
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.primaryPressed : Theme.surfacePressed
|
||||
if (hovered) return selected ? Theme.primaryHover : Theme.surfaceHover
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shorterDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: contentRow.implicitWidth
|
||||
implicitHeight: contentRow.implicitHeight
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
id: checkIcon
|
||||
name: "check"
|
||||
size: Theme.iconSizeSmall
|
||||
color: segment.selected ? Theme.surfaceText : Theme.primaryText
|
||||
visible: segment.selected
|
||||
opacity: segment.selected ? 1 : 0
|
||||
scale: segment.selected ? 1 : 0.6
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: buttonText
|
||||
text: typeof modelData === "string" ? modelData : modelData.text || ""
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: segment.selected ? Font.Medium : Font.Normal
|
||||
color: segment.selected ? Theme.surfaceText : Theme.primaryText
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.selectItem(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import qs.Widgets
|
||||
Item {
|
||||
id: toggle
|
||||
|
||||
// API
|
||||
property bool checked: false
|
||||
property bool enabled: true
|
||||
property bool toggling: false
|
||||
@@ -17,13 +18,15 @@ Item {
|
||||
|
||||
readonly property bool showText: text && !hideText
|
||||
|
||||
width: showText ? parent.width : 48
|
||||
height: showText ? 60 : 24
|
||||
readonly property int trackWidth: 52
|
||||
readonly property int trackHeight: 30
|
||||
readonly property int insetCircle: 24
|
||||
|
||||
width: showText ? parent.width : trackWidth
|
||||
height: showText ? 60 : trackHeight
|
||||
|
||||
function handleClick() {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
if (!enabled) return
|
||||
checked = !checked
|
||||
clicked()
|
||||
toggled(checked)
|
||||
@@ -31,7 +34,6 @@ Item {
|
||||
|
||||
StyledRect {
|
||||
id: background
|
||||
|
||||
anchors.fill: parent
|
||||
radius: showText ? Theme.cornerRadius : 0
|
||||
color: showText ? Theme.surfaceHover : "transparent"
|
||||
@@ -80,26 +82,35 @@ Item {
|
||||
StyledRect {
|
||||
id: toggleTrack
|
||||
|
||||
width: text ? 48 : parent.width
|
||||
height: text ? 24 : parent.height
|
||||
width: showText ? trackWidth : Math.max(parent.width, trackWidth)
|
||||
height: showText ? trackHeight : Math.max(parent.height, trackHeight)
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: text ? Theme.spacingM : 0
|
||||
anchors.rightMargin: showText ? Theme.spacingM : 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: height / 2
|
||||
|
||||
color: (checked && enabled) ? Theme.primary : Theme.surfaceVariantAlpha
|
||||
opacity: toggling ? 0.6 : (enabled ? 1 : 0.4)
|
||||
|
||||
StyledRect {
|
||||
id: toggleHandle
|
||||
border.color: (!checked || !enabled) ? Theme.outline : "transparent"
|
||||
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: Theme.surface
|
||||
readonly property int pad: Math.round((height - thumb.width) / 2)
|
||||
readonly property int edgeLeft: pad
|
||||
readonly property int edgeRight: width - thumb.width - pad
|
||||
|
||||
StyledRect {
|
||||
id: thumb
|
||||
|
||||
width: (checked && enabled) ? insetCircle : insetCircle - 4
|
||||
height: (checked && enabled) ? insetCircle : insetCircle - 4
|
||||
radius: width / 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: (checked && enabled) ? parent.width - width - 2 : 2
|
||||
border.color: Qt.rgba(0, 0, 0, 0.1)
|
||||
border.width: 1
|
||||
|
||||
color: (checked && enabled) ? Theme.surface : Theme.outline
|
||||
border.color: (checked && enabled) ? Theme.outline : Theme.outline
|
||||
border.width: (checked && enabled) ? 1 : 2
|
||||
|
||||
x: (checked && enabled) ? toggleTrack.edgeRight : toggleTrack.edgeLeft
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
@@ -108,6 +119,48 @@ Item {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on border.width {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: checkIcon
|
||||
anchors.centerIn: parent
|
||||
name: "check"
|
||||
size: 20
|
||||
color: Theme.surfaceText
|
||||
filled: true
|
||||
opacity: checked && enabled ? 1 : 0
|
||||
scale: checked && enabled ? 1 : 0.6
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Anims.durShort
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Anims.emphasized
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Anims.durShort
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Anims.emphasized
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
|
||||
Reference in New Issue
Block a user