1use std::cell::RefCell;
8use std::rc::Rc;
9
10use repose_core::prelude::*;
11use repose_core::{Brush, PointerEvent, Rect, Scene, SceneNode, View};
12
13use crate::Text;
14use crate::textfield::{caret_xy_for_byte, index_for_xy_bytes};
15
16pub fn SelectableText(
17 text: impl Into<String>,
18 font_size_dp: f32,
19 on_selection_change: impl Fn(Option<(usize, usize)>) + 'static,
20) -> View {
21 let text: String = text.into();
22 let text_for_handlers = text.clone();
23 let text_for_paint = text.clone();
24
25 let selection: Rc<RefCell<Option<(usize, usize)>>> = remember_with_key("sel:range", || {
26 RefCell::new(None)
27 });
28 let anchor: Rc<RefCell<usize>> = remember_with_key("sel:anchor", || RefCell::new(0));
29 let dragging: Rc<RefCell<bool>> = remember_with_key("sel:dragging", || RefCell::new(false));
30 let last_rect: Rc<RefCell<Rect>> =
31 remember_with_key("sel:last_rect", || RefCell::new(Rect::default()));
32
33 let callback = Rc::new(on_selection_change);
34
35 let cb_down = callback.clone();
36 let on_down = {
37 let text = text_for_handlers.clone();
38 let selection = selection.clone();
39 let anchor = anchor.clone();
40 let dragging = dragging.clone();
41 let last_rect = last_rect.clone();
42 move |ev: PointerEvent| {
43 let r = *last_rect.borrow();
44 if r.w <= 0.0 || r.h <= 0.0 {
45 return;
46 }
47 let font_px = dp_to_px(font_size_dp) * text_scale().0;
48 let lx = (ev.position.x - r.x).max(0.0);
49 let ly = (ev.position.y - r.y).max(0.0);
50 let wrap_w = r.w.max(1.0);
51 let byte = index_for_xy_bytes(&text, font_px, wrap_w, lx, ly);
52 *anchor.borrow_mut() = byte;
53 *selection.borrow_mut() = Some((byte, byte));
54 *dragging.borrow_mut() = true;
55 cb_down(Some((byte, byte)));
56 }
57 };
58
59 let cb_move = callback.clone();
60 let on_move = {
61 let text = text_for_handlers.clone();
62 let selection = selection.clone();
63 let anchor = anchor.clone();
64 let dragging = dragging.clone();
65 let last_rect = last_rect.clone();
66 move |ev: PointerEvent| {
67 if !*dragging.borrow() {
68 return;
69 }
70 let r = *last_rect.borrow();
71 if r.w <= 0.0 || r.h <= 0.0 {
72 return;
73 }
74 let font_px = dp_to_px(font_size_dp) * text_scale().0;
75 let lx = (ev.position.x - r.x).max(0.0);
76 let ly = (ev.position.y - r.y).max(0.0);
77 let wrap_w = r.w.max(1.0);
78 let byte = index_for_xy_bytes(&text, font_px, wrap_w, lx, ly);
79 let a = *anchor.borrow();
80 *selection.borrow_mut() = Some((a, byte));
81 cb_move(Some((a, byte)));
82 }
83 };
84
85 let cb_up = callback.clone();
86 let on_up = {
87 let selection = selection.clone();
88 let dragging = dragging.clone();
89 move |_ev: PointerEvent| {
90 *dragging.borrow_mut() = false;
91 cb_up(*selection.borrow());
92 }
93 };
94
95 let painter = {
96 let text = text_for_paint.clone();
97 let selection = selection.clone();
98 let last_rect = last_rect.clone();
99 move |scene: &mut Scene, rect: Rect| {
100 *last_rect.borrow_mut() = rect;
101
102 let (s, e) = match *selection.borrow() {
103 Some((a, b)) if a != b => {
104 if a < b { (a, b) } else { (b, a) }
105 }
106 _ => return,
107 };
108 if e == 0 || e <= s {
109 return;
110 }
111
112 let font_px = dp_to_px(font_size_dp) * text_scale().0;
113 let wrap_w = rect.w.max(1.0);
114 let (sx, sy, sli) = caret_xy_for_byte(&text, font_px, wrap_w, s);
115 let (ex, ey, eli) = caret_xy_for_byte(&text, font_px, wrap_w, e);
116 let th = theme();
117 let brush = Brush::Solid(th.primary.with_alpha(96));
118 let line_h = font_px * 1.2;
119
120 if sli == eli {
121 let x = sx.min(ex);
122 let w = (ex - sx).abs().max(2.0);
123 scene.nodes.push(SceneNode::Rect {
124 rect: Rect { x: rect.x + x, y: rect.y + sy, w, h: line_h },
125 brush,
126 radius: 0.0,
127 });
128 } else {
129 scene.nodes.push(SceneNode::Rect {
130 rect: Rect {
131 x: rect.x + sx,
132 y: rect.y + sy,
133 w: (rect.w - sx).max(2.0),
134 h: line_h,
135 },
136 brush: brush.clone(),
137 radius: 0.0,
138 });
139 if eli > sli + 1 {
140 scene.nodes.push(SceneNode::Rect {
141 rect: Rect {
142 x: rect.x,
143 y: rect.y + (sli as f32 + 1.0) * line_h,
144 w: rect.w,
145 h: (eli as f32 - sli as f32 - 1.0) * line_h,
146 },
147 brush: brush.clone(),
148 radius: 0.0,
149 });
150 }
151 scene.nodes.push(SceneNode::Rect {
152 rect: Rect {
153 x: rect.x,
154 y: rect.y + ey,
155 w: ex.max(2.0),
156 h: line_h,
157 },
158 brush,
159 radius: 0.0,
160 });
161 }
162 }
163 };
164
165 let mut v = Text(text_for_paint);
166 v.modifier = v
167 .modifier
168 .on_pointer_down(on_down)
169 .on_pointer_move(on_move)
170 .on_pointer_up(on_up)
171 .painter(painter);
172 v
173}