fn build_display_pane(title: &str, capture_path: &str) -> DisplayPaneWidgets { let root = gtk::Box::new(gtk::Orientation::Vertical, 10); root.add_css_class("display-card"); root.set_hexpand(true); root.set_vexpand(true); let header = gtk::Box::new(gtk::Orientation::Horizontal, 8); header.set_hexpand(true); let title_label = gtk::Label::new(Some(title)); title_label.add_css_class("title-4"); title_label.set_halign(gtk::Align::Start); title_label.set_hexpand(true); let capture_label = gtk::Label::new(Some(capture_path)); capture_label.add_css_class("dim-label"); capture_label.set_halign(gtk::Align::End); capture_label.set_ellipsize(pango::EllipsizeMode::Start); header.append(&title_label); header.append(&capture_label); root.append(&header); let picture = gtk::Picture::new(); picture.set_hexpand(true); picture.set_vexpand(true); picture.set_halign(gtk::Align::Fill); picture.set_valign(gtk::Align::Fill); picture.set_can_shrink(true); picture.set_keep_aspect_ratio(true); picture.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); let preview_box = gtk::Box::new(gtk::Orientation::Vertical, 0); preview_box.set_hexpand(true); preview_box.set_vexpand(true); preview_box.set_halign(gtk::Align::Fill); preview_box.set_valign(gtk::Align::Fill); preview_box.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); preview_frame.set_hexpand(false); preview_frame.set_vexpand(false); preview_frame.set_halign(gtk::Align::Center); preview_frame.set_valign(gtk::Align::Center); preview_frame.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); preview_frame.set_child(Some(&picture)); preview_box.append(&preview_frame); let placeholder = gtk::Label::new(Some( "This feed is running in its own window.\nUse Return To Preview to dock it back here.", )); placeholder.set_wrap(true); placeholder.set_justify(gtk::Justification::Center); placeholder.set_halign(gtk::Align::Center); placeholder.set_valign(gtk::Align::Center); let placeholder_box = gtk::Box::new(gtk::Orientation::Vertical, 6); placeholder_box.add_css_class("display-placeholder"); placeholder_box.set_hexpand(true); placeholder_box.set_vexpand(true); placeholder_box.set_size_request(EYE_PREVIEW_MIN_WIDTH, EYE_PREVIEW_MIN_HEIGHT); placeholder_box.append(&placeholder); let stack = gtk::Stack::new(); stack.set_hexpand(true); stack.set_vexpand(true); stack.add_named(&preview_box, Some("preview")); stack.add_named(&placeholder_box, Some("placeholder")); stack.set_visible_child_name("preview"); root.append(&stack); let feed_source_combo = gtk::ComboBoxText::new(); feed_source_combo.add_css_class("compact-combo"); feed_source_combo.set_tooltip_text(Some("Eye source for this pane.")); feed_source_combo.set_hexpand(true); feed_source_combo.set_size_request(0, -1); let capture_resolution_combo = gtk::ComboBoxText::new(); capture_resolution_combo.add_css_class("compact-combo"); capture_resolution_combo.set_tooltip_text(Some("Eye capture mode.")); capture_resolution_combo.set_size_request(0, -1); capture_resolution_combo.set_hexpand(true); let breakout_combo = gtk::ComboBoxText::new(); breakout_combo.add_css_class("compact-combo"); breakout_combo.set_tooltip_text(Some("Breakout window size.")); breakout_combo.set_size_request(0, -1); breakout_combo.set_hexpand(true); let action_button = gtk::Button::with_label("Break Out"); stabilize_button(&action_button, 104); action_button.set_halign(gtk::Align::End); let stream_status = gtk::Label::new(Some("Preview pending")); stream_status.add_css_class("status-line"); stream_status.add_css_class("eye-inline-status"); stream_status.set_halign(gtk::Align::Fill); stream_status.set_valign(gtk::Align::Center); stream_status.set_hexpand(true); stream_status.set_ellipsize(pango::EllipsizeMode::End); stream_status.set_single_line_mode(true); stream_status.set_width_chars(12); stream_status.set_max_width_chars(18); stream_status.set_tooltip_text(Some("Eye stream status.")); let footer_shell = gtk::Box::new(gtk::Orientation::Vertical, 6); let controls_grid = gtk::Grid::new(); controls_grid.set_column_spacing(8); controls_grid.set_row_spacing(8); controls_grid.set_hexpand(true); let feed_row = build_inline_combo_row("Feed", &feed_source_combo, 7); let capture_row = build_inline_combo_row("Capture", &capture_resolution_combo, 7); let breakout_row = build_inline_combo_row("Display", &breakout_combo, 7); feed_row.set_hexpand(true); capture_row.set_hexpand(true); breakout_row.set_hexpand(true); controls_grid.attach(&feed_row, 0, 0, 1, 1); controls_grid.attach(&capture_row, 1, 0, 2, 1); controls_grid.attach(&breakout_row, 0, 1, 1, 1); controls_grid.attach(&stream_status, 1, 1, 1, 1); controls_grid.attach(&action_button, 2, 1, 1, 1); footer_shell.append(&controls_grid); root.append(&footer_shell); DisplayPaneWidgets { root, stack, preview_frame, picture, stream_status, placeholder, feed_source_combo, capture_resolution_combo, breakout_combo, action_button, preview_binding: Rc::new(RefCell::new(None)), title: title.to_string(), } }