refactor: relocate live stream controls to ReadoutPanel and update chart axis rendering
This commit is contained in:
@@ -75,6 +75,115 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Live-mode stream stats + stop — moved here from the footer transport bar
|
||||
// so that bar collapses to zero height in live mode, freeing vertical
|
||||
// space for the trend chart.
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
visible: streamController.mode === "live"
|
||||
implicitHeight: visible ? liveStatsCard.implicitHeight + 24 : 0
|
||||
color: Theme.sidePanelBackground
|
||||
radius: Theme.sidePanelRadius
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: liveStatsCard
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
margins: 12
|
||||
}
|
||||
spacing: 8
|
||||
|
||||
Label {
|
||||
text: "LIVE STREAM"
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.letterSpacing: 1.5
|
||||
font.bold: true
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: "Received"
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.bold: true
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Label {
|
||||
text: streamController.receivedCount
|
||||
color: Theme.headingColor
|
||||
font.family: Theme.codeFontFamily
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Theme.cardBorder
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: "Errors"
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontXs
|
||||
font.bold: true
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Label {
|
||||
text: streamController.errorCount
|
||||
color: streamController.errorCount > 0 ? Theme.statusWarningColor : Theme.headingColor
|
||||
font.family: Theme.codeFontFamily
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: stopStreamBtn
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 4
|
||||
hoverEnabled: true
|
||||
text: "STOP"
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontSm
|
||||
font.weight: Font.Medium
|
||||
font.letterSpacing: 1.0
|
||||
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: stopStreamBtn.down ? Theme.transportButtonHover
|
||||
: (stopStreamBtn.hovered ? Theme.transportButtonBg : "transparent")
|
||||
border.width: Theme.borderThin
|
||||
border.color: Theme.sideBorder
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: stopStreamBtn.text
|
||||
color: Theme.headingColor
|
||||
font: stopStreamBtn.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
onClicked: streamController.stopStream()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: readoutCard.implicitHeight + 24
|
||||
|
||||
@@ -200,16 +200,13 @@ ColumnLayout {
|
||||
return streamController.mode === "review" && streamController.loadedFile !== "" && modelData.fileName === streamController.loadedFile;
|
||||
}
|
||||
|
||||
// "live_<serial>_<timestamp>.csv" is SessionController's recording
|
||||
// naming convention (see FileBrowser._refresh_files) — grey these out
|
||||
// unconditionally rather than only while actively being written, since
|
||||
// a stale directory listing can't tell "mid-write" from "just finished".
|
||||
readonly property bool isLiveFile: (modelData.baseName || "").toLowerCase().indexOf("live") >= 0
|
||||
readonly property bool liveFileBlocked: root.selectedTabIndex === 1 && isLiveFile
|
||||
|
||||
visible: matchesFilter
|
||||
height: matchesFilter ? implicitHeight : 0
|
||||
enabled: !isLiveFile
|
||||
opacity: isLiveFile ? 0.45 : 1.0
|
||||
enabled: !liveFileBlocked
|
||||
opacity: liveFileBlocked ? 0.45 : 1.0
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import QtQuick.Layouts
|
||||
import ISC
|
||||
|
||||
// ===== Control Bar =====
|
||||
// Mode-aware footer: cross-fades between Live and Review layouts.
|
||||
// Both variants render as a single centered pill — no full-width bar.
|
||||
// Review-mode transport footer (frame counter, play/pause/step, speed).
|
||||
// Live-mode's Received/Errors/Stop moved to ReadoutPanel's "LIVE STREAM"
|
||||
// card so this bar can collapse to zero height and free vertical space for
|
||||
// the trend chart while streaming.
|
||||
Item {
|
||||
id: bar
|
||||
Layout.fillWidth: true
|
||||
@@ -16,120 +18,13 @@ Item {
|
||||
}
|
||||
clip: true
|
||||
|
||||
// Hide entirely when in review mode with no file loaded
|
||||
readonly property bool hasContent: {
|
||||
if (streamController.mode === "live") return true;
|
||||
return streamController.loadedFile !== "";
|
||||
}
|
||||
readonly property bool isLive: streamController.mode === "live"
|
||||
|
||||
// ── LIVE MODE ─────────────────────────────────────────────────
|
||||
Item {
|
||||
id: liveContent
|
||||
anchors.fill: parent
|
||||
opacity: bar.isLive ? 1.0 : 0.0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Theme.durationBase; easing.type: Theme.easeStandard }
|
||||
}
|
||||
|
||||
// Single centered pill for Received + Errors + Stop
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: liveRow.implicitWidth + 48
|
||||
implicitHeight: 68
|
||||
radius: Theme.radiusMd
|
||||
color: Theme.sidePanelBackground
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
|
||||
Row {
|
||||
id: liveRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 24
|
||||
|
||||
// Received counter
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Received: " + streamController.receivedCount
|
||||
color: Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
// Separator
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 1
|
||||
height: 36
|
||||
color: Theme.sideBorder
|
||||
}
|
||||
|
||||
// Errors counter
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Errors: " + streamController.errorCount
|
||||
color: streamController.errorCount > 0
|
||||
? Theme.statusWarningColor : Theme.sideMutedText
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
// Separator
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 1
|
||||
height: 36
|
||||
color: Theme.sideBorder
|
||||
}
|
||||
|
||||
// STOP button
|
||||
Button {
|
||||
id: stopBtn
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitWidth: 112
|
||||
implicitHeight: 48
|
||||
hoverEnabled: true
|
||||
text: "STOP"
|
||||
font.family: Theme.uiFontFamily
|
||||
font.pixelSize: Theme.fontSm
|
||||
font.weight: Font.Medium
|
||||
font.letterSpacing: 1.0
|
||||
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: stopBtn.pressed ? Theme.transportButtonHover
|
||||
: Theme.transportButtonBg
|
||||
border.color: Theme.sideBorder
|
||||
border.width: 1
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: Theme.headingColor
|
||||
font: parent.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
onClicked: streamController.stopStream()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only shown in review mode, and only once a file is loaded
|
||||
readonly property bool hasContent: streamController.mode !== "live" && streamController.loadedFile !== ""
|
||||
|
||||
// ── REVIEW MODE ──────────────────────────────────────────────
|
||||
Item {
|
||||
id: reviewContent
|
||||
anchors.fill: parent
|
||||
opacity: !bar.isLive ? 1.0 : 0.0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Theme.durationBase; easing.type: Theme.easeStandard }
|
||||
}
|
||||
|
||||
// Single centered pill for frame counter + transport + speed
|
||||
Rectangle {
|
||||
|
||||
Reference in New Issue
Block a user