mirror of
https://github.com/spalencsar/bearwave.git
synced 2026-07-06 22:24:17 +02:00
Move ScrollView/ListView/empty-state into StationListPanel and toast UI into ToastPopup with show(). Main.qml is now ~271 lines; stationList alias preserved for search debounce logic.
272 lines
7.1 KiB
QML
272 lines
7.1 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
|
|
import components 1.0
|
|
|
|
ApplicationWindow {
|
|
id: root
|
|
title: qsTr("BearWave")
|
|
width: 980
|
|
height: 700
|
|
minimumWidth: 520
|
|
minimumHeight: 460
|
|
visible: true
|
|
|
|
onClosing: function(close) {
|
|
close.accepted = false
|
|
root.hide()
|
|
}
|
|
|
|
property var currentPage: "top"
|
|
property var backend: (typeof radioBackend !== "undefined" ? radioBackend : null)
|
|
property bool compactMode: width < 780
|
|
property real contentOpacity: 1.0
|
|
property string activeQuickFilter: ""
|
|
property string selectedWorldCategory: ""
|
|
property string selectedWorldType: ""
|
|
property string countrySearchText: ""
|
|
property alias searchField: searchToolbar.searchField
|
|
property alias stationList: stationPanel.stationList
|
|
|
|
function toast(message) {
|
|
toastPopup.show(message)
|
|
}
|
|
|
|
function resetSearchFilter() {
|
|
searchTimer.stop()
|
|
if (searchField.text !== "") {
|
|
searchField.text = ""
|
|
} else if (backend && backend.filterQuery !== "") {
|
|
backend.filterQuery = ""
|
|
}
|
|
}
|
|
|
|
function getFilteredCountries() {
|
|
if (!backend || !backend.countries) return [];
|
|
var query = countrySearchText.toLowerCase().trim();
|
|
var list = [];
|
|
if (query === "") {
|
|
list.push({ name: qsTr("Top Global"), code: "GLOBAL" });
|
|
}
|
|
for (var i = 0; i < backend.countries.length; i++) {
|
|
var c = backend.countries[i];
|
|
if (query === "" || c.name.toLowerCase().indexOf(query) !== -1 || c.code.toLowerCase().indexOf(query) !== -1) {
|
|
list.push(c);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
function activeModel() {
|
|
if (!backend) {
|
|
return []
|
|
}
|
|
if (currentPage === "favorites") {
|
|
return backend.favoriteStations
|
|
} else if (currentPage === "history") {
|
|
return backend.recentStations
|
|
} else if (currentPage === "world" && selectedWorldCategory === "") {
|
|
return []
|
|
} else {
|
|
return backend.stations
|
|
}
|
|
}
|
|
|
|
onCurrentPageChanged: {
|
|
contentOpacity = 0.85
|
|
contentFadeRestart.restart()
|
|
if (currentPage !== "search") {
|
|
resetSearchFilter()
|
|
}
|
|
if (currentPage !== "world") {
|
|
selectedWorldCategory = ""
|
|
selectedWorldType = ""
|
|
countrySearchText = ""
|
|
}
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: "Space"
|
|
onActivated: {
|
|
if (backend && backend.player) {
|
|
backend.player.togglePlayPause()
|
|
}
|
|
}
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: "Ctrl+F"
|
|
onActivated: searchField.forceActiveFocus()
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
gradient: Gradient {
|
|
GradientStop { position: 0.0; color: BearTheme.bgA }
|
|
GradientStop { position: 1.0; color: BearTheme.bgB }
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: 12
|
|
spacing: 10
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: headerContent.implicitHeight + 20
|
|
radius: 12
|
|
color: BearTheme.panel
|
|
border.color: BearTheme.cardBorder
|
|
|
|
ColumnLayout {
|
|
id: headerContent
|
|
anchors.fill: parent
|
|
anchors.margins: 10
|
|
spacing: 8
|
|
|
|
HeaderNavigation {
|
|
Layout.fillWidth: true
|
|
app: root
|
|
compactMode: root.compactMode
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 1
|
|
color: BearTheme.cardBorder
|
|
opacity: 0.6
|
|
}
|
|
|
|
SearchToolbar {
|
|
id: searchToolbar
|
|
Layout.fillWidth: true
|
|
app: root
|
|
compactMode: root.compactMode
|
|
searchTimer: searchTimer
|
|
}
|
|
|
|
QuickFilters {
|
|
Layout.fillWidth: true
|
|
app: root
|
|
compactMode: root.compactMode
|
|
}
|
|
|
|
Label {
|
|
Layout.fillWidth: true
|
|
visible: backend && backend.lastError.length > 0
|
|
text: backend ? (qsTr("Error: ") + backend.lastError) : ""
|
|
color: BearTheme.warn
|
|
elide: Text.ElideRight
|
|
font.pixelSize: 12
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
radius: 12
|
|
color: BearTheme.panel
|
|
border.color: BearTheme.cardBorder
|
|
clip: true
|
|
opacity: contentOpacity
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: 180; easing.type: Easing.OutCubic }
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: 10
|
|
spacing: 10
|
|
|
|
WorldCategoryHeader {
|
|
Layout.fillWidth: true
|
|
visible: currentPage === "world" && selectedWorldCategory !== ""
|
|
app: root
|
|
}
|
|
|
|
StationListPanel {
|
|
id: stationPanel
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
app: root
|
|
}
|
|
|
|
WorldCategories {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
visible: currentPage === "world" && selectedWorldCategory === ""
|
|
app: root
|
|
compactMode: root.compactMode
|
|
}
|
|
}
|
|
}
|
|
|
|
PlayerBar {
|
|
Layout.fillWidth: true
|
|
app: root
|
|
}
|
|
}
|
|
|
|
LoadingOverlay {
|
|
app: root
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
if (backend) {
|
|
backend.loadTopStations()
|
|
}
|
|
}
|
|
|
|
AddStationDialog {
|
|
id: addDialog
|
|
app: root
|
|
compactMode: root.compactMode
|
|
}
|
|
|
|
EditStationDialog {
|
|
id: editDialog
|
|
app: root
|
|
compactMode: root.compactMode
|
|
}
|
|
|
|
AboutDialog {
|
|
id: aboutDialog
|
|
compactMode: root.compactMode
|
|
}
|
|
|
|
ToastPopup {
|
|
id: toastPopup
|
|
window: root
|
|
}
|
|
|
|
Timer {
|
|
id: searchTimer
|
|
interval: 600
|
|
repeat: false
|
|
onTriggered: {
|
|
var text = searchField.text.trim()
|
|
if (text.length < 2 || !backend) return
|
|
|
|
if (currentPage === "search" || stationList.count === 0) {
|
|
currentPage = "search"
|
|
backend.searchStations(text)
|
|
}
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
id: contentFadeRestart
|
|
interval: 120
|
|
repeat: false
|
|
onTriggered: contentOpacity = 1.0
|
|
}
|
|
}
|