fix(ui): rebalance launcher preview surfaces

This commit is contained in:
Brad Stein 2026-04-23 17:03:12 -03:00
parent 0325f32d58
commit 7c03c0281b
11 changed files with 145 additions and 72 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -15,7 +15,6 @@ use std::{
}; };
fn present_and_settle(window: &gtk::ApplicationWindow) { fn present_and_settle(window: &gtk::ApplicationWindow) {
window.set_default_size(1280, 780);
window.present(); window.present();
let deadline = Instant::now() + Duration::from_millis(450); let deadline = Instant::now() + Duration::from_millis(450);
while Instant::now() < deadline { while Instant::now() < deadline {
@ -75,11 +74,11 @@ fn launcher_shell_measures_inside_a_1080p_desktop_budget() {
let (min_height, natural_height, _, _) = view.window.measure(gtk::Orientation::Vertical, 1920); let (min_height, natural_height, _, _) = view.window.measure(gtk::Orientation::Vertical, 1920);
assert!( assert!(
min_width <= 1280 && view.window.width() <= 1280, min_width <= 1560 && view.window.width() <= 1560,
"launcher width budget regressed: min={min_width}, natural={natural_width}" "launcher width budget regressed: min={min_width}, natural={natural_width}"
); );
assert!( assert!(
min_height <= 900 && view.window.height() <= 900 && natural_height <= 1080, min_height <= 960 && view.window.height() <= 960,
"launcher height budget regressed: min={min_height}, natural={natural_height}" "launcher height budget regressed: min={min_height}, natural={natural_height}"
); );
} }
@ -107,11 +106,11 @@ fn populated_launcher_shell_measures_inside_a_1080p_desktop_budget() {
let (min_height, natural_height, _, _) = view.window.measure(gtk::Orientation::Vertical, 1920); let (min_height, natural_height, _, _) = view.window.measure(gtk::Orientation::Vertical, 1920);
assert!( assert!(
min_width <= 1280 && view.window.width() <= 1280, min_width <= 1560 && view.window.width() <= 1560,
"populated launcher width budget regressed: min={min_width}, natural={natural_width}" "populated launcher width budget regressed: min={min_width}, natural={natural_width}"
); );
assert!( assert!(
min_height <= 900 && view.window.height() <= 900 && natural_height <= 1080, min_height <= 960 && view.window.height() <= 960,
"populated launcher height budget regressed: min={min_height}, natural={natural_height}" "populated launcher height budget regressed: min={min_height}, natural={natural_height}"
); );
} }
@ -142,38 +141,38 @@ fn populated_launcher_runtime_widgets_stay_compact() {
let (camera_min_h, camera_nat_h, _, _) = view let (camera_min_h, camera_nat_h, _, _) = view
.device_stage .device_stage
.camera_preview .camera_preview
.measure(gtk::Orientation::Vertical, 160); .measure(gtk::Orientation::Vertical, 420);
let (testing_panel_min_h, testing_panel_nat_h, _, _) = view let (testing_panel_min_h, testing_panel_nat_h, _, _) = view
.device_stage .device_stage
.preview_panel .preview_panel
.measure(gtk::Orientation::Vertical, 320); .measure(gtk::Orientation::Vertical, 520);
let (left_min_w, left_nat_w, _, _) = view.widgets.display_panes[0] let (left_min_w, left_nat_w, _, _) = view.widgets.display_panes[0]
.root .root
.measure(gtk::Orientation::Horizontal, -1); .measure(gtk::Orientation::Horizontal, -1);
let (left_min_h, left_nat_h, _, _) = view.widgets.display_panes[0] let (left_min_h, left_nat_h, _, _) = view.widgets.display_panes[0]
.root .root
.measure(gtk::Orientation::Vertical, 640); .measure(gtk::Orientation::Vertical, 720);
let (server_min_w, server_nat_w, _, _) = let (server_min_w, server_nat_w, _, _) =
view.server_entry.measure(gtk::Orientation::Horizontal, -1); view.server_entry.measure(gtk::Orientation::Horizontal, -1);
assert!( assert!(
camera_min_w <= 160 && camera_nat_w <= 160, camera_min_w >= 400 && camera_nat_w >= 400,
"camera preview width regressed: min={camera_min_w}, natural={camera_nat_w}" "camera preview width regressed: min={camera_min_w}, natural={camera_nat_w}"
); );
assert!( assert!(
camera_min_h <= 90 && camera_nat_h <= 90, camera_min_h >= 225 && camera_nat_h >= 225,
"camera preview height regressed: min={camera_min_h}, natural={camera_nat_h}" "camera preview height regressed: min={camera_min_h}, natural={camera_nat_h}"
); );
assert!( assert!(
testing_panel_min_h <= 260 && testing_panel_nat_h <= 260, testing_panel_min_h <= 420 && testing_panel_nat_h <= 420,
"device testing panel height regressed: min={testing_panel_min_h}, natural={testing_panel_nat_h}" "device testing panel height regressed: min={testing_panel_min_h}, natural={testing_panel_nat_h}"
); );
assert!( assert!(
left_min_w <= 445 && left_nat_w <= 470, left_min_w <= 700 && left_nat_w <= 720,
"eye pane width regressed: min={left_min_w}, natural={left_nat_w}" "eye pane width regressed: min={left_min_w}, natural={left_nat_w}"
); );
assert!( assert!(
left_min_h <= 520 && left_nat_h <= 520, left_min_h <= 700 && left_nat_h <= 700,
"eye pane height regressed: min={left_min_h}, natural={left_nat_h}" "eye pane height regressed: min={left_min_h}, natural={left_nat_h}"
); );
assert!( assert!(
@ -194,6 +193,26 @@ fn populated_launcher_runtime_widgets_stay_compact() {
view.widgets.display_panes[0].preview_frame.height(), view.widgets.display_panes[0].preview_frame.height(),
view.device_stage.camera_preview_frame.height() view.device_stage.camera_preview_frame.height()
); );
assert!(
view.widgets.display_panes[0].preview_frame.width()
>= view.device_stage.camera_preview_frame.width(),
"eye preview should stay at least as wide as the webcam preview: eye={}, webcam={}",
view.widgets.display_panes[0].preview_frame.width(),
view.device_stage.camera_preview_frame.width()
);
assert!(
view.widgets.display_panes[0].root.width()
- view.widgets.display_panes[0].preview_frame.width()
<= 60,
"eye preview stopped filling its card width: card={}, preview={}",
view.widgets.display_panes[0].root.width(),
view.widgets.display_panes[0].preview_frame.width()
);
assert!(
view.device_stage.camera_preview_frame.height() >= 225,
"webcam preview should use the taller upstream media space: {}",
view.device_stage.camera_preview_frame.height()
);
} }
#[gtk::test] #[gtk::test]

View File

@ -77,7 +77,7 @@ fn normalize_breakout_limit(width: u32, height: u32) -> (u32, u32) {
fn launcher_default_size(width: u32, height: u32) -> (i32, i32) { fn launcher_default_size(width: u32, height: u32) -> (i32, i32) {
let max_width = width.saturating_sub(72).max(640) as i32; let max_width = width.saturating_sub(72).max(640) as i32;
let max_height = height.saturating_sub(120).max(520) as i32; let max_height = height.saturating_sub(120).max(520) as i32;
(1280.min(max_width), 780.min(max_height)) (1540.min(max_width), 880.min(max_height))
} }
#[cfg(not(coverage))] #[cfg(not(coverage))]

View File

@ -5,8 +5,9 @@
let (devices_panel, devices_body) = let (devices_panel, devices_body) =
build_panel_with_action("Device Staging", Some(device_refresh_button.upcast_ref())); build_panel_with_action("Device Staging", Some(device_refresh_button.upcast_ref()));
devices_panel.set_hexpand(true); devices_panel.set_hexpand(true);
devices_panel.set_vexpand(false); devices_panel.set_vexpand(true);
devices_panel.set_valign(gtk::Align::Fill); devices_panel.set_valign(gtk::Align::Fill);
devices_body.set_vexpand(true);
devices_body.set_spacing(8); devices_body.set_spacing(8);
let control_group = build_subgroup("Control Inputs"); let control_group = build_subgroup("Control Inputs");
@ -68,6 +69,8 @@
devices_body.append(&control_group); devices_body.append(&control_group);
let media_group = build_subgroup("Media Controls"); let media_group = build_subgroup("Media Controls");
media_group.set_vexpand(true);
media_group.set_valign(gtk::Align::Fill);
let media_grid = gtk::Grid::new(); let media_grid = gtk::Grid::new();
media_grid.set_row_spacing(10); media_grid.set_row_spacing(10);
media_grid.set_column_spacing(8); media_grid.set_column_spacing(8);
@ -185,21 +188,22 @@
staging_row.append(&devices_panel); staging_row.append(&devices_panel);
let (preview_panel, preview_body) = build_panel("Upstream Media"); let (preview_panel, preview_body) = build_panel("Upstream Media");
preview_panel.set_hexpand(false); preview_panel.set_hexpand(true);
preview_panel.set_vexpand(false); preview_panel.set_vexpand(true);
preview_panel.set_valign(gtk::Align::Fill); preview_panel.set_valign(gtk::Align::Fill);
preview_body.set_vexpand(false); preview_body.set_hexpand(true);
preview_body.set_vexpand(true);
preview_body.set_spacing(8); preview_body.set_spacing(8);
let testing_row = gtk::Box::new(gtk::Orientation::Horizontal, 8); let testing_row = gtk::Box::new(gtk::Orientation::Horizontal, 8);
testing_row.set_hexpand(true); testing_row.set_hexpand(true);
testing_row.set_vexpand(false); testing_row.set_vexpand(true);
testing_row.set_valign(gtk::Align::Start); testing_row.set_valign(gtk::Align::Fill);
let camera_preview = gtk::Picture::new(); let camera_preview = gtk::Picture::new();
camera_preview.set_can_shrink(true); camera_preview.set_can_shrink(true);
camera_preview.set_hexpand(false); camera_preview.set_hexpand(true);
camera_preview.set_vexpand(false); camera_preview.set_vexpand(true);
camera_preview.set_halign(gtk::Align::Fill); camera_preview.set_halign(gtk::Align::Fill);
camera_preview.set_valign(gtk::Align::Start); camera_preview.set_valign(gtk::Align::Fill);
camera_preview.set_size_request( camera_preview.set_size_request(
CAMERA_PREVIEW_VIEWPORT_WIDTH, CAMERA_PREVIEW_VIEWPORT_WIDTH,
CAMERA_PREVIEW_VIEWPORT_HEIGHT, CAMERA_PREVIEW_VIEWPORT_HEIGHT,
@ -213,19 +217,19 @@
camera_status.set_xalign(0.0); camera_status.set_xalign(0.0);
camera_status.set_visible(false); camera_status.set_visible(false);
let camera_preview_shell = gtk::Box::new(gtk::Orientation::Vertical, 0); let camera_preview_shell = gtk::Box::new(gtk::Orientation::Vertical, 0);
camera_preview_shell.set_hexpand(false); camera_preview_shell.set_hexpand(true);
camera_preview_shell.set_vexpand(false); camera_preview_shell.set_vexpand(true);
camera_preview_shell.set_halign(gtk::Align::Fill); camera_preview_shell.set_halign(gtk::Align::Fill);
camera_preview_shell.set_valign(gtk::Align::Start); camera_preview_shell.set_valign(gtk::Align::Fill);
camera_preview_shell.set_size_request( camera_preview_shell.set_size_request(
CAMERA_PREVIEW_VIEWPORT_WIDTH, CAMERA_PREVIEW_VIEWPORT_WIDTH,
CAMERA_PREVIEW_VIEWPORT_HEIGHT, CAMERA_PREVIEW_VIEWPORT_HEIGHT,
); );
let camera_preview_frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false); let camera_preview_frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false);
camera_preview_frame.set_hexpand(false); camera_preview_frame.set_hexpand(true);
camera_preview_frame.set_vexpand(false); camera_preview_frame.set_vexpand(false);
camera_preview_frame.set_halign(gtk::Align::Fill); camera_preview_frame.set_halign(gtk::Align::Fill);
camera_preview_frame.set_valign(gtk::Align::Start); camera_preview_frame.set_valign(gtk::Align::End);
camera_preview_frame.set_size_request( camera_preview_frame.set_size_request(
CAMERA_PREVIEW_VIEWPORT_WIDTH, CAMERA_PREVIEW_VIEWPORT_WIDTH,
CAMERA_PREVIEW_VIEWPORT_HEIGHT, CAMERA_PREVIEW_VIEWPORT_HEIGHT,
@ -233,27 +237,27 @@
camera_preview_frame.set_child(Some(&camera_preview)); camera_preview_frame.set_child(Some(&camera_preview));
camera_preview_shell.append(&camera_preview_frame); camera_preview_shell.append(&camera_preview_frame);
let webcam_group = build_subgroup("Webcam Preview"); let webcam_group = build_subgroup("Webcam Preview");
webcam_group.set_hexpand(false); webcam_group.set_hexpand(true);
webcam_group.set_vexpand(false); webcam_group.set_vexpand(true);
webcam_group.set_valign(gtk::Align::Start); webcam_group.set_valign(gtk::Align::Fill);
webcam_group.append(&camera_preview_shell); webcam_group.append(&camera_preview_shell);
testing_row.append(&webcam_group); testing_row.append(&webcam_group);
let playback_group = build_subgroup("Mic Playback"); let playback_group = build_subgroup("Mic Playback");
playback_group.set_hexpand(false); playback_group.set_hexpand(false);
playback_group.set_vexpand(false); playback_group.set_vexpand(true);
playback_group.set_valign(gtk::Align::Fill); playback_group.set_valign(gtk::Align::Fill);
playback_group.set_size_request(72, -1); playback_group.set_size_request(72, -1);
let playback_body = gtk::Box::new(gtk::Orientation::Vertical, 6); let playback_body = gtk::Box::new(gtk::Orientation::Vertical, 6);
playback_body.set_halign(gtk::Align::Center); playback_body.set_halign(gtk::Align::Center);
playback_body.set_vexpand(false); playback_body.set_vexpand(true);
playback_body.set_valign(gtk::Align::Fill); playback_body.set_valign(gtk::Align::Fill);
let microphone_replay_button = gtk::Button::with_label("Replay"); let microphone_replay_button = gtk::Button::with_label("Replay");
stabilize_button(&microphone_replay_button, 70); stabilize_button(&microphone_replay_button, 70);
audio_check_meter.set_orientation(gtk::Orientation::Vertical); audio_check_meter.set_orientation(gtk::Orientation::Vertical);
audio_check_meter.set_inverted(true); audio_check_meter.set_inverted(true);
audio_check_meter.set_hexpand(false); audio_check_meter.set_hexpand(false);
audio_check_meter.set_vexpand(false); audio_check_meter.set_vexpand(true);
audio_check_meter.set_halign(gtk::Align::Center); audio_check_meter.set_halign(gtk::Align::Center);
audio_check_meter.set_size_request(20, 0); audio_check_meter.set_size_request(20, 0);
audio_check_meter.set_show_text(false); audio_check_meter.set_show_text(false);

View File

@ -19,19 +19,21 @@ fn build_display_pane(title: &str, capture_path: &str) -> DisplayPaneWidgets {
root.append(&header); root.append(&header);
let picture = gtk::Picture::new(); let picture = gtk::Picture::new();
picture.add_css_class("eye-preview-surface");
picture.set_hexpand(true); picture.set_hexpand(true);
picture.set_vexpand(false); picture.set_vexpand(true);
picture.set_halign(gtk::Align::Fill); picture.set_halign(gtk::Align::Fill);
picture.set_valign(gtk::Align::Start); picture.set_valign(gtk::Align::Fill);
picture.set_can_shrink(true); picture.set_can_shrink(true);
picture.set_keep_aspect_ratio(true); picture.set_keep_aspect_ratio(true);
picture.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); picture.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
let preview_frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false); let preview_frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false);
preview_frame.set_hexpand(false); preview_frame.add_css_class("eye-preview-surface");
preview_frame.set_vexpand(false); preview_frame.set_hexpand(true);
preview_frame.set_halign(gtk::Align::Center); preview_frame.set_vexpand(true);
preview_frame.set_valign(gtk::Align::Center); preview_frame.set_halign(gtk::Align::Fill);
preview_frame.set_valign(gtk::Align::Fill);
preview_frame.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); preview_frame.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
preview_frame.set_child(Some(&picture)); preview_frame.set_child(Some(&picture));
@ -47,10 +49,11 @@ fn build_display_pane(title: &str, capture_path: &str) -> DisplayPaneWidgets {
preview_placeholder.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); preview_placeholder.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
let preview_overlay = gtk::Overlay::new(); let preview_overlay = gtk::Overlay::new();
preview_overlay.add_css_class("eye-preview-surface");
preview_overlay.set_hexpand(true); preview_overlay.set_hexpand(true);
preview_overlay.set_vexpand(false); preview_overlay.set_vexpand(true);
preview_overlay.set_halign(gtk::Align::Fill); preview_overlay.set_halign(gtk::Align::Fill);
preview_overlay.set_valign(gtk::Align::Start); preview_overlay.set_valign(gtk::Align::Fill);
preview_overlay.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); preview_overlay.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
preview_overlay.set_child(Some(&preview_frame)); preview_overlay.set_child(Some(&preview_frame));
preview_overlay.add_overlay(&preview_placeholder); preview_overlay.add_overlay(&preview_placeholder);
@ -58,9 +61,9 @@ fn build_display_pane(title: &str, capture_path: &str) -> DisplayPaneWidgets {
let preview_box = gtk::Box::new(gtk::Orientation::Vertical, 0); let preview_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
preview_box.set_hexpand(true); preview_box.set_hexpand(true);
preview_box.set_vexpand(false); preview_box.set_vexpand(true);
preview_box.set_halign(gtk::Align::Fill); preview_box.set_halign(gtk::Align::Fill);
preview_box.set_valign(gtk::Align::Start); preview_box.set_valign(gtk::Align::Fill);
preview_box.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); preview_box.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
preview_box.append(&preview_overlay); preview_box.append(&preview_overlay);
@ -75,13 +78,31 @@ fn build_display_pane(title: &str, capture_path: &str) -> DisplayPaneWidgets {
window_placeholder.set_sensitive(false); window_placeholder.set_sensitive(false);
window_placeholder.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); window_placeholder.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
let placeholder_overlay = gtk::Overlay::new();
placeholder_overlay.add_css_class("eye-preview-surface");
placeholder_overlay.set_hexpand(true);
placeholder_overlay.set_vexpand(true);
placeholder_overlay.set_halign(gtk::Align::Fill);
placeholder_overlay.set_valign(gtk::Align::Fill);
placeholder_overlay.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
let placeholder_frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false);
placeholder_frame.add_css_class("eye-preview-surface");
placeholder_frame.set_hexpand(true);
placeholder_frame.set_vexpand(true);
placeholder_frame.set_halign(gtk::Align::Fill);
placeholder_frame.set_valign(gtk::Align::Fill);
placeholder_frame.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
placeholder_overlay.set_child(Some(&placeholder_frame));
placeholder_overlay.add_overlay(&window_placeholder);
placeholder_overlay.set_clip_overlay(&window_placeholder, true);
let placeholder_box = gtk::Box::new(gtk::Orientation::Vertical, 0); let placeholder_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
placeholder_box.add_css_class("display-placeholder");
placeholder_box.set_hexpand(true); placeholder_box.set_hexpand(true);
placeholder_box.set_vexpand(false); placeholder_box.set_vexpand(true);
placeholder_box.set_valign(gtk::Align::Start); placeholder_box.set_halign(gtk::Align::Fill);
placeholder_box.set_valign(gtk::Align::Fill);
placeholder_box.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); placeholder_box.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT);
placeholder_box.append(&window_placeholder); placeholder_box.append(&placeholder_overlay);
preview_placeholder.set_visible(picture.paintable().is_none()); preview_placeholder.set_visible(picture.paintable().is_none());
{ {
@ -93,7 +114,7 @@ fn build_display_pane(title: &str, capture_path: &str) -> DisplayPaneWidgets {
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
stack.set_hexpand(true); stack.set_hexpand(true);
stack.set_vexpand(false); stack.set_vexpand(true);
stack.add_named(&preview_box, Some("preview")); stack.add_named(&preview_box, Some("preview"));
stack.add_named(&placeholder_box, Some("placeholder")); stack.add_named(&placeholder_box, Some("placeholder"));
stack.set_visible_child_name("preview"); stack.set_visible_child_name("preview");

View File

@ -85,8 +85,16 @@ pub fn install_css(window: &gtk::ApplicationWindow) {
padding: 12px; padding: 12px;
} }
picture.display-placeholder-art { picture.display-placeholder-art {
background: transparent;
background-color: transparent;
opacity: 0.78; opacity: 0.78;
} }
overlay.eye-preview-surface,
aspectframe.eye-preview-surface,
picture.eye-preview-surface {
background: transparent;
background-color: transparent;
}
picture.camera-preview-frame { picture.camera-preview-frame {
background: rgba(0, 0, 0, 0.28); background: rgba(0, 0, 0, 0.28);
border: 1px solid rgba(255, 255, 255, 0.10); border: 1px solid rgba(255, 255, 255, 0.10);

View File

@ -186,11 +186,11 @@ pub struct LauncherView {
pub const LESAVKA_ICON_NAME: &str = "dev.lesavka.launcher"; pub const LESAVKA_ICON_NAME: &str = "dev.lesavka.launcher";
const LESAVKA_ICON_SEARCH_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons"); const LESAVKA_ICON_SEARCH_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons");
const LAUNCHER_DEFAULT_WIDTH: i32 = 1280; const LAUNCHER_DEFAULT_WIDTH: i32 = 1540;
const LAUNCHER_DEFAULT_HEIGHT: i32 = 780; const LAUNCHER_DEFAULT_HEIGHT: i32 = 880;
const OPERATIONS_RAIL_WIDTH: i32 = 288; const OPERATIONS_RAIL_WIDTH: i32 = 288;
const CAMERA_PREVIEW_VIEWPORT_HEIGHT: i32 = 90; const CAMERA_PREVIEW_VIEWPORT_HEIGHT: i32 = 225;
const CAMERA_PREVIEW_VIEWPORT_WIDTH: i32 = 160; const CAMERA_PREVIEW_VIEWPORT_WIDTH: i32 = 400;
const EYE_PREVIEW_MIN_HEIGHT: i32 = 203; const EYE_PREVIEW_MIN_HEIGHT: i32 = 315;
const EYE_PREVIEW_MIN_WIDTH: i32 = 360; const EYE_PREVIEW_MIN_WIDTH: i32 = 560;
const SIDE_LOG_MIN_HEIGHT: i32 = 124; const SIDE_LOG_MIN_HEIGHT: i32 = 124;

View File

@ -42,11 +42,11 @@ fn source_index(needle: &str) -> usize {
#[test] #[test]
fn launcher_default_size_stays_inside_1080p() { fn launcher_default_size_stays_inside_1080p() {
assert_eq!(const_i32("LAUNCHER_DEFAULT_WIDTH"), 1280); assert_eq!(const_i32("LAUNCHER_DEFAULT_WIDTH"), 1540);
assert_eq!(const_i32("LAUNCHER_DEFAULT_HEIGHT"), 780); assert_eq!(const_i32("LAUNCHER_DEFAULT_HEIGHT"), 880);
assert!(const_i32("LAUNCHER_DEFAULT_WIDTH") <= 1920); assert!(const_i32("LAUNCHER_DEFAULT_WIDTH") <= 1920);
assert!( assert!(
const_i32("LAUNCHER_DEFAULT_HEIGHT") <= 900, const_i32("LAUNCHER_DEFAULT_HEIGHT") <= 960,
"leave room for desktop panels and window chrome on a 1080p monitor" "leave room for desktop panels and window chrome on a 1080p monitor"
); );
assert!( assert!(
@ -57,7 +57,7 @@ fn launcher_default_size_stays_inside_1080p() {
assert!(UI_LAYOUT_SRC.contains("let max_width = width.saturating_sub(72).max(640) as i32;")); assert!(UI_LAYOUT_SRC.contains("let max_width = width.saturating_sub(72).max(640) as i32;"));
assert!(UI_LAYOUT_SRC.contains("let max_height = height.saturating_sub(120).max(520) as i32;")); assert!(UI_LAYOUT_SRC.contains("let max_height = height.saturating_sub(120).max(520) as i32;"));
assert!( assert!(
UI_LAYOUT_SRC.contains("(1280.min(max_width), 780.min(max_height))"), UI_LAYOUT_SRC.contains("(1540.min(max_width), 880.min(max_height))"),
"the activation path must use the same compact startup budget as the shell" "the activation path must use the same compact startup budget as the shell"
); );
assert!(UI_LAYOUT_SRC.contains("schedule_launcher_window_guard(app, &window, launcher_size);")); assert!(UI_LAYOUT_SRC.contains("schedule_launcher_window_guard(app, &window, launcher_size);"));
@ -68,14 +68,19 @@ fn launcher_default_size_stays_inside_1080p() {
#[test] #[test]
fn eye_panes_keep_the_docked_preview_footprint_without_forcing_maximized_width() { fn eye_panes_keep_the_docked_preview_footprint_without_forcing_maximized_width() {
assert_eq!(const_i32("EYE_PREVIEW_MIN_WIDTH"), 360); assert_eq!(const_i32("EYE_PREVIEW_MIN_WIDTH"), 560);
assert_eq!(const_i32("EYE_PREVIEW_MIN_HEIGHT"), 203); assert_eq!(const_i32("EYE_PREVIEW_MIN_HEIGHT"), 315);
assert!(UI_LAYOUT_SRC.contains("display_row.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("display_row.set_vexpand(false);"));
assert!(UI_LAYOUT_SRC.contains("display_row.set_valign(gtk::Align::Start);")); assert!(UI_LAYOUT_SRC.contains("display_row.set_valign(gtk::Align::Start);"));
assert!( assert!(
!UI_LAYOUT_SRC.contains("display_row.set_vexpand(true);"), !UI_LAYOUT_SRC.contains("display_row.set_vexpand(true);"),
"a vertically expanding 16:9 eye row turns extra height into horizontal overflow" "a vertically expanding 16:9 eye row turns extra height into horizontal overflow"
); );
assert!(UI_LAYOUT_SRC.contains("preview_frame.set_hexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("preview_frame.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("preview_frame.set_halign(gtk::Align::Fill);"));
assert!(UI_LAYOUT_SRC.contains("preview_box.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("stack.set_vexpand(true);"));
assert!( assert!(
UI_LAYOUT_SRC.contains("caption_label.set_halign(gtk::Align::End)") UI_LAYOUT_SRC.contains("caption_label.set_halign(gtk::Align::End)")
|| UI_LAYOUT_SRC.contains("capture_label.set_halign(gtk::Align::End)") || UI_LAYOUT_SRC.contains("capture_label.set_halign(gtk::Align::End)")
@ -122,7 +127,7 @@ fn device_staging_and_testing_stay_independent_so_preview_does_not_fill_dead_hei
assert!(UI_LAYOUT_SRC.contains("staging_row.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("staging_row.set_vexpand(false);"));
assert!(UI_LAYOUT_SRC.contains("devices_panel.set_valign(gtk::Align::Fill);")); assert!(UI_LAYOUT_SRC.contains("devices_panel.set_valign(gtk::Align::Fill);"));
assert!(UI_LAYOUT_SRC.contains("preview_panel.set_valign(gtk::Align::Fill);")); assert!(UI_LAYOUT_SRC.contains("preview_panel.set_valign(gtk::Align::Fill);"));
assert!(UI_LAYOUT_SRC.contains("preview_panel.set_hexpand(false);")); assert!(UI_LAYOUT_SRC.contains("preview_panel.set_hexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("build_panel(\"Upstream Media\")")); assert!(UI_LAYOUT_SRC.contains("build_panel(\"Upstream Media\")"));
assert!( assert!(
!UI_LAYOUT_SRC.contains("gtk::SizeGroup::new(gtk::SizeGroupMode::Vertical)"), !UI_LAYOUT_SRC.contains("gtk::SizeGroup::new(gtk::SizeGroupMode::Vertical)"),
@ -137,25 +142,41 @@ fn eye_placeholders_use_overlay_art_without_reflowing_the_shell() {
UI_LAYOUT_SRC.contains("preview_placeholder.set_visible(picture.paintable().is_none());") UI_LAYOUT_SRC.contains("preview_placeholder.set_visible(picture.paintable().is_none());")
); );
assert!(UI_LAYOUT_SRC.contains("stack.add_named(&placeholder_box, Some(\"placeholder\"));")); assert!(UI_LAYOUT_SRC.contains("stack.add_named(&placeholder_box, Some(\"placeholder\"));"));
assert!(UI_LAYOUT_SRC.contains("placeholder_overlay.add_overlay(&window_placeholder);"));
assert!(
!UI_LAYOUT_SRC.contains("placeholder_box.add_css_class(\"display-placeholder\");"),
"open-eye placeholder should sit on the clear eye surface rather than an extra box"
);
assert!(UI_LAYOUT_SRC.contains("env!(\"CARGO_MANIFEST_DIR\")")); assert!(UI_LAYOUT_SRC.contains("env!(\"CARGO_MANIFEST_DIR\")"));
assert!(UI_LAYOUT_SRC.contains("/assets/placeholders/eye_{}_{}.png")); assert!(UI_LAYOUT_SRC.contains("/assets/placeholders/eye_{}_{}.png"));
assert!(UI_LAYOUT_SRC.contains("picture.add_css_class(\"eye-preview-surface\");"));
assert!(UI_LAYOUT_SRC.contains("preview_frame.add_css_class(\"eye-preview-surface\");"));
assert!(UI_LAYOUT_SRC.contains("placeholder_frame.add_css_class(\"eye-preview-surface\");"));
assert!(UI_LAYOUT_SRC.contains("overlay.eye-preview-surface"));
} }
#[test] #[test]
fn device_testing_keeps_webcam_and_mic_playback_compact() { fn device_testing_keeps_webcam_and_mic_playback_compact() {
assert_eq!(const_i32("CAMERA_PREVIEW_VIEWPORT_WIDTH"), 160); assert_eq!(const_i32("CAMERA_PREVIEW_VIEWPORT_WIDTH"), 400);
assert_eq!(const_i32("CAMERA_PREVIEW_VIEWPORT_HEIGHT"), 90); assert_eq!(const_i32("CAMERA_PREVIEW_VIEWPORT_HEIGHT"), 225);
assert!(UI_LAYOUT_SRC.contains("testing_row.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("devices_panel.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("testing_row.set_valign(gtk::Align::Start);")); assert!(UI_LAYOUT_SRC.contains("devices_body.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("webcam_group.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("media_group.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("webcam_group.set_valign(gtk::Align::Start);")); assert!(UI_LAYOUT_SRC.contains("testing_row.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("testing_row.set_valign(gtk::Align::Fill);"));
assert!(UI_LAYOUT_SRC.contains("preview_panel.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("preview_body.set_hexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("webcam_group.set_vexpand(true);"));
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_can_shrink(true);"));
assert!(UI_LAYOUT_SRC.contains("camera_preview.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("camera_preview.set_vexpand(true);"));
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);")); assert!(UI_LAYOUT_SRC.contains("playback_group.set_valign(gtk::Align::Fill);"));
assert!(UI_LAYOUT_SRC.contains("playback_group.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("playback_group.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("preview_body.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("preview_body.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("playback_body.set_valign(gtk::Align::Fill);")); assert!(UI_LAYOUT_SRC.contains("playback_body.set_valign(gtk::Align::Fill);"));
assert!(UI_LAYOUT_SRC.contains("audio_check_meter.set_vexpand(false);")); assert!(UI_LAYOUT_SRC.contains("audio_check_meter.set_vexpand(true);"));
assert!(UI_LAYOUT_SRC.contains("playback_body.append(&audio_check_meter);")); assert!(UI_LAYOUT_SRC.contains("playback_body.append(&audio_check_meter);"));
assert!(UI_LAYOUT_SRC.contains("playback_body.append(&microphone_replay_button);")); assert!(UI_LAYOUT_SRC.contains("playback_body.append(&microphone_replay_button);"));
} }