lesavka/client/src/launcher/ui_runtime/display_popouts.rs

274 lines
8.9 KiB
Rust

pub fn open_popout_window(
app: &gtk::Application,
preview: &LauncherPreview,
state: &Rc<RefCell<LauncherState>>,
child_proc: &Rc<RefCell<Option<RelayChild>>>,
popouts: &Rc<RefCell<[Option<PopoutWindowHandle>; 2]>>,
widgets: &LauncherWidgets,
monitor_id: usize,
) {
let already_open = {
let popouts = popouts.borrow();
popouts[monitor_id].is_some()
};
if already_open {
return;
}
if let Some(binding) = widgets.display_panes[monitor_id]
.preview_binding
.borrow()
.as_ref()
{
binding.set_enabled(false);
}
let (breakout_size, breakout_limit) = {
let state = state.borrow();
(
state.breakout_size_choice(monitor_id),
state.breakout_display_size(),
)
};
let title = format!("Lesavka {}", widgets.display_panes[monitor_id].title);
let window = gtk::ApplicationWindow::builder()
.application(app)
.title(&title)
.default_width(breakout_size.width)
.default_height(breakout_size.height)
.build();
super::ui_components::install_css(&window);
super::ui_components::install_window_icon(&window);
super::ui_components::install_window_chrome(&window, &title);
window.set_resizable(false);
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(breakout_size.width, breakout_size.height);
let root = gtk::Box::new(gtk::Orientation::Vertical, 0);
root.set_hexpand(true);
root.set_vexpand(true);
root.set_halign(gtk::Align::Fill);
root.set_valign(gtk::Align::Fill);
root.set_size_request(breakout_size.width, breakout_size.height);
let frame = gtk::AspectFrame::new(0.5, 0.5, 16.0 / 9.0, false);
frame.set_hexpand(true);
frame.set_vexpand(true);
frame.set_halign(gtk::Align::Fill);
frame.set_valign(gtk::Align::Fill);
frame.set_size_request(breakout_size.width, breakout_size.height);
frame.set_child(Some(&picture));
root.append(&frame);
let stream_status = gtk::Label::new(Some(""));
let binding = preview
.install_on_picture(monitor_id, PreviewSurface::Window, &picture, &stream_status)
.expect("preview binding for popout");
window.set_child(Some(&root));
install_popout_drag(&window, &picture);
apply_popout_window_geometry(&window, &root, &picture, breakout_size, breakout_limit);
let state_handle = Rc::clone(state);
let child_proc_handle = Rc::clone(child_proc);
let popouts_handle = Rc::clone(popouts);
let widgets_handle = widgets.clone();
let close_binding = binding.clone();
window.connect_close_request(move |_| {
let handle = {
let mut popouts = popouts_handle.borrow_mut();
popouts[monitor_id].take()
};
if let Some(handle) = handle {
handle.binding.close();
if let Some(preview_binding) = widgets_handle.display_panes[monitor_id]
.preview_binding
.borrow()
.as_ref()
{
preview_binding.set_enabled(true);
}
{
let mut state = state_handle.borrow_mut();
state.set_display_surface(monitor_id, DisplaySurface::Preview);
}
let child_running = child_proc_handle.borrow().is_some();
let state_snapshot = state_handle.borrow().clone();
refresh_launcher_ui(&widgets_handle, &state_snapshot, child_running);
} else {
close_binding.close();
}
glib::Propagation::Proceed
});
{
let mut state = state.borrow_mut();
state.set_display_surface(monitor_id, DisplaySurface::Window);
}
{
let mut popouts = popouts.borrow_mut();
popouts[monitor_id] = Some(PopoutWindowHandle {
window: window.clone(),
root: root.clone(),
frame: frame.clone(),
picture: picture.clone(),
status_label: stream_status.clone(),
binding,
});
}
let child_running = child_proc.borrow().is_some();
let state_snapshot = state.borrow().clone();
refresh_launcher_ui(widgets, &state_snapshot, child_running);
window.present();
schedule_popout_window_geometry(
window.clone(),
root.clone(),
picture.clone(),
breakout_size,
breakout_limit,
);
}
pub fn apply_popout_window_size(
handle: &PopoutWindowHandle,
size: BreakoutSizeChoice,
display_limit: super::state::PreviewSourceSize,
) {
handle.frame.set_size_request(size.width, size.height);
apply_popout_window_geometry(
&handle.window,
&handle.root,
&handle.picture,
size,
display_limit,
);
handle.window.present();
schedule_popout_window_geometry(
handle.window.clone(),
handle.root.clone(),
handle.picture.clone(),
size,
display_limit,
);
}
pub fn dock_display_to_preview(
state: &Rc<RefCell<LauncherState>>,
child_proc: &Rc<RefCell<Option<RelayChild>>>,
popouts: &Rc<RefCell<[Option<PopoutWindowHandle>; 2]>>,
widgets: &LauncherWidgets,
monitor_id: usize,
) {
let handle = {
let mut popouts = popouts.borrow_mut();
popouts[monitor_id].take()
};
if let Some(handle) = handle {
handle.binding.close();
handle.window.close();
}
if let Some(binding) = widgets.display_panes[monitor_id]
.preview_binding
.borrow()
.as_ref()
{
binding.set_enabled(true);
}
{
let mut state = state.borrow_mut();
state.set_display_surface(monitor_id, DisplaySurface::Preview);
}
let child_running = child_proc.borrow().is_some();
if !child_running {
let pane = &widgets.display_panes[monitor_id];
pane.picture.set_paintable(Option::<&gdk::Paintable>::None);
pane.preview_placeholder.set_visible(true);
pane.stream_status.set_text("Connect relay to preview.");
}
let state_snapshot = state.borrow().clone();
refresh_launcher_ui(widgets, &state_snapshot, child_running);
}
pub fn dock_all_displays_to_preview(
state: &Rc<RefCell<LauncherState>>,
child_proc: &Rc<RefCell<Option<RelayChild>>>,
popouts: &Rc<RefCell<[Option<PopoutWindowHandle>; 2]>>,
widgets: &LauncherWidgets,
) {
let mut handles = Vec::new();
{
let mut popouts = popouts.borrow_mut();
for monitor_id in 0..2 {
if let Some(handle) = popouts[monitor_id].take() {
handles.push(handle);
}
}
}
for handle in handles {
handle.binding.close();
handle.window.close();
}
for monitor_id in 0..2 {
if let Some(binding) = widgets.display_panes[monitor_id]
.preview_binding
.borrow()
.as_ref()
{
binding.set_enabled(true);
}
}
{
let mut state = state.borrow_mut();
for monitor_id in 0..2 {
state.set_display_surface(monitor_id, DisplaySurface::Preview);
}
}
let child_running = child_proc.borrow().is_some();
if !child_running {
for pane in &widgets.display_panes {
pane.picture.set_paintable(Option::<&gdk::Paintable>::None);
pane.preview_placeholder.set_visible(true);
pane.stream_status.set_text("Connect relay to preview.");
}
}
let state_snapshot = state.borrow().clone();
refresh_launcher_ui(widgets, &state_snapshot, child_running);
}
pub fn refresh_display_pane(pane: &DisplayPaneWidgets, surface: DisplaySurface) {
if let Some(binding) = pane.preview_binding.borrow().as_ref() {
binding.set_enabled(matches!(surface, DisplaySurface::Preview));
}
let preview_ready = pane.preview_binding.borrow().is_some();
pane.action_button.set_sensitive(preview_ready);
pane.clip_button.set_sensitive(preview_ready);
pane.record_button.set_sensitive(preview_ready);
pane.save_button.set_sensitive(true);
match surface {
DisplaySurface::Preview => {
pane.stack.set_visible_child_name("preview");
pane.action_button.set_label("Break Out");
pane.preview_placeholder
.set_visible(pane.picture.paintable().is_none());
if pane.preview_binding.borrow().is_none() {
pane.stream_status.set_text("Preview unavailable");
}
}
DisplaySurface::Window => {
pane.stack.set_visible_child_name("placeholder");
pane.action_button.set_label("Return");
pane.stream_status.set_text("Streaming in its own window");
pane.preview_placeholder.set_visible(false);
}
}
}