cabana: playback controls for live streaming (#38802)

* Hide Cabana playback controls during live streaming

* cabana: retain pause and go-live controls for live streams
This commit is contained in:
Trey Moen
2026-09-07 14:47:20 -07:00
committed by GitHub
parent 20ae4ac44d
commit 3e54af9db8
2 changed files with 18 additions and 15 deletions
+5 -3
View File
@@ -903,8 +903,8 @@ void MainWindow::drawVideoPanel() {
video_h = avail.y - splitter_h - charts_min_h;
}
}
// The splitter provides the gap; extra ItemSpacing would leave an undraggable strip.
if (!charts_floating_) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f));
// Replay uses a splitter for the gap; live streams use normal item spacing.
if (!charts_floating_ && !live) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f));
if (video_h > 0.0f) {
ImGui::BeginChild("video", ImVec2(0, video_h), ImGuiChildFlags_Borders);
help_overlay_.add(video_widget_->whatsThis(), ImGui::GetCurrentWindow()->Rect());
@@ -913,7 +913,7 @@ void MainWindow::drawVideoPanel() {
} else {
video_widget_->setVisible(false); // the splitter collapsed the video: stop the vipc thread
}
if (!charts_floating_) {
if (!charts_floating_ && !live) {
ImGui::InvisibleButton("##splitter", ImVec2(-1.0f, splitter_h));
const bool splitter_hovered = ImGui::IsItemHovered() && !live, splitter_active = ImGui::IsItemActive() && !live;
if (splitter_active) {
@@ -927,6 +927,8 @@ void MainWindow::drawVideoPanel() {
ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(splitter.Min.x, line_y), ImVec2(splitter.Max.x, line_y + 2.0f),
ImGui::GetColorU32(splitter_active ? ImGuiCol_SeparatorActive : splitter_hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Border));
ImGui::PopStyleVar();
}
if (!charts_floating_) {
if (!charts_collapsed) {
// the chart list scrolls in its own child, the container itself never scrolls
ImGui::BeginChild("charts", ImVec2(0, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
@@ -141,7 +141,8 @@ std::string VideoWidget::whatsThis() const {
static float toolbarHeight() { return TOOLBAR_MARGIN_Y + ImGui::GetFrameHeight(); }
void VideoWidget::drawPlaybackController() {
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + TOOLBAR_MARGIN_Y);
if (!can->liveStreaming())
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + TOOLBAR_MARGIN_Y);
const float speed_width = menuButtonWidth("0.05x", true);
const char *play_icon = can->isPaused() ? icon::PLAY : icon::PAUSE;
@@ -151,13 +152,15 @@ void VideoWidget::drawPlaybackController() {
: formatTime(can->currentSec(), true);
const char *time_tooltip = settings.absolute_time ? "Elapsed time" : "Absolute time";
std::vector<ToolbarItem> items = {
toolbarAction("rewind", icon::REWIND, "Seek backward", []() { can->seekTo(can->currentSec() - 1); }),
toolbarAction("play", play_icon, play_tooltip, []() { can->pause(!can->isPaused()); }, true, true),
toolbarAction("fast-forward", icon::FAST_FORWARD, "Seek forward", []() { can->seekTo(can->currentSec() + 1); }, true, true),
};
std::vector<ToolbarItem> items;
if (!can->liveStreaming()) {
items.push_back(toolbarAction("rewind", icon::REWIND, "Seek backward", []() { can->seekTo(can->currentSec() - 1); }));
}
items.push_back(toolbarAction("play", play_icon, play_tooltip, []() { can->pause(!can->isPaused()); }, true, true));
if (can->liveStreaming()) {
items.push_back(toolbarAction("skip-end", icon::SKIP_END, "Skip to the end", [this]() { skipToEnd(); }, skip_to_end_enabled_, true));
items.push_back(toolbarAction("skip-end", icon::SKIP_END, "Go live", [this]() { skipToEnd(); }, skip_to_end_enabled_, true));
} else {
items.push_back(toolbarAction("fast-forward", icon::FAST_FORWARD, "Seek forward", []() { can->seekTo(can->currentSec() + 1); }, true, true));
}
if (slider_ || msgs_received_) {
// a mono font: with proportional digits the time changed width as it ticked and the items after it moved
@@ -190,13 +193,11 @@ void VideoWidget::drawPlaybackController() {
return item;
};
const char *aspect_ratio_icon = settings.crop_video ? icon::ASPECT_RATIO_FILL : icon::ASPECT_RATIO;
items.push_back(toolbarAction("crop_video", aspect_ratio_icon, "Crop to fill", [this]() { cropVideoClicked(); }));
if (!can->liveStreaming()) {
items.push_back(toolbarAction("crop_video", aspect_ratio_icon, "Crop to fill", [this]() { cropVideoClicked(); }));
items.push_back(separator());
items.push_back(toolbarAction("loop", loop_icon, "Loop playback", [this]() { loopPlaybackClicked(); }, true, true));
}
items.push_back(toolbarMenu("speed_btn", speed_text_, "Speed", [this]() { drawSpeedMenuItems(); }, true, true, speed_width));
if (!can->liveStreaming()) {
items.push_back(toolbarMenu("speed_btn", speed_text_, "Speed", [this]() { drawSpeedMenuItems(); }, true, true, speed_width));
items.push_back(separator());
items.push_back(toolbarAction("route_info", icon::INFO_CIRCLE, "View route details", [this]() { showRouteInfo(); }, true, true));
}
@@ -356,7 +357,7 @@ float VideoWidget::sizeHintHeight() const {
// Keep the pane's default proportions stable as frames arrive or cameras change.
float VideoWidget::defaultHeight(float width) const {
if (!cam_widget_) return toolbarHeight(); // live streams have no camera or slider
if (!cam_widget_) return ImGui::GetFrameHeight(); // live streams have no camera or slider
const float cam_height = std::max((float)MIN_VIDEO_HEIGHT, width / DEFAULT_CAMERA_ASPECT_RATIO);
const float tab_height = camera_tab_->count() >= 2 ? ImGui::GetFrameHeight() : 0.0f;
return cam_height + tab_height + SLIDER_HEIGHT + toolbarHeight();