relay_view.rs (7060B)
1 use crate::relay_pool_manager::{RelayPoolManager, RelayStatus}; 2 use egui::{Align, Button, Frame, Layout, Margin, Rgba, RichText, Rounding, Ui, Vec2}; 3 4 use crate::app_style::NotedeckTextStyle; 5 6 pub struct RelayView<'a> { 7 ctx: &'a egui::Context, 8 manager: RelayPoolManager<'a>, 9 } 10 11 impl<'a> RelayView<'a> { 12 pub fn new(ctx: &'a egui::Context, manager: RelayPoolManager<'a>) -> Self { 13 RelayView { ctx, manager } 14 } 15 16 pub fn panel(&'a mut self) { 17 let mut indices_to_remove: Option<Vec<usize>> = None; 18 19 egui::CentralPanel::default().show(self.ctx, |ui| { 20 ui.add_space(24.0); 21 22 ui.horizontal(|ui| { 23 ui.with_layout(Layout::left_to_right(Align::Center), |ui| { 24 ui.label( 25 RichText::new("Relays") 26 .text_style(NotedeckTextStyle::Heading2.text_style()), 27 ); 28 }); 29 30 ui.with_layout(Layout::right_to_left(Align::Center), |ui| { 31 if ui.add(add_relay_button()).clicked() { 32 // TODO: navigate to 'add relay view' 33 }; 34 }); 35 }); 36 37 ui.add_space(8.0); 38 39 egui::ScrollArea::vertical() 40 .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) 41 .auto_shrink([false; 2]) 42 .show(ui, |ui| { 43 indices_to_remove = self.show_relays(ui); 44 }); 45 }); 46 47 if let Some(indices) = indices_to_remove { 48 self.manager.remove_relays(indices); 49 } 50 } 51 52 /// Show the current relays, and returns the indices of relays the user requested to delete 53 fn show_relays(&'a self, ui: &mut Ui) -> Option<Vec<usize>> { 54 let mut indices_to_remove: Option<Vec<usize>> = None; 55 for (index, relay_info) in self.manager.get_relay_infos().iter().enumerate() { 56 ui.add_space(8.0); 57 ui.vertical_centered_justified(|ui| { 58 relay_frame(ui).show(ui, |ui| { 59 ui.horizontal(|ui| { 60 ui.with_layout(Layout::left_to_right(Align::Center), |ui| { 61 Frame::none() 62 // This frame is needed to add margin because the label will be added to the outer frame first and centered vertically before the connection status is added so the vertical centering isn't accurate. 63 // TODO: remove this hack and actually center the url & status at the same time 64 .inner_margin(Margin::symmetric(0.0, 4.0)) 65 .show(ui, |ui| { 66 egui::ScrollArea::horizontal() 67 .id_source(index) 68 .max_width( 69 ui.max_rect().width() 70 - get_right_side_width(relay_info.status), 71 ) // TODO: refactor to dynamically check the size of the 'right to left' portion and set the max width to be the screen width minus padding minus 'right to left' width 72 .show(ui, |ui| { 73 ui.label( 74 RichText::new(relay_info.relay_url) 75 .text_style( 76 NotedeckTextStyle::Monospace.text_style(), 77 ) 78 .color( 79 ui.style() 80 .visuals 81 .noninteractive() 82 .fg_stroke 83 .color, 84 ), 85 ); 86 }); 87 }); 88 }); 89 90 ui.with_layout(Layout::right_to_left(Align::Center), |ui| { 91 if ui.add(delete_button(ui.visuals().dark_mode)).clicked() { 92 indices_to_remove.get_or_insert_with(Vec::new).push(index); 93 }; 94 95 show_connection_status(ui, relay_info.status); 96 }); 97 }); 98 }); 99 }); 100 } 101 102 indices_to_remove 103 } 104 } 105 106 fn get_right_side_width(status: &RelayStatus) -> f32 { 107 match status { 108 RelayStatus::Connected => 150.0, 109 RelayStatus::Connecting => 160.0, 110 RelayStatus::Disconnected => 175.0, 111 } 112 } 113 114 fn add_relay_button() -> egui::Button<'static> { 115 Button::new("+ Add relay").min_size(Vec2::new(0.0, 32.0)) 116 } 117 118 fn delete_button(dark_mode: bool) -> egui::Button<'static> { 119 let img_data = if dark_mode { 120 egui::include_image!("../assets/icons/delete_icon_4x.png") 121 } else { 122 // TODO: use light delete icon 123 egui::include_image!("../assets/icons/delete_icon_4x.png") 124 }; 125 126 egui::Button::image(egui::Image::new(img_data).max_width(10.0)).frame(false) 127 } 128 129 fn relay_frame(ui: &mut Ui) -> Frame { 130 Frame::none() 131 .inner_margin(Margin::same(8.0)) 132 .rounding(ui.style().noninteractive().rounding) 133 .stroke(ui.style().visuals.noninteractive().bg_stroke) 134 } 135 136 fn show_connection_status(ui: &mut Ui, status: &RelayStatus) { 137 let fg_color = match status { 138 RelayStatus::Connected => ui.visuals().selection.bg_fill, 139 RelayStatus::Connecting => ui.visuals().warn_fg_color, 140 RelayStatus::Disconnected => ui.visuals().error_fg_color, 141 }; 142 let bg_color = egui::lerp(Rgba::from(fg_color)..=Rgba::BLACK, 0.8).into(); 143 144 let label_text = match status { 145 RelayStatus::Connected => "Connected", 146 RelayStatus::Connecting => "Connecting...", 147 RelayStatus::Disconnected => "Not Connected", 148 }; 149 150 let frame = Frame::none() 151 .rounding(Rounding::same(100.0)) 152 .fill(bg_color) 153 .inner_margin(Margin::symmetric(12.0, 4.0)); 154 155 frame.show(ui, |ui| { 156 ui.label(RichText::new(label_text).color(fg_color)); 157 ui.add(get_connection_icon(status)); 158 }); 159 } 160 161 fn get_connection_icon(status: &RelayStatus) -> egui::Image<'static> { 162 let img_data = match status { 163 RelayStatus::Connected => egui::include_image!("../assets/icons/connected_icon_4x.png"), 164 RelayStatus::Connecting => egui::include_image!("../assets/icons/connecting_icon_4x.png"), 165 RelayStatus::Disconnected => { 166 egui::include_image!("../assets/icons/disconnected_icon_4x.png") 167 } 168 }; 169 170 egui::Image::new(img_data) 171 }