mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Merge pull request #851 from lonerOrz/feat/mm
feat: Add circular progress bar to MediaMini widget
This commit is contained in:
@@ -231,6 +231,10 @@
|
||||
"description": "Audio-Visualizer anzeigen, wenn Musik abgespielt wird.",
|
||||
"label": "Visualizer anzeigen"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Runden Fortschrittsindikator anzeigen, der den Titelfortschritt anzeigt.",
|
||||
"label": "Fortschrittsring anzeigen"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Wenn aktiviert, verwendet das Widget immer die maximale Breite, anstatt sich dynamisch an den Inhalt anzupassen.",
|
||||
"label": "Feste Breite verwenden"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Display an audio visualizer when music is playing.",
|
||||
"label": "Show visualizer"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Display a circular progress indicator showing track progress.",
|
||||
"label": "Show progress ring"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "When enabled, the widget will always use the maximum width instead of dynamically adjusting to content.",
|
||||
"label": "Use Fixed Width"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Mostrar un visualizador de audio cuando se reproduce música.",
|
||||
"label": "Mostrar visualizador"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Mostrar un indicador de progreso circular que muestre el progreso de la pista.",
|
||||
"label": "Mostrar anillo de progreso"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Cuando está activado, el widget siempre usará el ancho máximo en lugar de ajustarse dinámicamente al contenido.",
|
||||
"label": "Usar Ancho Fijo"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Afficher un visualiseur audio quand la musique est en cours de lecture.",
|
||||
"label": "Afficher le visualiseur"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Afficher un indicateur de progression circulaire montrant la progression de la piste.",
|
||||
"label": "Afficher l'anneau de progression"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Lorsque activé, le widget utilisera toujours la largeur maximale au lieu de s'ajuster dynamiquement au contenu.",
|
||||
"label": "Utiliser une Largeur Fixe"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Toon een audiovisualizer wanneer muziek wordt afgespeeld.",
|
||||
"label": "Visualizer tonen"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Toon een circulaire voortgangsindicator die het bestandsspoor voortgang toont.",
|
||||
"label": "Voortgangscirkel tonen"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Indien ingeschakeld gebruikt de widget altijd de maximale breedte in plaats van zich aan te passen aan de inhoud.",
|
||||
"label": "Vaste breedte gebruiken"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Exibir um visualizador de áudio quando música está sendo reproduzida.",
|
||||
"label": "Mostrar visualizador"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Exibir um indicador de progresso circular mostrando o progresso da faixa.",
|
||||
"label": "Mostrar anel de progresso"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Quando ativado, o widget sempre usará a largura máxima em vez de ajustar dinamicamente ao conteúdo.",
|
||||
"label": "Usar Largura Fixa"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Отображать аудиовизуализатор при воспроизведении музыки.",
|
||||
"label": "Показывать визуализатор"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Отображать круговой индикатор прогресса воспроизведения трека.",
|
||||
"label": "Показывать кольцо прогресса"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Если включено, виджет всегда будет использовать максимальную ширину вместо динамической подстройки под содержимое.",
|
||||
"label": "Использовать фиксированную ширину"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Müzik çalarken bir ses görselleştirici göster.",
|
||||
"label": "Görselleştiriciyi göster"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Parça ilerlemesini gösteren dairesel bir ilerleme göstergesi gösterin.",
|
||||
"label": "İlerleme halkası göster"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Etkinleştirildiğinde, widget dinamik olarak içerik göre ayarlamak yerine her zaman maksimum genişliği kullanır.",
|
||||
"label": "Sabit Genişlik Kullan"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "Відображати аудіовізуалізатор під час відтворення музики.",
|
||||
"label": "Показувати візуалізатор"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "Відображати круговий індикатор прогресу, що показує просування треку.",
|
||||
"label": "Показувати кільце прогресу"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "Коли увімкнено, віджет завжди використовуватиме максимальну ширину замість динамічного налаштування до вмісту.",
|
||||
"label": "Використовувати фіксовану ширину"
|
||||
|
||||
@@ -231,6 +231,10 @@
|
||||
"description": "播放音乐时显示音频可视化器。",
|
||||
"label": "显示可视化器"
|
||||
},
|
||||
"show-progress-ring": {
|
||||
"description": "显示显示曲目进度的圆形进度指示器。",
|
||||
"label": "显示进度环"
|
||||
},
|
||||
"use-fixed-width": {
|
||||
"description": "启用后,小部件将始终使用最大宽度,而不根据内容动态调整。",
|
||||
"label": "使用固定宽度"
|
||||
|
||||
@@ -42,6 +42,7 @@ Item {
|
||||
readonly property bool showVisualizer: (widgetSettings.showVisualizer !== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer
|
||||
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType !== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
|
||||
readonly property string scrollingMode: (widgetSettings.scrollingMode !== undefined) ? widgetSettings.scrollingMode : widgetMetadata.scrollingMode
|
||||
readonly property bool showProgressRing: (widgetSettings.showProgressRing !== undefined) ? widgetSettings.showProgressRing : widgetMetadata.showProgressRing
|
||||
|
||||
// Maximum widget width with user settings support
|
||||
readonly property real maxWidth: (widgetSettings.maxWidth !== undefined) ? widgetSettings.maxWidth : Math.max(widgetMetadata.maxWidth, screen ? screen.width * 0.06 : 0)
|
||||
@@ -321,14 +322,79 @@ Item {
|
||||
Layout.preferredWidth: Math.round(21 * scaling)
|
||||
Layout.preferredHeight: Math.round(21 * scaling)
|
||||
|
||||
// Background for progress circle
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: Color.transparent
|
||||
}
|
||||
|
||||
// Progress circle
|
||||
Canvas {
|
||||
id: progressCanvas
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0 // Align exactly with parent to avoid clipping
|
||||
visible: showProgressRing // Control visibility with setting
|
||||
z: 0 // Behind the album art
|
||||
|
||||
// Calculate progress ratio: 0 to 1
|
||||
property real progressRatio: {
|
||||
if (!MediaService.currentPlayer || MediaService.trackLength <= 0)
|
||||
return 0;
|
||||
const r = MediaService.currentPosition / MediaService.trackLength;
|
||||
if (isNaN(r) || !isFinite(r))
|
||||
return 0;
|
||||
return Math.max(0, Math.min(1, r));
|
||||
}
|
||||
|
||||
onProgressRatioChanged: requestPaint()
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
var centerX = width / 2;
|
||||
var centerY = height / 2;
|
||||
var radius = Math.min(width, height) / 2 - (1.25 * scaling); // Larger radius, accounting for line width to approach edge
|
||||
|
||||
ctx.reset();
|
||||
|
||||
// Background circle (full track, not played yet)
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
|
||||
ctx.lineWidth = 3 * scaling; // Thicker line width based on scaling property
|
||||
ctx.strokeStyle = Qt.alpha(Color.mOnSurface, 0.4); // More opaque for better visibility
|
||||
ctx.stroke();
|
||||
|
||||
// Progress arc (played portion)
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progressRatio * 2 * Math.PI);
|
||||
ctx.lineWidth = 3 * scaling; // Thicker line width based on scaling property
|
||||
ctx.strokeStyle = Color.mPrimary; // Use primary color for progress
|
||||
ctx.lineCap = "round";
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// Connection to update progress when media position changes
|
||||
Connections {
|
||||
target: MediaService
|
||||
function onCurrentPositionChanged() {
|
||||
progressCanvas.requestPaint();
|
||||
}
|
||||
function onTrackLengthChanged() {
|
||||
progressCanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
NImageCircled {
|
||||
id: trackArt
|
||||
anchors.fill: parent
|
||||
anchors.margins: showProgressRing ? (2.5 * scaling) : 0.5 // Make album art smaller only when progress ring is visible, scaled with widget
|
||||
imagePath: MediaService.trackArtUrl
|
||||
fallbackIcon: MediaService.isPlaying ? "media-pause" : "media-play"
|
||||
fallbackIconSize: 10
|
||||
borderWidth: 0
|
||||
border.color: Color.transparent
|
||||
z: 1 // In front of the progress circle
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -484,6 +550,52 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Progress circle for vertical layout - follows background radius
|
||||
Canvas {
|
||||
id: progressCanvasVertical
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0 // Align with parent container (mainContainer which matches mediaMini)
|
||||
visible: isVerticalBar && showProgressRing // Control visibility with setting
|
||||
z: 0 // Behind other content
|
||||
|
||||
// Calculate progress ratio: 0 to 1
|
||||
property real progressRatio: {
|
||||
if (!MediaService.currentPlayer || MediaService.trackLength <= 0)
|
||||
return 0;
|
||||
const r = MediaService.currentPosition / MediaService.trackLength;
|
||||
if (isNaN(r) || !isFinite(r))
|
||||
return 0;
|
||||
return Math.max(0, Math.min(1, r));
|
||||
}
|
||||
|
||||
onProgressRatioChanged: requestPaint()
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
var centerX = width / 2;
|
||||
var centerY = height / 2;
|
||||
// Align with mediaMini radius which is circular in vertical mode
|
||||
var radius = Math.min(width, height) / 2 - 4; // Position ring near the outer edge of background
|
||||
|
||||
ctx.reset();
|
||||
|
||||
// Background circle (full track, not played yet)
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
|
||||
ctx.lineWidth = 1.5 * scaling; // Line width based on scaling property, thinner for vertical layout
|
||||
ctx.strokeStyle = Qt.alpha(Color.mOnSurface, 0.4); // More opaque for better visibility
|
||||
ctx.stroke();
|
||||
|
||||
// Progress arc (played portion)
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progressRatio * 2 * Math.PI);
|
||||
ctx.lineWidth = 1.5 * scaling; // Line width based on scaling property, thinner for vertical layout
|
||||
ctx.strokeStyle = Color.mPrimary; // Use primary color for progress
|
||||
ctx.lineCap = "round";
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical layout for left/right bars - icon only
|
||||
Item {
|
||||
id: verticalLayout
|
||||
@@ -507,10 +619,22 @@ Item {
|
||||
pointSize: Style.fontSizeL * scaling
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
z: 1 // In front of the progress circle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connection to update vertical progress when media position changes
|
||||
Connections {
|
||||
target: MediaService
|
||||
function onCurrentPositionChanged() {
|
||||
progressCanvasVertical.requestPaint();
|
||||
}
|
||||
function onTrackLengthChanged() {
|
||||
progressCanvasVertical.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse area for hover detection
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
@@ -23,6 +23,7 @@ ColumnLayout {
|
||||
property string valueScrollingMode: widgetData.scrollingMode || widgetMetadata.scrollingMode
|
||||
property int valueMaxWidth: widgetData.maxWidth !== undefined ? widgetData.maxWidth : widgetMetadata.maxWidth
|
||||
property bool valueUseFixedWidth: widgetData.useFixedWidth !== undefined ? widgetData.useFixedWidth : widgetMetadata.useFixedWidth
|
||||
property bool valueShowProgressRing: widgetData.showProgressRing !== undefined ? widgetData.showProgressRing : widgetMetadata.showProgressRing
|
||||
|
||||
Component.onCompleted: {
|
||||
if (widgetData && widgetData.hideMode !== undefined) {
|
||||
@@ -41,6 +42,7 @@ ColumnLayout {
|
||||
settings.scrollingMode = valueScrollingMode;
|
||||
settings.maxWidth = parseInt(widthInput.text) || widgetMetadata.maxWidth;
|
||||
settings.useFixedWidth = valueUseFixedWidth;
|
||||
settings.showProgressRing = valueShowProgressRing;
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -130,6 +132,13 @@ ColumnLayout {
|
||||
onToggled: checked => valueUseFixedWidth = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("bar.widget-settings.media-mini.show-progress-ring.label")
|
||||
description: I18n.tr("bar.widget-settings.media-mini.show-progress-ring.description")
|
||||
checked: valueShowProgressRing
|
||||
onToggled: checked => valueShowProgressRing = checked
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
label: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.label")
|
||||
description: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.description")
|
||||
|
||||
@@ -161,6 +161,7 @@ Singleton {
|
||||
"showAlbumArt": false,
|
||||
"showArtistFirst": true,
|
||||
"showVisualizer": false,
|
||||
"showProgressRing": true,
|
||||
"visualizerType": "linear"
|
||||
},
|
||||
"Microphone": {
|
||||
|
||||
Reference in New Issue
Block a user