notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

repost.rs (6997B)


      1 use std::f32::consts::PI;
      2 
      3 use egui::{
      4     epaint::PathShape, pos2, vec2, CornerRadius, Layout, Margin, RichText, Sense, Shape, Stroke,
      5 };
      6 use egui_extras::StripBuilder;
      7 use enostr::NoteId;
      8 use notedeck::{fonts::get_font_size, NotedeckTextStyle};
      9 use notedeck_ui::{app_images, galley_centered_pos};
     10 
     11 use crate::repost::RepostAction;
     12 
     13 pub struct RepostDecisionView<'a> {
     14     noteid: &'a NoteId,
     15 }
     16 
     17 impl<'a> RepostDecisionView<'a> {
     18     pub fn new(noteid: &'a NoteId) -> Self {
     19         Self { noteid }
     20     }
     21 
     22     pub fn show(&self, ui: &mut egui::Ui) -> Option<RepostAction> {
     23         let mut action = None;
     24         egui::Frame::new()
     25             .inner_margin(Margin::symmetric(48, 24))
     26             .show(ui, |ui| {
     27                 StripBuilder::new(ui)
     28                     .sizes(egui_extras::Size::exact(48.0), 2)
     29                     .size(egui_extras::Size::exact(80.0))
     30                     .vertical(|mut strip| {
     31                         strip.cell(|ui| {
     32                             if ui
     33                                 .with_layout(Layout::left_to_right(egui::Align::Center), |ui| {
     34                                     let r = ui.add(
     35                                         app_images::repost_image(ui.visuals().dark_mode)
     36                                             .max_height(24.0)
     37                                             .sense(Sense::click()),
     38                                     );
     39                                     r.union(ui.add(repost_item_text("Repost")))
     40                                 })
     41                                 .inner
     42                                 .on_hover_cursor(egui::CursorIcon::PointingHand)
     43                                 .clicked()
     44                             {
     45                                 action = Some(RepostAction::Kind06Repost(*self.noteid))
     46                             }
     47                         });
     48 
     49                         strip.cell(|ui| {
     50                             if ui
     51                                 .with_layout(Layout::left_to_right(egui::Align::Center), |ui| {
     52                                     let r = ui
     53                                         .add(quote_icon())
     54                                         .on_hover_cursor(egui::CursorIcon::PointingHand);
     55                                     r.union(ui.add(repost_item_text("Quote")))
     56                                 })
     57                                 .inner
     58                                 .on_hover_cursor(egui::CursorIcon::PointingHand)
     59                                 .clicked()
     60                             {
     61                                 action = Some(RepostAction::Quote(*self.noteid))
     62                             }
     63                         });
     64 
     65                         strip.cell(|ui| {
     66                             ui.add_space(16.0);
     67                             let resp = ui.allocate_response(
     68                                 vec2(ui.available_width(), 48.0),
     69                                 Sense::click(),
     70                             );
     71 
     72                             let color = if resp.hovered() {
     73                                 ui.visuals().widgets.hovered.bg_fill
     74                             } else {
     75                                 ui.visuals().text_color()
     76                             };
     77 
     78                             let painter = ui.painter_at(resp.rect);
     79                             ui.painter().rect_stroke(
     80                                 resp.rect,
     81                                 CornerRadius::same(32),
     82                                 egui::Stroke::new(1.5, color),
     83                                 egui::StrokeKind::Inside,
     84                             );
     85 
     86                             let galley = painter.layout_no_wrap(
     87                                 "Cancel".to_owned(),
     88                                 NotedeckTextStyle::Heading3.get_font_id(ui.ctx()),
     89                                 ui.visuals().text_color(),
     90                             );
     91 
     92                             painter.galley(
     93                                 galley_centered_pos(&galley, resp.rect.center()),
     94                                 galley,
     95                                 ui.visuals().text_color(),
     96                             );
     97 
     98                             if resp.clicked() {
     99                                 action = Some(RepostAction::Cancel);
    100                             }
    101                         });
    102                     });
    103             });
    104 
    105         action
    106     }
    107 }
    108 
    109 fn repost_item_text(text: &str) -> impl egui::Widget + use<'_> {
    110     move |ui: &mut egui::Ui| -> egui::Response {
    111         ui.add(egui::Label::new(
    112             RichText::new(text).size(get_font_size(ui.ctx(), &NotedeckTextStyle::Heading3)),
    113         ))
    114         .on_hover_cursor(egui::CursorIcon::PointingHand)
    115     }
    116 }
    117 
    118 pub fn quote_icon() -> impl egui::Widget {
    119     move |ui: &mut egui::Ui| -> egui::Response {
    120         let h = 32.0; // scaling constant
    121         let color = ui.visuals().strong_text_color();
    122         let r = h * 0.12; // dot radius
    123         let arc_r = r * 2.0; // larger so it protrudes above
    124         let sw = h * 0.06; // stroke width
    125         let stroke = Stroke::new(sw, color);
    126 
    127         // Same horizontal layout as before (in "local" coords using height scale)
    128         let cx1_raw = h * 0.34;
    129         let cx2_raw = h * 0.72;
    130         let gap = cx2_raw - cx1_raw;
    131 
    132         // Bounds including stroke (only the left side needs +sw/2 for arcs)
    133         let left_bound = (cx1_raw - r) - sw * 0.5;
    134         let right_bound = (cx2_raw + r).max(cx2_raw + (arc_r - r)); // safe if arc_r > 2r
    135         let width = right_bound - left_bound;
    136 
    137         // Vertical bounds: arc top needs sw/2; total content height = arc_r + r + sw/2
    138         let content_h = arc_r + r + sw * 0.5;
    139         let v_margin = (h - content_h) * 0.5;
    140 
    141         let resp = ui.allocate_response(vec2(width, h), egui::Sense::click());
    142         let rect = resp.rect;
    143         let origin = rect.min;
    144 
    145         // Place centers with bounds aligned to rect
    146         let dx = origin.x - left_bound;
    147         let cy = origin.y + v_margin + arc_r + sw * 0.5;
    148 
    149         let c1 = pos2(dx + cx1_raw, cy);
    150         let c2 = pos2(dx + cx1_raw + gap, cy);
    151 
    152         // arc centers (unchanged)
    153         let a1 = pos2(c1.x + (arc_r - r) + 0.8, c1.y);
    154         let a2 = pos2(c2.x + (arc_r - r) + 0.8, c2.y);
    155 
    156         // Draw arcs FIRST (upper-left quadrant [π, 3π/2])
    157         let arc = |ac: egui::Pos2| {
    158             let steps = 16;
    159             (0..=steps)
    160                 .map(|i| {
    161                     let t = i as f32 / steps as f32;
    162                     let ang = PI + t * (PI * 0.5);
    163                     pos2(ac.x + arc_r * ang.cos(), ac.y + arc_r * ang.sin())
    164                 })
    165                 .collect::<Vec<_>>()
    166         };
    167         let painter = ui.painter_at(rect);
    168         painter.add(Shape::Path(PathShape::line(arc(a1), stroke)));
    169         painter.add(Shape::Path(PathShape::line(arc(a2), stroke)));
    170 
    171         // Dots LAST to hide the junction
    172         painter.circle_filled(c1, r, color);
    173         painter.circle_filled(c2, r, color);
    174 
    175         resp
    176     }
    177 }