mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
make media control seekable (closes #1615)
This commit is contained in:
@@ -72,8 +72,8 @@ RippleButton {
|
|||||||
Layout.bottomMargin: 5
|
Layout.bottomMargin: 5
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
value: 0.7
|
value: 0.7
|
||||||
sperm: true
|
wavy: true
|
||||||
animateSperm: lightDarkButtonRoot.toggled
|
animateWave: lightDarkButtonRoot.toggled
|
||||||
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
|
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
|
||||||
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
|
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import qs.services
|
pragma ComponentBehavior: Bound
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Widgets
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Material 3 progress bar. See https://m3.material.io/components/progress-indicators/overview
|
* Material 3 progress bar. See https://m3.material.io/components/progress-indicators/overview
|
||||||
@@ -18,13 +15,13 @@ ProgressBar {
|
|||||||
property real valueBarGap: 4
|
property real valueBarGap: 4
|
||||||
property color highlightColor: Appearance?.colors.colPrimary ?? "#685496"
|
property color highlightColor: Appearance?.colors.colPrimary ?? "#685496"
|
||||||
property color trackColor: Appearance?.m3colors.m3secondaryContainer ?? "#F1D3F9"
|
property color trackColor: Appearance?.m3colors.m3secondaryContainer ?? "#F1D3F9"
|
||||||
property bool sperm: false // If true, the progress bar will have a wavy fill effect
|
property bool wavy: false // If true, the progress bar will have a wavy fill effect
|
||||||
property bool animateSperm: true
|
property bool animateWave: true
|
||||||
property real spermAmplitudeMultiplier: sperm ? 0.5 : 0
|
property real waveAmplitudeMultiplier: wavy ? 0.5 : 0
|
||||||
property real spermFrequency: 6
|
property real waveFrequency: 6
|
||||||
property real spermFps: 60
|
property real waveFps: 60
|
||||||
|
|
||||||
Behavior on spermAmplitudeMultiplier {
|
Behavior on waveAmplitudeMultiplier {
|
||||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,64 +35,62 @@ ProgressBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
|
id: contentItem
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Canvas {
|
Loader {
|
||||||
id: wavyFill
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
height: parent.height * 6
|
active: root.wavy
|
||||||
onPaint: {
|
sourceComponent: WavyLine {
|
||||||
var ctx = getContext("2d");
|
id: wavyFill
|
||||||
ctx.clearRect(0, 0, width, height);
|
frequency: root.waveFrequency
|
||||||
|
color: root.highlightColor
|
||||||
var progress = root.visualPosition;
|
amplitudeMultiplier: root.wavy ? 0.5 : 0
|
||||||
var fillWidth = progress * width;
|
height: contentItem.height * 6
|
||||||
var amplitude = parent.height * root.spermAmplitudeMultiplier;
|
width: contentItem.width * root.visualPosition
|
||||||
var frequency = root.spermFrequency;
|
lineWidth: contentItem.height
|
||||||
var phase = Date.now() / 400.0;
|
fullLength: root.width
|
||||||
var centerY = height / 2;
|
Connections {
|
||||||
|
target: root
|
||||||
ctx.strokeStyle = root.highlightColor;
|
function onValueChanged() { wavyFill.requestPaint(); }
|
||||||
ctx.lineWidth = parent.height;
|
function onHighlightColorChanged() { wavyFill.requestPaint(); }
|
||||||
ctx.lineCap = "round";
|
}
|
||||||
ctx.beginPath();
|
FrameAnimation {
|
||||||
for (var x = ctx.lineWidth / 2; x <= fillWidth; x += 1) {
|
running: root.animateWave
|
||||||
var waveY = centerY + amplitude * Math.sin(frequency * 2 * Math.PI * x / width + phase);
|
onTriggered: {
|
||||||
if (x === 0)
|
wavyFill.requestPaint()
|
||||||
ctx.moveTo(x, waveY);
|
}
|
||||||
else
|
|
||||||
ctx.lineTo(x, waveY);
|
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onValueChanged() { wavyFill.requestPaint(); }
|
|
||||||
function onHighlightColorChanged() { wavyFill.requestPaint(); }
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
interval: 1000 / root.spermFps
|
|
||||||
running: root.animateSperm
|
|
||||||
repeat: root.sperm
|
|
||||||
onTriggered: wavyFill.requestPaint()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: !root.wavy
|
||||||
|
sourceComponent: Rectangle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: contentItem.width * root.visualPosition
|
||||||
|
height: contentItem.height
|
||||||
|
radius: height / 2
|
||||||
|
color: root.highlightColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle { // Right remaining part fill
|
Rectangle { // Right remaining part fill
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: Appearance?.rounding.full ?? 9999
|
radius: height / 2
|
||||||
color: root.trackColor
|
color: root.trackColor
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle { // Stop point
|
Rectangle { // Stop point
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: valueBarGap
|
width: valueBarGap
|
||||||
height: valueBarGap
|
height: valueBarGap
|
||||||
radius: Appearance?.rounding.full ?? 9999
|
radius: height / 2
|
||||||
color: root.highlightColor
|
color: root.highlightColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.services
|
import qs.services
|
||||||
@@ -17,6 +18,7 @@ Slider {
|
|||||||
|
|
||||||
property list<real> stopIndicatorValues: [1]
|
property list<real> stopIndicatorValues: [1]
|
||||||
enum Configuration {
|
enum Configuration {
|
||||||
|
Wavy = 4,
|
||||||
XS = 12,
|
XS = 12,
|
||||||
S = 18,
|
S = 18,
|
||||||
M = 30,
|
M = 30,
|
||||||
@@ -28,10 +30,9 @@ Slider {
|
|||||||
|
|
||||||
property real handleDefaultWidth: 3
|
property real handleDefaultWidth: 3
|
||||||
property real handlePressedWidth: 1.5
|
property real handlePressedWidth: 1.5
|
||||||
|
|
||||||
property color highlightColor: Appearance.colors.colPrimary
|
property color highlightColor: Appearance.colors.colPrimary
|
||||||
property color trackColor: Appearance.colors.colSecondaryContainer
|
property color trackColor: Appearance.colors.colSecondaryContainer
|
||||||
property color handleColor: Appearance.m3colors.m3onSecondaryContainer
|
property color handleColor: Appearance.colors.colPrimary
|
||||||
property color dotColor: Appearance.m3colors.m3onSecondaryContainer
|
property color dotColor: Appearance.m3colors.m3onSecondaryContainer
|
||||||
property color dotColorHighlighted: Appearance.m3colors.m3onPrimary
|
property color dotColorHighlighted: Appearance.m3colors.m3onPrimary
|
||||||
property real unsharpenRadius: Appearance.rounding.unsharpen
|
property real unsharpenRadius: Appearance.rounding.unsharpen
|
||||||
@@ -39,15 +40,18 @@ Slider {
|
|||||||
property real trackRadius: trackWidth >= StyledSlider.Configuration.XL ? 21
|
property real trackRadius: trackWidth >= StyledSlider.Configuration.XL ? 21
|
||||||
: trackWidth >= StyledSlider.Configuration.L ? 12
|
: trackWidth >= StyledSlider.Configuration.L ? 12
|
||||||
: trackWidth >= StyledSlider.Configuration.M ? 9
|
: trackWidth >= StyledSlider.Configuration.M ? 9
|
||||||
: 6
|
: trackWidth >= StyledSlider.Configuration.S ? 6
|
||||||
property real handleHeight: Math.max(33, trackWidth + 9)
|
: height / 2
|
||||||
|
property real handleHeight: (configuration === StyledSlider.Configuration.Wavy) ? 24 : Math.max(33, trackWidth + 9)
|
||||||
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
|
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
|
||||||
property real handleMargins: 4
|
property real handleMargins: 4
|
||||||
onHandleMarginsChanged: {
|
|
||||||
console.log("Handle margins changed to", handleMargins);
|
|
||||||
}
|
|
||||||
property real trackDotSize: 3
|
property real trackDotSize: 3
|
||||||
property string tooltipContent: `${Math.round(value * 100)}%`
|
property string tooltipContent: `${Math.round(value * 100)}%`
|
||||||
|
property bool wavy: configuration === StyledSlider.Configuration.Wavy // If true, the progress bar will have a wavy fill effect
|
||||||
|
property bool animateWave: true
|
||||||
|
property real waveAmplitudeMultiplier: wavy ? 0.5 : 0
|
||||||
|
property real waveFrequency: 6
|
||||||
|
property real waveFps: 60
|
||||||
|
|
||||||
leftPadding: handleMargins
|
leftPadding: handleMargins
|
||||||
rightPadding: handleMargins
|
rightPadding: handleMargins
|
||||||
@@ -93,18 +97,51 @@ Slider {
|
|||||||
implicitHeight: trackWidth
|
implicitHeight: trackWidth
|
||||||
|
|
||||||
// Fill left
|
// Fill left
|
||||||
Rectangle {
|
Loader {
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
left: parent.left
|
left: parent.left
|
||||||
}
|
}
|
||||||
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||||
height: trackWidth
|
height: root.trackWidth
|
||||||
color: root.highlightColor
|
active: !root.wavy
|
||||||
topLeftRadius: root.trackRadius
|
sourceComponent: Rectangle {
|
||||||
bottomLeftRadius: root.trackRadius
|
color: root.highlightColor
|
||||||
topRightRadius: root.unsharpenRadius
|
topLeftRadius: root.trackRadius
|
||||||
bottomRightRadius: root.unsharpenRadius
|
bottomLeftRadius: root.trackRadius
|
||||||
|
topRightRadius: root.unsharpenRadius
|
||||||
|
bottomRightRadius: root.unsharpenRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
}
|
||||||
|
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||||
|
height: root.height
|
||||||
|
active: root.wavy
|
||||||
|
sourceComponent: WavyLine {
|
||||||
|
id: wavyFill
|
||||||
|
frequency: root.waveFrequency
|
||||||
|
fullLength: root.width
|
||||||
|
color: root.highlightColor
|
||||||
|
amplitudeMultiplier: root.wavy ? 0.5 : 0
|
||||||
|
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||||
|
height: root.trackWidth
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onValueChanged() { wavyFill.requestPaint(); }
|
||||||
|
function onHighlightColorChanged() { wavyFill.requestPaint(); }
|
||||||
|
}
|
||||||
|
FrameAnimation {
|
||||||
|
running: root.animateWave
|
||||||
|
onTriggered: {
|
||||||
|
wavyFill.requestPaint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill right
|
// Fill right
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import qs.modules.common
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: root
|
||||||
|
property real amplitudeMultiplier: 0.5
|
||||||
|
property real frequency: 6
|
||||||
|
property color color: Appearance?.colors.colPrimary ?? "#685496"
|
||||||
|
property real lineWidth: 4
|
||||||
|
property real fullLength: width
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
var amplitude = root.lineWidth * root.amplitudeMultiplier;
|
||||||
|
var frequency = root.frequency;
|
||||||
|
var phase = Date.now() / 400.0;
|
||||||
|
var centerY = height / 2;
|
||||||
|
|
||||||
|
ctx.strokeStyle = root.color;
|
||||||
|
ctx.lineWidth = root.lineWidth;
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
ctx.beginPath();
|
||||||
|
for (var x = ctx.lineWidth / 2; x <= root.width - ctx.lineWidth / 2; x += 1) {
|
||||||
|
var waveY = centerY + amplitude * Math.sin(frequency * 2 * Math.PI * x / root.fullLength + phase);
|
||||||
|
if (x === 0)
|
||||||
|
ctx.moveTo(x, waveY);
|
||||||
|
else
|
||||||
|
ctx.lineTo(x, waveY);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.models
|
import qs.modules.common.models
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
@@ -233,16 +234,41 @@ Item { // Player instance
|
|||||||
Item {
|
Item {
|
||||||
id: progressBarContainer
|
id: progressBarContainer
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: progressBar.implicitHeight
|
implicitHeight: Math.max(sliderLoader.implicitHeight, progressBarLoader.implicitHeight)
|
||||||
|
|
||||||
StyledProgressBar {
|
Loader {
|
||||||
id: progressBar
|
id: sliderLoader
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
highlightColor: blendedColors.colPrimary
|
active: playerController.player?.canSeek ?? false
|
||||||
trackColor: blendedColors.colSecondaryContainer
|
sourceComponent: StyledSlider {
|
||||||
value: playerController.player?.position / playerController.player?.length
|
configuration: StyledSlider.Configuration.Wavy
|
||||||
sperm: playerController.player?.isPlaying
|
highlightColor: blendedColors.colPrimary
|
||||||
|
trackColor: blendedColors.colSecondaryContainer
|
||||||
|
handleColor: blendedColors.colPrimary
|
||||||
|
value: playerController.player?.position / playerController.player?.length
|
||||||
|
onMoved: {
|
||||||
|
playerController.player.position = value * playerController.player.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: progressBarLoader
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
active: !(playerController.player?.canSeek ?? false)
|
||||||
|
sourceComponent: StyledProgressBar {
|
||||||
|
wavy: playerController.player?.isPlaying
|
||||||
|
highlightColor: blendedColors.colPrimary
|
||||||
|
trackColor: blendedColors.colSecondaryContainer
|
||||||
|
value: playerController.player?.position / playerController.player?.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
TrackChangeButton {
|
TrackChangeButton {
|
||||||
iconName: "skip_next"
|
iconName: "skip_next"
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Item {
|
|||||||
StyledSlider {
|
StyledSlider {
|
||||||
id: slider
|
id: slider
|
||||||
value: root.node.audio.volume
|
value: root.node.audio.volume
|
||||||
onValueChanged: root.node.audio.volume = value
|
onMoved: root.node.audio.volume = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user