notification changes
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
import QtQuick
|
||||
import qs.Modules
|
||||
|
||||
Flickable {
|
||||
id: root
|
||||
|
||||
maximumFlickVelocity: 3000
|
||||
|
||||
rebound: Transition {
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
|
||||
IconImage {
|
||||
id: root
|
||||
|
||||
asynchronous: true
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
|
||||
ListView {
|
||||
id: root
|
||||
|
||||
maximumFlickVelocity: 3000
|
||||
|
||||
rebound: Transition {
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
import QtQuick
|
||||
import QtQuick.Templates
|
||||
|
||||
ScrollBar {
|
||||
id: root
|
||||
|
||||
required property Flickable flickable
|
||||
property bool shouldBeActive
|
||||
property real nonAnimPosition
|
||||
property bool animating
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered)
|
||||
shouldBeActive = true;
|
||||
else
|
||||
shouldBeActive = flickable.moving;
|
||||
}
|
||||
|
||||
property bool _updatingFromFlickable: false
|
||||
property bool _updatingFromUser: false
|
||||
|
||||
// Sync nonAnimPosition with Qt's automatic position binding
|
||||
onPositionChanged: {
|
||||
if (_updatingFromUser) {
|
||||
_updatingFromUser = false;
|
||||
return;
|
||||
}
|
||||
if (position === nonAnimPosition) {
|
||||
animating = false;
|
||||
return;
|
||||
}
|
||||
if (!animating && !_updatingFromFlickable && !fullMouse.pressed) {
|
||||
nonAnimPosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
// Sync nonAnimPosition with flickable when not animating
|
||||
Connections {
|
||||
target: flickable
|
||||
function onContentYChanged() {
|
||||
if (!animating && !fullMouse.pressed) {
|
||||
_updatingFromFlickable = true;
|
||||
const contentHeight = flickable.contentHeight;
|
||||
const height = flickable.height;
|
||||
if (contentHeight > height) {
|
||||
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
|
||||
} else {
|
||||
nonAnimPosition = 0;
|
||||
}
|
||||
_updatingFromFlickable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (flickable) {
|
||||
const contentHeight = flickable.contentHeight;
|
||||
const height = flickable.height;
|
||||
if (contentHeight > height) {
|
||||
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
|
||||
}
|
||||
}
|
||||
}
|
||||
implicitWidth: 8
|
||||
|
||||
contentItem: CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
opacity: {
|
||||
if (root.size === 1)
|
||||
return 0;
|
||||
if (fullMouse.pressed)
|
||||
return 1;
|
||||
if (mouse.containsMouse)
|
||||
return 0.8;
|
||||
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
|
||||
return 0.6;
|
||||
return 0;
|
||||
}
|
||||
radius: 1000
|
||||
color: DynamicColors.palette.m3secondary
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.flickable
|
||||
|
||||
function onMovingChanged(): void {
|
||||
if (root.flickable.moving)
|
||||
root.shouldBeActive = true;
|
||||
else
|
||||
hideDelay.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hideDelay
|
||||
|
||||
interval: 600
|
||||
onTriggered: root.shouldBeActive = root.flickable.moving || root.hovered
|
||||
}
|
||||
|
||||
CustomMouseArea {
|
||||
id: fullMouse
|
||||
|
||||
anchors.fill: parent
|
||||
preventStealing: true
|
||||
|
||||
onPressed: event => {
|
||||
root.animating = true;
|
||||
root._updatingFromUser = true;
|
||||
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
|
||||
root.nonAnimPosition = newPos;
|
||||
// Update flickable position
|
||||
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
|
||||
if (root.flickable) {
|
||||
const contentHeight = root.flickable.contentHeight;
|
||||
const height = root.flickable.height;
|
||||
if (contentHeight > height) {
|
||||
const maxContentY = contentHeight - height;
|
||||
const maxPos = 1 - root.size;
|
||||
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
|
||||
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: event => {
|
||||
root._updatingFromUser = true;
|
||||
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
|
||||
root.nonAnimPosition = newPos;
|
||||
// Update flickable position
|
||||
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
|
||||
if (root.flickable) {
|
||||
const contentHeight = root.flickable.contentHeight;
|
||||
const height = root.flickable.height;
|
||||
if (contentHeight > height) {
|
||||
const maxContentY = contentHeight - height;
|
||||
const maxPos = 1 - root.size;
|
||||
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
|
||||
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onWheel(event: WheelEvent): void {
|
||||
root.animating = true;
|
||||
root._updatingFromUser = true;
|
||||
let newPos = root.nonAnimPosition;
|
||||
if (event.angleDelta.y > 0)
|
||||
newPos = Math.max(0, root.nonAnimPosition - 0.1);
|
||||
else if (event.angleDelta.y < 0)
|
||||
newPos = Math.min(1 - root.size, root.nonAnimPosition + 0.1);
|
||||
root.nonAnimPosition = newPos;
|
||||
// Update flickable position
|
||||
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
|
||||
if (root.flickable) {
|
||||
const contentHeight = root.flickable.contentHeight;
|
||||
const height = root.flickable.height;
|
||||
if (contentHeight > height) {
|
||||
const maxContentY = contentHeight - height;
|
||||
const maxPos = 1 - root.size;
|
||||
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
|
||||
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on position {
|
||||
enabled: !fullMouse.pressed
|
||||
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
import QtQuick
|
||||
import QtQuick.Templates
|
||||
import QtQuick.Shapes
|
||||
|
||||
Switch {
|
||||
id: root
|
||||
|
||||
property int cLayer: 1
|
||||
|
||||
implicitWidth: implicitIndicatorWidth
|
||||
implicitHeight: implicitIndicatorHeight
|
||||
|
||||
indicator: CustomRect {
|
||||
radius: 1000
|
||||
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
|
||||
|
||||
implicitWidth: implicitHeight * 1.7
|
||||
implicitHeight: 13 + 7 * 2
|
||||
|
||||
CustomRect {
|
||||
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
|
||||
|
||||
radius: 1000
|
||||
color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
|
||||
|
||||
x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
|
||||
implicitWidth: nonAnimWidth
|
||||
implicitHeight: parent.implicitHeight - 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
|
||||
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
||||
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Shape {
|
||||
id: icon
|
||||
|
||||
property point start1: {
|
||||
if (root.pressed)
|
||||
return Qt.point(width * 0.2, height / 2);
|
||||
if (root.checked)
|
||||
return Qt.point(width * 0.15, height / 2);
|
||||
return Qt.point(width * 0.15, height * 0.15);
|
||||
}
|
||||
property point end1: {
|
||||
if (root.pressed) {
|
||||
if (root.checked)
|
||||
return Qt.point(width * 0.4, height / 2);
|
||||
return Qt.point(width * 0.8, height / 2);
|
||||
}
|
||||
if (root.checked)
|
||||
return Qt.point(width * 0.4, height * 0.7);
|
||||
return Qt.point(width * 0.85, height * 0.85);
|
||||
}
|
||||
property point start2: {
|
||||
if (root.pressed) {
|
||||
if (root.checked)
|
||||
return Qt.point(width * 0.4, height / 2);
|
||||
return Qt.point(width * 0.2, height / 2);
|
||||
}
|
||||
if (root.checked)
|
||||
return Qt.point(width * 0.4, height * 0.7);
|
||||
return Qt.point(width * 0.15, height * 0.85);
|
||||
}
|
||||
property point end2: {
|
||||
if (root.pressed)
|
||||
return Qt.point(width * 0.8, height / 2);
|
||||
if (root.checked)
|
||||
return Qt.point(width * 0.85, height * 0.2);
|
||||
return Qt.point(width * 0.85, height * 0.15);
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: height
|
||||
height: parent.implicitHeight - 10 * 2
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
asynchronous: true
|
||||
|
||||
ShapePath {
|
||||
strokeWidth: 20 * 0.15
|
||||
strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
|
||||
fillColor: "transparent"
|
||||
capStyle: 1 === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
|
||||
|
||||
startX: icon.start1.x
|
||||
startY: icon.start1.y
|
||||
|
||||
PathLine {
|
||||
x: icon.end1.x
|
||||
y: icon.end1.y
|
||||
}
|
||||
PathMove {
|
||||
x: icon.start2.x
|
||||
y: icon.start2.y
|
||||
}
|
||||
PathLine {
|
||||
x: icon.end2.x
|
||||
y: icon.end2.y
|
||||
}
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on start1 {
|
||||
PropAnim {}
|
||||
}
|
||||
Behavior on end1 {
|
||||
PropAnim {}
|
||||
}
|
||||
Behavior on start2 {
|
||||
PropAnim {}
|
||||
}
|
||||
Behavior on end2 {
|
||||
PropAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
component PropAnim: PropertyAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
|
||||
RectangularShadow {
|
||||
property int level
|
||||
property real dp: [0, 1, 3, 6, 8, 12][level]
|
||||
|
||||
color: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
|
||||
blur: (dp * 5) ** 0.7
|
||||
spread: -dp * 0.3 + (dp * 0.1) ** 2
|
||||
offset.y: dp / 2
|
||||
|
||||
Behavior on dp {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
import QtQuick
|
||||
|
||||
CustomRect {
|
||||
required property int extra
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 8
|
||||
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
radius: 8
|
||||
|
||||
implicitWidth: count.implicitWidth + 8 * 2
|
||||
implicitHeight: count.implicitHeight + 4 * 2
|
||||
|
||||
opacity: extra > 0 ? 1 : 0
|
||||
scale: extra > 0 ? 1 : 0.5
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
opacity: parent.opacity
|
||||
z: -1
|
||||
level: 2
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: count
|
||||
|
||||
anchors.centerIn: parent
|
||||
animate: parent.opacity > 0
|
||||
text: qsTr("+%1").arg(parent.extra)
|
||||
color: DynamicColors.palette.m3onTertiary
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
import QtQuick
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
enum Type {
|
||||
Filled,
|
||||
Tonal,
|
||||
Text
|
||||
}
|
||||
|
||||
property alias icon: label.text
|
||||
property bool checked
|
||||
property bool toggle
|
||||
property real padding: type === IconButton.Text ? 10 / 2 : 7
|
||||
property alias font: label.font
|
||||
property int type: IconButton.Filled
|
||||
property bool disabled
|
||||
|
||||
property alias stateLayer: stateLayer
|
||||
property alias label: label
|
||||
property alias radiusAnim: radiusAnim
|
||||
|
||||
property bool internalChecked
|
||||
property color activeColour: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
|
||||
property color inactiveColour: {
|
||||
if (!toggle && type === IconButton.Filled)
|
||||
return DynamicColors.palette.m3primary;
|
||||
return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
|
||||
}
|
||||
property color activeOnColour: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
|
||||
property color inactiveOnColour: {
|
||||
if (!toggle && type === IconButton.Filled)
|
||||
return DynamicColors.palette.m3onPrimary;
|
||||
return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant;
|
||||
}
|
||||
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
|
||||
property color disabledOnColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
|
||||
|
||||
signal clicked
|
||||
|
||||
onCheckedChanged: internalChecked = checked
|
||||
|
||||
radius: internalChecked ? 6 : implicitHeight / 2 * Math.min(1, 1)
|
||||
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: label.implicitHeight + padding * 2
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
|
||||
disabled: root.disabled
|
||||
|
||||
function onClicked(): void {
|
||||
if (root.toggle)
|
||||
root.internalChecked = !root.internalChecked;
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: label
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour
|
||||
fill: !root.toggle || root.internalChecked ? 1 : 0
|
||||
|
||||
Behavior on fill {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
id: radiusAnim
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
ShaderEffect {
|
||||
required property Item source
|
||||
required property Item maskSource
|
||||
|
||||
fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/opacitymask.frag.qsb`)
|
||||
}
|
||||
Reference in New Issue
Block a user