host_picker.rs (5273B)
1 use crate::ui::keybind_hint::paint_keybind_hint; 2 use egui::{RichText, Vec2}; 3 4 /// Actions from the host picker overlay. 5 #[derive(Debug, Clone)] 6 pub enum HostPickerAction { 7 /// User picked a host. `None` = local, `Some(hostname)` = remote. 8 HostSelected(Option<String>), 9 /// User cancelled. 10 Cancelled, 11 } 12 13 /// Render the host picker as a full-panel overlay. 14 /// 15 /// `known_hosts` should contain all remote hostnames (not the local one). 16 /// The local machine is always shown as the first option. 17 pub fn host_picker_overlay_ui( 18 ui: &mut egui::Ui, 19 local_hostname: &str, 20 known_hosts: &[String], 21 has_sessions: bool, 22 ) -> Option<HostPickerAction> { 23 let mut action = None; 24 let is_narrow = notedeck::ui::is_narrow(ui.ctx()); 25 let ctrl_held = ui.input(|i| i.modifiers.ctrl); 26 27 // Keyboard shortcuts: Ctrl+1 = local, Ctrl+2..9 = remote hosts 28 if ctrl_held { 29 if ui.input(|i| i.key_pressed(egui::Key::Num1)) { 30 return Some(HostPickerAction::HostSelected(None)); 31 } 32 for (idx, host) in known_hosts.iter().take(8).enumerate() { 33 let key = match idx { 34 0 => egui::Key::Num2, 35 1 => egui::Key::Num3, 36 2 => egui::Key::Num4, 37 3 => egui::Key::Num5, 38 4 => egui::Key::Num6, 39 5 => egui::Key::Num7, 40 6 => egui::Key::Num8, 41 7 => egui::Key::Num9, 42 _ => continue, 43 }; 44 if ui.input(|i| i.key_pressed(key)) { 45 return Some(HostPickerAction::HostSelected(Some(host.clone()))); 46 } 47 } 48 } 49 50 egui::Frame::new() 51 .fill(ui.visuals().panel_fill) 52 .inner_margin(egui::Margin::symmetric(if is_narrow { 16 } else { 40 }, 20)) 53 .show(ui, |ui| { 54 // Header 55 ui.horizontal(|ui| { 56 if has_sessions { 57 if ui.button("< Back").clicked() { 58 action = Some(HostPickerAction::Cancelled); 59 } 60 ui.add_space(16.0); 61 } 62 ui.heading("Select Host"); 63 }); 64 65 ui.add_space(16.0); 66 67 let max_content_width = if is_narrow { 68 ui.available_width() 69 } else { 70 500.0 71 }; 72 let available_height = ui.available_height(); 73 74 ui.allocate_ui_with_layout( 75 egui::vec2(max_content_width, available_height), 76 egui::Layout::top_down(egui::Align::LEFT), 77 |ui| { 78 let button_height = if is_narrow { 44.0 } else { 32.0 }; 79 let hint_width = if ctrl_held { 24.0 } else { 0.0 }; 80 let button_width = ui.available_width() - hint_width - 4.0; 81 82 // Local option 83 ui.horizontal(|ui| { 84 let button = egui::Button::new( 85 RichText::new(format!("{} (local)", local_hostname)).monospace(), 86 ) 87 .min_size(Vec2::new(button_width, button_height)) 88 .fill(ui.visuals().widgets.inactive.weak_bg_fill); 89 90 let response = ui.add(button); 91 92 if ctrl_held { 93 let hint_center = response.rect.right_center() 94 + egui::vec2(hint_width / 2.0 + 2.0, 0.0); 95 paint_keybind_hint(ui, hint_center, "1", 18.0); 96 } 97 98 if response.clicked() { 99 action = Some(HostPickerAction::HostSelected(None)); 100 } 101 }); 102 103 ui.add_space(4.0); 104 105 // Remote hosts 106 for (idx, host) in known_hosts.iter().enumerate() { 107 ui.horizontal(|ui| { 108 let button = 109 egui::Button::new(RichText::new(host.as_str()).monospace()) 110 .min_size(Vec2::new(button_width, button_height)) 111 .fill(ui.visuals().widgets.inactive.weak_bg_fill); 112 113 let response = ui.add(button); 114 115 if ctrl_held && idx < 8 { 116 let hint_text = format!("{}", idx + 2); 117 let hint_center = response.rect.right_center() 118 + egui::vec2(hint_width / 2.0 + 2.0, 0.0); 119 paint_keybind_hint(ui, hint_center, &hint_text, 18.0); 120 } 121 122 if response.clicked() { 123 action = Some(HostPickerAction::HostSelected(Some(host.clone()))); 124 } 125 }); 126 127 ui.add_space(4.0); 128 } 129 }, 130 ); 131 }); 132 133 // Escape to cancel 134 if has_sessions 135 && ui 136 .ctx() 137 .input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Escape)) 138 { 139 action = Some(HostPickerAction::Cancelled); 140 } 141 142 action 143 }