ui(launcher): hide idle webcam viewport shell

This commit is contained in:
Brad Stein 2026-04-23 19:27:04 -03:00
parent 6d0387ace7
commit 069d17980f
9 changed files with 74 additions and 2 deletions

View File

@ -229,7 +229,7 @@ impl LocalCameraPreview {
}
}
fn camera_preview_placeholder_texture() -> gdk::Texture {
pub(super) fn camera_preview_placeholder_texture() -> gdk::Texture {
let path = format!(
"{}/assets/placeholders/webcam_disabled.png",
env!("CARGO_MANIFEST_DIR")

View File

@ -186,6 +186,18 @@ fn populated_launcher_runtime_widgets_stay_compact() {
view.device_stage.devices_panel.height(),
view.device_stage.preview_panel.height()
);
assert_eq!(
view.device_stage
.camera_preview_stack
.visible_child_name()
.as_deref(),
Some("idle"),
"idle launcher should show the placeholder webcam surface until preview wakes up"
);
view.device_stage
.camera_preview_stack
.set_visible_child_name("live");
present_and_settle(&view.window);
assert!(
view.widgets.display_panes[0].preview_frame.height()
>= view.device_stage.camera_preview_frame.height(),

View File

@ -63,6 +63,7 @@ pub fn build_launcher_view(
audio_check_meter,
devices_panel,
preview_panel,
camera_preview_stack,
camera_preview_frame,
camera_preview,
camera_mirror_button,

View File

@ -141,6 +141,7 @@
device_refresh_button: device_refresh_button.clone(),
swap_key_button: swap_key_button.clone(),
camera_test_button: camera_test_button.clone(),
camera_preview_stack: camera_preview_stack.clone(),
camera_mirror_button: camera_mirror_button.clone(),
camera_mirror_revealer: camera_mirror_revealer.clone(),
microphone_test_button: microphone_test_button.clone(),
@ -173,6 +174,7 @@
device_stage: DeviceStageWidgets {
devices_panel,
preview_panel,
camera_preview_stack,
camera_preview_frame,
camera_preview,
camera_mirror_button,

View File

@ -33,6 +33,7 @@ struct DeviceControlsContext {
audio_check_meter: gtk::ProgressBar,
devices_panel: gtk::Box,
preview_panel: gtk::Box,
camera_preview_stack: gtk::Stack,
camera_preview_frame: gtk::AspectFrame,
camera_preview: gtk::Picture,
camera_mirror_button: gtk::ToggleButton,

View File

@ -219,6 +219,15 @@
);
camera_preview.set_keep_aspect_ratio(true);
camera_preview.add_css_class("camera-preview-frame");
let camera_preview_idle = gtk::Picture::new();
camera_preview_idle.set_can_shrink(true);
camera_preview_idle.set_hexpand(true);
camera_preview_idle.set_vexpand(true);
camera_preview_idle.set_halign(gtk::Align::Fill);
camera_preview_idle.set_valign(gtk::Align::Fill);
camera_preview_idle.set_keep_aspect_ratio(true);
camera_preview_idle.add_css_class("display-placeholder-art");
camera_preview_idle.set_paintable(Some(&super::device_test::camera_preview_placeholder_texture()));
let camera_mirror_button = gtk::ToggleButton::new();
camera_mirror_button.add_css_class("camera-preview-mirror-toggle");
camera_mirror_button.add_css_class("flat");
@ -290,7 +299,35 @@
}
});
camera_preview_overlay.add_controller(hover_controller);
camera_preview_shell.append(&camera_preview_overlay);
let camera_preview_idle_frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false);
camera_preview_idle_frame.set_hexpand(true);
camera_preview_idle_frame.set_vexpand(false);
camera_preview_idle_frame.set_halign(gtk::Align::Fill);
camera_preview_idle_frame.set_valign(gtk::Align::End);
camera_preview_idle_frame.set_size_request(
CAMERA_PREVIEW_VIEWPORT_WIDTH,
CAMERA_PREVIEW_VIEWPORT_HEIGHT,
);
camera_preview_idle_frame.set_child(Some(&camera_preview_idle));
let camera_preview_idle_shell = gtk::Box::new(gtk::Orientation::Vertical, 0);
camera_preview_idle_shell.set_hexpand(true);
camera_preview_idle_shell.set_vexpand(true);
camera_preview_idle_shell.set_halign(gtk::Align::Fill);
camera_preview_idle_shell.set_valign(gtk::Align::Fill);
camera_preview_idle_shell.append(&camera_preview_idle_frame);
let camera_preview_stack = gtk::Stack::new();
camera_preview_stack.set_hexpand(true);
camera_preview_stack.set_vexpand(true);
camera_preview_stack.set_halign(gtk::Align::Fill);
camera_preview_stack.set_valign(gtk::Align::Fill);
camera_preview_stack.set_size_request(
CAMERA_PREVIEW_VIEWPORT_WIDTH,
CAMERA_PREVIEW_VIEWPORT_HEIGHT,
);
camera_preview_stack.add_named(&camera_preview_idle_shell, Some("idle"));
camera_preview_stack.add_named(&camera_preview_overlay, Some("live"));
camera_preview_stack.set_visible_child_name("idle");
camera_preview_shell.append(&camera_preview_stack);
let webcam_group = build_subgroup("Webcam Preview");
webcam_group.set_hexpand(true);
webcam_group.set_vexpand(true);
@ -343,6 +380,7 @@
audio_check_meter,
devices_panel,
preview_panel,
camera_preview_stack,
camera_preview_frame,
camera_preview,
camera_mirror_button,

View File

@ -145,6 +145,7 @@ pub struct LauncherWidgets {
pub device_refresh_button: gtk::Button,
pub swap_key_button: gtk::Button,
pub camera_test_button: gtk::Button,
pub camera_preview_stack: gtk::Stack,
pub camera_mirror_button: gtk::ToggleButton,
pub camera_mirror_revealer: gtk::Revealer,
pub microphone_test_button: gtk::Button,
@ -164,6 +165,7 @@ pub struct LauncherWidgets {
pub struct DeviceStageWidgets {
pub devices_panel: gtk::Box,
pub preview_panel: gtk::Box,
pub camera_preview_stack: gtk::Stack,
pub camera_preview_frame: gtk::AspectFrame,
pub camera_preview: gtk::Picture,
pub camera_mirror_button: gtk::ToggleButton,

View File

@ -211,6 +211,9 @@ pub fn refresh_test_buttons(widgets: &LauncherWidgets, tests: &mut DeviceTestCon
} else {
"Start Preview"
});
widgets
.camera_preview_stack
.set_visible_child_name(if camera_running { "live" } else { "idle" });
let camera_mirrored = tests.camera_preview_mirrored();
if widgets.camera_mirror_button.is_active() != camera_mirrored {
widgets.camera_mirror_button.set_active(camera_mirrored);

View File

@ -170,6 +170,19 @@ fn device_testing_keeps_webcam_and_mic_playback_compact() {
assert!(UI_LAYOUT_SRC.contains("webcam_group.set_valign(gtk::Align::Fill);"));
assert!(UI_LAYOUT_SRC.contains("camera_preview.set_can_shrink(true);"));
assert!(UI_LAYOUT_SRC.contains("camera_preview.set_vexpand(true);"));
assert!(
UI_LAYOUT_SRC.contains("camera_preview_idle.add_css_class(\"display-placeholder-art\");")
);
assert!(
UI_LAYOUT_SRC.contains(
"camera_preview_stack.add_named(&camera_preview_idle_shell, Some(\"idle\"));"
)
);
assert!(
UI_LAYOUT_SRC
.contains("camera_preview_stack.add_named(&camera_preview_overlay, Some(\"live\"));")
);
assert!(UI_LAYOUT_SRC.contains("camera_preview_stack.set_visible_child_name(\"idle\");"));
assert!(UI_LAYOUT_SRC.contains("camera_preview_frame.set_vexpand(false);"));
assert!(UI_LAYOUT_SRC.contains("camera_preview_frame.set_valign(gtk::Align::End);"));
assert!(UI_LAYOUT_SRC.contains("playback_group.set_valign(gtk::Align::Fill);"));