cranpose_ui/widgets/
clickable_text.rs1#![allow(non_snake_case)]
7
8use crate::modifier::Modifier;
9use crate::text::{AnnotatedString, TextOverflow, TextStyle};
10use crate::widgets::BasicText;
11use cranpose_core::NodeId;
12use std::rc::Rc;
13
14#[doc(hidden)]
15pub trait IntoSharedAnnotatedString {
16 fn into_shared(self) -> Rc<AnnotatedString>;
17}
18
19impl IntoSharedAnnotatedString for AnnotatedString {
20 fn into_shared(self) -> Rc<AnnotatedString> {
21 Rc::new(self)
22 }
23}
24
25impl IntoSharedAnnotatedString for Rc<AnnotatedString> {
26 fn into_shared(self) -> Rc<AnnotatedString> {
27 self
28 }
29}
30
31#[allow(clippy::needless_pass_by_value)]
61pub fn ClickableText<T>(
62 text: T,
63 modifier: Modifier,
64 style: TextStyle,
65 on_click: impl Fn(usize) + 'static,
66) -> NodeId
67where
68 T: IntoSharedAnnotatedString,
69{
70 let text = text.into_shared();
71 let text_for_click = text.clone();
72 let style_for_click = style.clone();
73 let on_click: Rc<dyn Fn(usize)> = Rc::new(on_click);
74
75 let clickable_modifier = modifier.clickable(move |point| {
76 let offset = crate::text::get_offset_for_position(
77 &text_for_click,
78 &style_for_click,
79 point.x,
80 point.y,
81 );
82 on_click(offset);
83 });
84
85 BasicText(
86 text,
87 clickable_modifier,
88 style,
89 TextOverflow::Clip,
90 true,
91 usize::MAX,
92 1,
93 )
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use cranpose_core::{location_key, Composition, MemoryApplier};
100
101 #[test]
102 fn clickable_text_composes_without_panic() {
103 let mut comp = Composition::new(MemoryApplier::new());
104 comp.render(location_key(file!(), line!(), column!()), || {
105 ClickableText(
106 AnnotatedString::from("Hello"),
107 Modifier::empty(),
108 TextStyle::default(),
109 |_offset| {},
110 );
111 })
112 .expect("composition succeeds");
113 }
114}