widgets.rs (5056B)
1 use crate::anim::{AnimationHelper, ICON_EXPANSION_MULTIPLE}; 2 use crate::icons::search_icon; 3 use egui::{emath::GuiRounding, Align, Color32, CornerRadius, Pos2, RichText, Stroke, TextEdit}; 4 use notedeck::NotedeckTextStyle; 5 6 pub fn x_button(rect: egui::Rect) -> impl egui::Widget { 7 move |ui: &mut egui::Ui| -> egui::Response { 8 let max_width = rect.width(); 9 let helper = AnimationHelper::new_from_rect(ui, "user_search_close", rect); 10 11 let fill_color = ui.visuals().text_color(); 12 13 let radius = max_width / (2.0 * ICON_EXPANSION_MULTIPLE); 14 15 let painter = ui.painter(); 16 let ppp = ui.ctx().pixels_per_point(); 17 let nw_edge = helper 18 .scale_pos_from_center(Pos2::new(-radius, radius)) 19 .round_to_pixel_center(ppp); 20 let se_edge = helper 21 .scale_pos_from_center(Pos2::new(radius, -radius)) 22 .round_to_pixel_center(ppp); 23 let sw_edge = helper 24 .scale_pos_from_center(Pos2::new(-radius, -radius)) 25 .round_to_pixel_center(ppp); 26 let ne_edge = helper 27 .scale_pos_from_center(Pos2::new(radius, radius)) 28 .round_to_pixel_center(ppp); 29 30 let line_width = helper.scale_1d_pos(2.0); 31 32 painter.line_segment([nw_edge, se_edge], Stroke::new(line_width, fill_color)); 33 painter.line_segment([ne_edge, sw_edge], Stroke::new(line_width, fill_color)); 34 35 helper.take_animation_response() 36 } 37 } 38 39 /// Button styled in the Notedeck theme 40 pub fn styled_button_toggleable( 41 text: &str, 42 fill_color: egui::Color32, 43 enabled: bool, 44 ) -> impl egui::Widget + '_ { 45 move |ui: &mut egui::Ui| -> egui::Response { 46 let painter = ui.painter(); 47 let text_color = if ui.visuals().dark_mode { 48 egui::Color32::WHITE 49 } else { 50 egui::Color32::BLACK 51 }; 52 53 let galley = painter.layout( 54 text.to_owned(), 55 NotedeckTextStyle::Button.get_font_id(ui.ctx()), 56 text_color, 57 ui.available_width(), 58 ); 59 60 let size = galley.rect.expand2(egui::vec2(16.0, 8.0)).size(); 61 let mut button = egui::Button::new(galley).corner_radius(8.0); 62 63 if !enabled { 64 button = button 65 .sense(egui::Sense::focusable_noninteractive()) 66 .fill(ui.visuals().noninteractive().bg_fill) 67 .stroke(ui.visuals().noninteractive().bg_stroke); 68 } else { 69 button = button.fill(fill_color); 70 } 71 72 let mut resp = ui.add_sized(size, button); 73 74 if !enabled { 75 resp = resp.on_hover_cursor(egui::CursorIcon::NotAllowed); 76 } 77 78 resp 79 } 80 } 81 82 /// Get appropriate background color for active side panel icon button 83 pub fn side_panel_active_bg(ui: &egui::Ui) -> egui::Color32 { 84 if ui.visuals().dark_mode { 85 egui::Color32::from_rgb(70, 70, 70) 86 } else { 87 egui::Color32::from_rgb(220, 220, 220) 88 } 89 } 90 91 /// Get appropriate tint color for side panel icons to ensure visibility 92 pub fn side_panel_icon_tint(ui: &egui::Ui) -> egui::Color32 { 93 if ui.visuals().dark_mode { 94 egui::Color32::WHITE 95 } else { 96 egui::Color32::BLACK 97 } 98 } 99 100 /// Returns a styled Frame for search input boxes with rounded corners. 101 pub fn search_input_frame(dark_mode: bool) -> egui::Frame { 102 egui::Frame { 103 inner_margin: egui::Margin::symmetric(8, 0), 104 outer_margin: egui::Margin::ZERO, 105 corner_radius: CornerRadius::same(18), 106 shadow: Default::default(), 107 fill: if dark_mode { 108 Color32::from_rgb(30, 30, 30) 109 } else { 110 Color32::from_rgb(240, 240, 240) 111 }, 112 stroke: if dark_mode { 113 Stroke::new(1.0, Color32::from_rgb(60, 60, 60)) 114 } else { 115 Stroke::new(1.0, Color32::from_rgb(200, 200, 200)) 116 }, 117 } 118 } 119 120 /// The standard height for search input boxes. 121 pub const SEARCH_INPUT_HEIGHT: f32 = 34.0; 122 123 /// A styled search input box with rounded corners and search icon. 124 pub fn search_input_box<'a>(query: &'a mut String, hint_text: &'a str) -> impl egui::Widget + 'a { 125 move |ui: &mut egui::Ui| -> egui::Response { 126 ui.horizontal(|ui| { 127 search_input_frame(ui.visuals().dark_mode) 128 .show(ui, |ui| { 129 ui.with_layout(egui::Layout::left_to_right(Align::Center), |ui| { 130 ui.spacing_mut().item_spacing = egui::vec2(8.0, 0.0); 131 132 ui.add(search_icon(16.0, SEARCH_INPUT_HEIGHT)); 133 134 ui.add_sized( 135 [ui.available_width(), SEARCH_INPUT_HEIGHT], 136 TextEdit::singleline(query) 137 .hint_text(RichText::new(hint_text).weak()) 138 .margin(egui::vec2(0.0, 8.0)) 139 .frame(false), 140 ) 141 }) 142 .inner 143 }) 144 .inner 145 }) 146 .response 147 } 148 }