Files
bearwave/src/qml/components/PlayerBar.qml
Sebastian Palencsar 4806f9306a feat(ui): Mac-inspired layout with sidebar, details panel, and About page
Replace the modal About dialog with an embedded About page and introduce
a three-pane desktop layout: persistent sidebar navigation, flat station
list, right-side details panel, and compact player bar.

Load the GPLv3 license text from C++ and expose it to QML as context
data. Normalize i18n source strings to English, complete German and
Russian translations (133 strings each), and centralize page navigation
helpers in Main.qml. Simplify compact HeaderNavigation and extend
.gitignore for local build artifacts.
2026-06-27 13:21:55 +02:00

191 lines
6.5 KiB
QML

// Copyright (c) 2026 Sebastian Palencsar
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import theme 1.0
Rectangle {
id: root
required property var app
implicitHeight: 76
radius: 0
color: BearTheme.panel
border.color: "transparent"
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 1
color: BearTheme.cardBorder
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 12
anchors.rightMargin: 12
anchors.topMargin: 9
anchors.bottomMargin: 9
spacing: 12
Rectangle {
Layout.preferredWidth: 56
Layout.preferredHeight: 56
radius: 6
color: "#34353b"
clip: true
border.color: "transparent"
Image {
id: coverImage
anchors.fill: parent
source: {
if (app.backend && app.backend.player && app.backend.player.currentCoverArtUrl
&& app.backend.player.currentCoverArtUrl !== "") {
return app.backend.player.currentCoverArtUrl
}
if (app.backend && app.backend.currentStation && app.backend.currentStation.favicon
&& app.backend.currentStation.favicon.startsWith("https://")) {
return app.backend.currentStation.favicon
}
return "qrc:/assets/app/bearwave.png"
}
fillMode: (app.backend && app.backend.player && app.backend.player.currentCoverArtUrl
&& app.backend.player.currentCoverArtUrl !== "")
? Image.PreserveAspectCrop : Image.PreserveAspectFit
smooth: true
asynchronous: true
anchors.margins: {
if (app.backend && app.backend.player && app.backend.player.currentCoverArtUrl
&& app.backend.player.currentCoverArtUrl !== "") {
return 0
}
if (app.backend && app.backend.currentStation && app.backend.currentStation.favicon
&& app.backend.currentStation.favicon.startsWith("https://")) {
return 8
}
return 10
}
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 3
Label {
Layout.fillWidth: true
text: app.backend && app.backend.player
? (app.backend.player.currentStationName || qsTr("No station selected"))
: qsTr("No station selected")
color: BearTheme.textMain
font.pixelSize: 14
font.bold: true
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: app.backend && app.backend.player && app.backend.player.currentNowPlaying.length > 0
? (qsTr("Now playing: ") + app.backend.player.currentNowPlaying)
: qsTr("Live radio")
color: BearTheme.textMuted
font.pixelSize: 12
elide: Text.ElideRight
}
}
RowLayout {
Layout.alignment: Qt.AlignVCenter
spacing: 8
Button {
text: "⏮"
Layout.preferredWidth: 42
Layout.preferredHeight: 30
enabled: app.backend ? app.backend.hasPreviousStation() : false
onClicked: {
if (app.backend) {
app.backend.playPreviousStation()
}
}
}
Button {
text: app.backend && app.backend.player && app.backend.player.playing ? "⏸" : "▶"
Layout.preferredWidth: 42
Layout.preferredHeight: 30
onClicked: {
if (app.backend && app.backend.player) {
app.backend.player.togglePlayPause()
}
}
}
Button {
text: "⏹"
Layout.preferredWidth: 42
Layout.preferredHeight: 30
onClicked: {
if (app.backend && app.backend.player) {
app.backend.player.stop()
}
}
}
Button {
text: "⏭"
Layout.preferredWidth: 42
Layout.preferredHeight: 30
enabled: app.backend ? app.backend.hasNextStation() : false
onClicked: {
if (app.backend) {
app.backend.playNextStation()
}
}
}
Button {
id: muteButton
text: (app.backend && app.backend.player && app.backend.player.volume > 0) ? "🔊" : "🔇"
Layout.preferredWidth: 36
Layout.preferredHeight: 30
property real lastVolume: 0.5
ToolTip.visible: hovered
ToolTip.text: (app.backend && app.backend.player && app.backend.player.volume > 0)
? qsTr("Mute") : qsTr("Unmute")
onClicked: {
if (app.backend && app.backend.player) {
if (app.backend.player.volume > 0) {
lastVolume = app.backend.player.volume
app.backend.player.setVolume(0)
} else {
app.backend.player.setVolume(lastVolume > 0 ? lastVolume : 0.5)
}
}
}
}
Slider {
Layout.preferredWidth: app.width < 1180 ? 115 : 160
from: 0
to: 1
value: app.backend && app.backend.player ? app.backend.player.volume : 0.5
onMoved: {
if (app.backend && app.backend.player) {
app.backend.player.setVolume(value)
}
}
}
}
}
}