mirror of
https://github.com/belsabbagh/dotfiles.git
synced 2026-04-11 09:36:46 +00:00
quickshell and hyprland additions
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property var sidebar
|
||||
readonly property real rounding: Config.border.rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: Colours.palette.m3surface
|
||||
|
||||
PathLine {
|
||||
relativeX: -(root.wrapper.width + root.rounding)
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.sidebar.utilsRoundingX
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.sidebar.utilsRoundingX
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.utilsRoundingX : root.wrapper.width
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.rounding
|
||||
radiusX: root.rounding
|
||||
radiusY: root.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
39
.config/quickshell/caelestia/modules/utilities/Content.qml
Normal file
39
.config/quickshell/caelestia/modules/utilities/Content.qml
Normal file
@@ -0,0 +1,39 @@
|
||||
import "cards"
|
||||
import qs.config
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var props
|
||||
required property var visibilities
|
||||
required property Item popouts
|
||||
|
||||
implicitWidth: layout.implicitWidth
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
IdleInhibit {}
|
||||
|
||||
Record {
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
z: 1
|
||||
}
|
||||
|
||||
Toggles {
|
||||
visibilities: root.visibilities
|
||||
popouts: root.popouts
|
||||
}
|
||||
}
|
||||
|
||||
RecordingDeleteModal {
|
||||
props: root.props
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
import qs.components.effects
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Caelestia
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Shapes
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
required property var props
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: root.props.recordingConfirmDelete ? 1 : 0
|
||||
active: opacity > 0
|
||||
|
||||
sourceComponent: MouseArea {
|
||||
id: deleteConfirmation
|
||||
|
||||
property string path
|
||||
|
||||
Component.onCompleted: path = root.props.recordingConfirmDelete
|
||||
|
||||
hoverEnabled: true
|
||||
onClicked: root.props.recordingConfirmDelete = ""
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.large
|
||||
anchors.rightMargin: -Appearance.padding.large - Config.border.thickness
|
||||
anchors.bottomMargin: -Appearance.padding.large - Config.border.thickness
|
||||
opacity: 0.5
|
||||
|
||||
StyledRect {
|
||||
anchors.fill: parent
|
||||
topLeftRadius: Config.border.rounding
|
||||
color: Colours.palette.m3scrim
|
||||
}
|
||||
|
||||
Shape {
|
||||
id: shape
|
||||
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
asynchronous: true
|
||||
|
||||
ShapePath {
|
||||
startX: -Config.border.rounding * 2
|
||||
startY: shape.height - Config.border.thickness
|
||||
strokeWidth: 0
|
||||
fillGradient: LinearGradient {
|
||||
orientation: LinearGradient.Horizontal
|
||||
x1: -Config.border.rounding * 2
|
||||
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Qt.alpha(Colours.palette.m3scrim, 0)
|
||||
}
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: Colours.palette.m3scrim
|
||||
}
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: Config.border.rounding
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeY: -Config.border.rounding
|
||||
radiusX: Config.border.rounding
|
||||
radiusY: Config.border.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: Config.border.rounding + Config.border.thickness
|
||||
}
|
||||
PathLine {
|
||||
relativeX: -Config.border.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
startX: shape.width - Config.border.rounding - Config.border.thickness
|
||||
strokeWidth: 0
|
||||
fillGradient: LinearGradient {
|
||||
orientation: LinearGradient.Vertical
|
||||
y1: -Config.border.rounding * 2
|
||||
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Qt.alpha(Colours.palette.m3scrim, 0)
|
||||
}
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: Colours.palette.m3scrim
|
||||
}
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: Config.border.rounding
|
||||
relativeY: -Config.border.rounding
|
||||
radiusX: Config.border.rounding
|
||||
radiusY: Config.border.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -Config.border.rounding
|
||||
}
|
||||
PathLine {
|
||||
relativeX: Config.border.thickness
|
||||
relativeY: 0
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
anchors.centerIn: parent
|
||||
radius: Appearance.rounding.large
|
||||
color: Colours.palette.m3surfaceContainerHigh
|
||||
|
||||
scale: 0
|
||||
Component.onCompleted: scale = Qt.binding(() => root.props.recordingConfirmDelete ? 1 : 0)
|
||||
|
||||
width: Math.min(parent.width - Appearance.padding.large * 2, implicitWidth)
|
||||
implicitWidth: deleteConfirmationLayout.implicitWidth + Appearance.padding.large * 3
|
||||
implicitHeight: deleteConfirmationLayout.implicitHeight + Appearance.padding.large * 3
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
level: 3
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: deleteConfirmationLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 1.5
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Delete recording?")
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Recording '%1' will be permanently deleted.").arg(deleteConfirmation.path)
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.small
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.topMargin: Appearance.spacing.normal
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
TextButton {
|
||||
text: qsTr("Cancel")
|
||||
type: TextButton.Text
|
||||
onClicked: root.props.recordingConfirmDelete = ""
|
||||
}
|
||||
|
||||
TextButton {
|
||||
text: qsTr("Delete")
|
||||
type: TextButton.Text
|
||||
onClicked: {
|
||||
CUtils.deleteFile(Qt.resolvedUrl(root.props.recordingConfirmDelete));
|
||||
root.props.recordingConfirmDelete = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
96
.config/quickshell/caelestia/modules/utilities/Wrapper.qml
Normal file
96
.config/quickshell/caelestia/modules/utilities/Wrapper.qml
Normal file
@@ -0,0 +1,96 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var visibilities
|
||||
required property Item sidebar
|
||||
required property Item popouts
|
||||
|
||||
readonly property PersistentProperties props: PersistentProperties {
|
||||
property bool recordingListExpanded: false
|
||||
property string recordingConfirmDelete
|
||||
property string recordingMode
|
||||
|
||||
reloadableId: "utilities"
|
||||
}
|
||||
readonly property bool shouldBeActive: visibilities.sidebar || (visibilities.utilities && Config.utilities.enabled && !(visibilities.session && Config.session.enabled))
|
||||
|
||||
visible: height > 0
|
||||
implicitHeight: 0
|
||||
implicitWidth: sidebar.visible ? sidebar.width : Config.utilities.sizes.width
|
||||
|
||||
onStateChanged: {
|
||||
if (state === "visible" && timer.running) {
|
||||
timer.triggered();
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.shouldBeActive
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight + Appearance.padding.large * 2
|
||||
}
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
running: true
|
||||
interval: Appearance.anim.durations.extraLarge
|
||||
onTriggered: {
|
||||
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
|
||||
content.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.large
|
||||
|
||||
visible: false
|
||||
active: true
|
||||
|
||||
sourceComponent: Content {
|
||||
implicitWidth: root.implicitWidth - Appearance.padding.large * 2
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
popouts: root.popouts
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight + (IdleInhibitor.enabled ? activeChip.implicitHeight + activeChip.anchors.topMargin : 0) + Appearance.padding.large * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
clip: true
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.smaller * 2
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
color: IdleInhibitor.enabled ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "coffee"
|
||||
color: IdleInhibitor.enabled ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Keep Awake")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: IdleInhibitor.enabled ? qsTr("Preventing sleep mode") : qsTr("Normal power management")
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.small
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
StyledSwitch {
|
||||
checked: IdleInhibitor.enabled
|
||||
onToggled: IdleInhibitor.enabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: activeChip
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: Appearance.spacing.larger
|
||||
anchors.bottomMargin: IdleInhibitor.enabled ? Appearance.padding.large : -implicitHeight
|
||||
anchors.leftMargin: Appearance.padding.large
|
||||
|
||||
opacity: IdleInhibitor.enabled ? 1 : 0
|
||||
scale: IdleInhibitor.enabled ? 1 : 0.5
|
||||
|
||||
Component.onCompleted: active = Qt.binding(() => opacity > 0)
|
||||
|
||||
sourceComponent: StyledRect {
|
||||
implicitWidth: activeText.implicitWidth + Appearance.padding.normal * 2
|
||||
implicitHeight: activeText.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
color: Colours.palette.m3primary
|
||||
|
||||
StyledText {
|
||||
id: activeText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Active since %1").arg(Qt.formatTime(IdleInhibitor.enabledSince, Config.services.useTwelveHourClock ? "hh:mm a" : "hh:mm"))
|
||||
color: Colours.palette.m3onPrimary
|
||||
font.pointSize: Math.round(Appearance.font.size.small * 0.9)
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on anchors.bottomMargin {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
277
.config/quickshell/caelestia/modules/utilities/cards/Record.qml
Normal file
277
.config/quickshell/caelestia/modules/utilities/cards/Record.qml
Normal file
@@ -0,0 +1,277 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property var props
|
||||
required property var visibilities
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
RowLayout {
|
||||
spacing: Appearance.spacing.normal
|
||||
z: 1
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: {
|
||||
const h = icon.implicitHeight + Appearance.padding.smaller * 2;
|
||||
return h - (h % 2);
|
||||
}
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
color: Recorder.running ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -0.5
|
||||
anchors.verticalCenterOffset: 1.5
|
||||
text: "screen_record"
|
||||
color: Recorder.running ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Screen Recorder")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: Recorder.paused ? qsTr("Recording paused") : Recorder.running ? qsTr("Recording running") : qsTr("Recording off")
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.small
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
SplitButton {
|
||||
disabled: Recorder.running
|
||||
active: menuItems.find(m => root.props.recordingMode === m.icon + m.text) ?? menuItems[0]
|
||||
menu.onItemSelected: item => root.props.recordingMode = item.icon + item.text
|
||||
|
||||
menuItems: [
|
||||
MenuItem {
|
||||
icon: "fullscreen"
|
||||
text: qsTr("Record fullscreen")
|
||||
activeText: qsTr("Fullscreen")
|
||||
onClicked: Recorder.start()
|
||||
},
|
||||
MenuItem {
|
||||
icon: "screenshot_region"
|
||||
text: qsTr("Record region")
|
||||
activeText: qsTr("Region")
|
||||
onClicked: Recorder.start(["-r"])
|
||||
},
|
||||
MenuItem {
|
||||
icon: "select_to_speak"
|
||||
text: qsTr("Record fullscreen with sound")
|
||||
activeText: qsTr("Fullscreen")
|
||||
onClicked: Recorder.start(["-s"])
|
||||
},
|
||||
MenuItem {
|
||||
icon: "volume_up"
|
||||
text: qsTr("Record region with sound")
|
||||
activeText: qsTr("Region")
|
||||
onClicked: Recorder.start(["-sr"])
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: listOrControls
|
||||
|
||||
property bool running: Recorder.running
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: implicitHeight
|
||||
sourceComponent: running ? recordingControls : recordingList
|
||||
|
||||
Behavior on Layout.preferredHeight {
|
||||
id: locHeightAnim
|
||||
|
||||
enabled: false
|
||||
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on running {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: listOrControls
|
||||
property: "scale"
|
||||
to: 0.7
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
Anim {
|
||||
target: listOrControls
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: locHeightAnim
|
||||
property: "enabled"
|
||||
value: true
|
||||
}
|
||||
PropertyAction {}
|
||||
PropertyAction {
|
||||
target: locHeightAnim
|
||||
property: "enabled"
|
||||
value: false
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: listOrControls
|
||||
property: "scale"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
target: listOrControls
|
||||
property: "opacity"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: recordingList
|
||||
|
||||
RecordingList {
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: recordingControls
|
||||
|
||||
RowLayout {
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledRect {
|
||||
radius: Appearance.rounding.full
|
||||
color: Recorder.paused ? Colours.palette.m3tertiary : Colours.palette.m3error
|
||||
|
||||
implicitWidth: recText.implicitWidth + Appearance.padding.normal * 2
|
||||
implicitHeight: recText.implicitHeight + Appearance.padding.smaller * 2
|
||||
|
||||
StyledText {
|
||||
id: recText
|
||||
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
text: Recorder.paused ? "PAUSED" : "REC"
|
||||
color: Recorder.paused ? Colours.palette.m3onTertiary : Colours.palette.m3onError
|
||||
font.family: Appearance.font.family.mono
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: !Recorder.paused
|
||||
alwaysRunToEnd: true
|
||||
loops: Animation.Infinite
|
||||
|
||||
Anim {
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedAccel
|
||||
}
|
||||
Anim {
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const elapsed = Recorder.elapsed;
|
||||
|
||||
const hours = Math.floor(elapsed / 3600);
|
||||
const mins = Math.floor((elapsed % 3600) / 60);
|
||||
const secs = Math.floor(elapsed % 60).toString().padStart(2, "0");
|
||||
|
||||
let time;
|
||||
if (hours > 0)
|
||||
time = `${hours}:${mins.toString().padStart(2, "0")}:${secs}`;
|
||||
else
|
||||
time = `${mins}:${secs}`;
|
||||
|
||||
return qsTr("Recording for %1").arg(time);
|
||||
}
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
IconButton {
|
||||
label.animate: true
|
||||
icon: Recorder.paused ? "play_arrow" : "pause"
|
||||
toggle: true
|
||||
checked: Recorder.paused
|
||||
type: IconButton.Tonal
|
||||
font.pointSize: Appearance.font.size.large
|
||||
onClicked: {
|
||||
Recorder.togglePause();
|
||||
internalChecked = Recorder.paused;
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: "stop"
|
||||
inactiveColour: Colours.palette.m3error
|
||||
inactiveOnColour: Colours.palette.m3onError
|
||||
font.pointSize: Appearance.font.size.large
|
||||
onClicked: Recorder.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
import qs.components.containers
|
||||
import qs.services
|
||||
import qs.config
|
||||
import qs.utils
|
||||
import Caelestia
|
||||
import Caelestia.Models
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property var props
|
||||
required property var visibilities
|
||||
|
||||
spacing: 0
|
||||
|
||||
WrapperMouseArea {
|
||||
Layout.fillWidth: true
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.props.recordingListExpanded = !root.props.recordingListExpanded
|
||||
|
||||
RowLayout {
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "list"
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Recordings")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: root.props.recordingListExpanded ? "unfold_less" : "unfold_more"
|
||||
type: IconButton.Text
|
||||
label.animate: true
|
||||
onClicked: root.props.recordingListExpanded = !root.props.recordingListExpanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledListView {
|
||||
id: list
|
||||
|
||||
model: FileSystemModel {
|
||||
path: Paths.recsdir
|
||||
nameFilters: ["recording_*.mp4"]
|
||||
sortReverse: true
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: -Appearance.spacing.small
|
||||
implicitHeight: (Appearance.font.size.larger + Appearance.padding.small) * (root.props.recordingListExpanded ? 10 : 3)
|
||||
clip: true
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: list
|
||||
}
|
||||
|
||||
delegate: RowLayout {
|
||||
id: recording
|
||||
|
||||
required property FileSystemEntry modelData
|
||||
property string baseName
|
||||
|
||||
anchors.left: list.contentItem.left
|
||||
anchors.right: list.contentItem.right
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
spacing: Appearance.spacing.small / 2
|
||||
|
||||
Component.onCompleted: baseName = modelData.baseName
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: Appearance.spacing.small / 2
|
||||
text: {
|
||||
const time = recording.baseName;
|
||||
const matches = time.match(/^recording_(\d{4})(\d{2})(\d{2})_(\d{2})-(\d{2})-(\d{2})/);
|
||||
if (!matches)
|
||||
return time;
|
||||
const date = new Date(...matches.slice(1));
|
||||
date.setMonth(date.getMonth() - 1); // Woe (months start from 0)
|
||||
return qsTr("Recording at %1").arg(Qt.formatDateTime(date, Qt.locale()));
|
||||
}
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: "play_arrow"
|
||||
type: IconButton.Text
|
||||
onClicked: {
|
||||
root.visibilities.utilities = false;
|
||||
root.visibilities.sidebar = false;
|
||||
Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.playback, recording.modelData.path]);
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: "folder"
|
||||
type: IconButton.Text
|
||||
onClicked: {
|
||||
root.visibilities.utilities = false;
|
||||
root.visibilities.sidebar = false;
|
||||
Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.explorer, recording.modelData.path]);
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: "delete_forever"
|
||||
type: IconButton.Text
|
||||
label.color: Colours.palette.m3error
|
||||
stateLayer.color: Colours.palette.m3error
|
||||
onClicked: root.props.recordingConfirmDelete = recording.modelData.path
|
||||
}
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
from: 0.5
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
to: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
|
||||
opacity: list.count === 0 ? 1 : 0
|
||||
active: opacity > 0
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "scan_delete"
|
||||
color: Colours.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
|
||||
opacity: root.props.recordingListExpanded ? 1 : 0
|
||||
scale: root.props.recordingListExpanded ? 1 : 0
|
||||
Layout.preferredHeight: root.props.recordingListExpanded ? implicitHeight : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "scan_delete"
|
||||
color: Colours.palette.m3outline
|
||||
|
||||
opacity: !root.props.recordingListExpanded ? 1 : 0
|
||||
scale: !root.props.recordingListExpanded ? 1 : 0
|
||||
Layout.preferredWidth: !root.props.recordingListExpanded ? implicitWidth : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: qsTr("No recordings found")
|
||||
color: Colours.palette.m3outline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
.config/quickshell/caelestia/modules/utilities/cards/Toggles.qml
Normal file
113
.config/quickshell/caelestia/modules/utilities/cards/Toggles.qml
Normal file
@@ -0,0 +1,113 @@
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
import qs.services
|
||||
import qs.config
|
||||
import qs.modules.controlcenter
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property var visibilities
|
||||
required property Item popouts
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Quick Toggles")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
Toggle {
|
||||
icon: "wifi"
|
||||
checked: Nmcli.wifiEnabled
|
||||
onClicked: Nmcli.toggleWifi()
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: "bluetooth"
|
||||
checked: Bluetooth.defaultAdapter?.enabled ?? false
|
||||
onClicked: {
|
||||
const adapter = Bluetooth.defaultAdapter;
|
||||
if (adapter)
|
||||
adapter.enabled = !adapter.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: "mic"
|
||||
checked: !Audio.sourceMuted
|
||||
onClicked: {
|
||||
const audio = Audio.source?.audio;
|
||||
if (audio)
|
||||
audio.muted = !audio.muted;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: "settings"
|
||||
inactiveOnColour: Colours.palette.m3onSurfaceVariant
|
||||
toggle: false
|
||||
onClicked: {
|
||||
root.visibilities.utilities = false;
|
||||
root.popouts.detach("network");
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: "gamepad"
|
||||
checked: GameMode.enabled
|
||||
onClicked: GameMode.enabled = !GameMode.enabled
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: "notifications_off"
|
||||
checked: Notifs.dnd
|
||||
onClicked: Notifs.dnd = !Notifs.dnd
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: "vpn_key"
|
||||
checked: VPN.connected
|
||||
enabled: !VPN.connecting
|
||||
visible: Config.utilities.vpn.provider.some(p => typeof p === "object" ? (p.enabled === true) : false)
|
||||
onClicked: VPN.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Toggle: IconButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? Appearance.padding.large : internalChecked ? Appearance.padding.smaller : 0)
|
||||
radius: stateLayer.pressed ? Appearance.rounding.small / 2 : internalChecked ? Appearance.rounding.small : Appearance.rounding.normal
|
||||
inactiveColour: Colours.layer(Colours.palette.m3surfaceContainerHighest, 2)
|
||||
toggle: true
|
||||
radiusAnim.duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
radiusAnim.easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import qs.components
|
||||
import qs.components.effects
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Caelestia
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property Toast modelData
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: layout.implicitHeight + Appearance.padding.smaller * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: {
|
||||
if (root.modelData.type === Toast.Success)
|
||||
return Colours.palette.m3successContainer;
|
||||
if (root.modelData.type === Toast.Warning)
|
||||
return Colours.palette.m3secondary;
|
||||
if (root.modelData.type === Toast.Error)
|
||||
return Colours.palette.m3errorContainer;
|
||||
return Colours.palette.m3surface;
|
||||
}
|
||||
|
||||
border.width: 1
|
||||
border.color: {
|
||||
let colour = Colours.palette.m3outlineVariant;
|
||||
if (root.modelData.type === Toast.Success)
|
||||
colour = Colours.palette.m3success;
|
||||
if (root.modelData.type === Toast.Warning)
|
||||
colour = Colours.palette.m3secondaryContainer;
|
||||
if (root.modelData.type === Toast.Error)
|
||||
colour = Colours.palette.m3error;
|
||||
return Qt.alpha(colour, 0.3);
|
||||
}
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
opacity: parent.opacity
|
||||
z: -1
|
||||
level: 3
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledRect {
|
||||
radius: Appearance.rounding.normal
|
||||
color: {
|
||||
if (root.modelData.type === Toast.Success)
|
||||
return Colours.palette.m3success;
|
||||
if (root.modelData.type === Toast.Warning)
|
||||
return Colours.palette.m3secondaryContainer;
|
||||
if (root.modelData.type === Toast.Error)
|
||||
return Colours.palette.m3error;
|
||||
return Colours.palette.m3surfaceContainerHigh;
|
||||
}
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.smaller * 2
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: root.modelData.icon
|
||||
color: {
|
||||
if (root.modelData.type === Toast.Success)
|
||||
return Colours.palette.m3onSuccess;
|
||||
if (root.modelData.type === Toast.Warning)
|
||||
return Colours.palette.m3onSecondaryContainer;
|
||||
if (root.modelData.type === Toast.Error)
|
||||
return Colours.palette.m3onError;
|
||||
return Colours.palette.m3onSurfaceVariant;
|
||||
}
|
||||
font.pointSize: Math.round(Appearance.font.size.large * 1.2)
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
id: title
|
||||
|
||||
Layout.fillWidth: true
|
||||
text: root.modelData.title
|
||||
color: {
|
||||
if (root.modelData.type === Toast.Success)
|
||||
return Colours.palette.m3onSuccessContainer;
|
||||
if (root.modelData.type === Toast.Warning)
|
||||
return Colours.palette.m3onSecondary;
|
||||
if (root.modelData.type === Toast.Error)
|
||||
return Colours.palette.m3onErrorContainer;
|
||||
return Colours.palette.m3onSurface;
|
||||
}
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
textFormat: Text.StyledText
|
||||
text: root.modelData.message
|
||||
color: {
|
||||
if (root.modelData.type === Toast.Success)
|
||||
return Colours.palette.m3onSuccessContainer;
|
||||
if (root.modelData.type === Toast.Warning)
|
||||
return Colours.palette.m3onSecondary;
|
||||
if (root.modelData.type === Toast.Error)
|
||||
return Colours.palette.m3onErrorContainer;
|
||||
return Colours.palette.m3onSurface;
|
||||
}
|
||||
opacity: 0.8
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
143
.config/quickshell/caelestia/modules/utilities/toasts/Toasts.qml
Normal file
143
.config/quickshell/caelestia/modules/utilities/toasts/Toasts.qml
Normal file
@@ -0,0 +1,143 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.config
|
||||
import Caelestia
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int spacing: Appearance.spacing.small
|
||||
property bool flag
|
||||
|
||||
implicitWidth: Config.utilities.sizes.toastWidth - Appearance.padding.normal * 2
|
||||
implicitHeight: {
|
||||
let h = -spacing;
|
||||
for (let i = 0; i < repeater.count; i++) {
|
||||
const item = repeater.itemAt(i) as ToastWrapper;
|
||||
if (!item.modelData.closed && !item.previewHidden)
|
||||
h += item.implicitHeight + spacing;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const toasts = [];
|
||||
let count = 0;
|
||||
for (const toast of Toaster.toasts) {
|
||||
toasts.push(toast);
|
||||
if (!toast.closed) {
|
||||
count++;
|
||||
if (count > Config.utilities.maxToasts)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return toasts;
|
||||
}
|
||||
onValuesChanged: root.flagChanged()
|
||||
}
|
||||
|
||||
ToastWrapper {}
|
||||
}
|
||||
|
||||
component ToastWrapper: MouseArea {
|
||||
id: toast
|
||||
|
||||
required property int index
|
||||
required property Toast modelData
|
||||
|
||||
readonly property bool previewHidden: {
|
||||
let extraHidden = 0;
|
||||
for (let i = 0; i < index; i++)
|
||||
if (Toaster.toasts[i].closed)
|
||||
extraHidden++;
|
||||
return index >= Config.utilities.maxToasts + extraHidden;
|
||||
}
|
||||
|
||||
onPreviewHiddenChanged: {
|
||||
if (initAnim.running && previewHidden)
|
||||
initAnim.stop();
|
||||
}
|
||||
|
||||
opacity: modelData.closed || previewHidden ? 0 : 1
|
||||
scale: modelData.closed || previewHidden ? 0.7 : 1
|
||||
|
||||
anchors.bottomMargin: {
|
||||
root.flag; // Force update
|
||||
let y = 0;
|
||||
for (let i = 0; i < index; i++) {
|
||||
const item = repeater.itemAt(i) as ToastWrapper;
|
||||
if (item && !item.modelData.closed && !item.previewHidden)
|
||||
y += item.implicitHeight + root.spacing;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
implicitHeight: toastInner.implicitHeight
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
|
||||
onClicked: modelData.close()
|
||||
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
|
||||
Anim {
|
||||
id: initAnim
|
||||
|
||||
Component.onCompleted: running = !toast.previewHidden
|
||||
|
||||
target: toast
|
||||
properties: "opacity,scale"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: toast.modelData.closed
|
||||
onStarted: toast.anchors.bottomMargin = toast.anchors.bottomMargin
|
||||
onFinished: toast.modelData.unlock(toast)
|
||||
|
||||
Anim {
|
||||
target: toast
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: toast
|
||||
property: "scale"
|
||||
to: 0.7
|
||||
}
|
||||
}
|
||||
|
||||
ToastItem {
|
||||
id: toastInner
|
||||
|
||||
modelData: toast.modelData
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on anchors.bottomMargin {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user