zng_wgt_text_input/
selectable.rs

1//! Selectable text.
2
3use zng_ext_clipboard::COPY_CMD;
4use zng_wgt::prelude::*;
5use zng_wgt_button::Button;
6use zng_wgt_input::focus::FocusableMix;
7use zng_wgt_menu::{
8    self as menu,
9    context::{ContextMenu, context_menu_fn},
10};
11use zng_wgt_style::{Style, StyleMix, impl_style_fn, style_fn};
12use zng_wgt_text::{self as text, *};
13
14#[doc(hidden)]
15pub use zng_wgt::prelude::formatx as __formatx;
16
17/// Styleable read-only text widget that can be selected and copied to clipboard.
18///
19/// # Shorthand
20///
21/// The same [`Text!`](struct@zng_wgt_text::Text#shorthand) shorthand can be used in this macro.
22#[widget($crate::selectable::SelectableText {
23    ($txt:literal) => {
24        txt = $crate::selectable::__formatx!($txt);
25    };
26    ($txt:expr) => {
27        txt = $txt;
28    };
29    ($txt:tt, $($format:tt)*) => {
30        txt = $crate::selectable::__formatx!($txt, $($format)*);
31    };
32})]
33pub struct SelectableText(FocusableMix<StyleMix<zng_wgt_text::Text>>);
34impl SelectableText {
35    fn widget_intrinsic(&mut self) {
36        self.style_intrinsic(STYLE_FN_VAR, property_id!(self::style_fn));
37        widget_set! {
38            self;
39            txt_selectable = true;
40            style_base_fn = style_fn!(|_| DefaultStyle!());
41        }
42    }
43}
44impl_style_fn!(SelectableText);
45
46/// Default selectable text style.
47#[widget($crate::selectable::DefaultStyle)]
48pub struct DefaultStyle(Style);
49impl DefaultStyle {
50    fn widget_intrinsic(&mut self) {
51        use zng_wgt_input::*;
52        use zng_wgt_layer::*;
53
54        widget_set! {
55            self;
56            replace = true;
57            cursor = CursorIcon::Text;
58
59            popup::context_capture = crate::default_popup_context_capture();
60            context_menu_fn = WidgetFn::new(default_context_menu);
61            selection_toolbar_fn = WidgetFn::new(default_selection_toolbar);
62            selection_color = colors::ACCENT_COLOR_VAR.rgba_map(|c| c.with_alpha(30.pct()));
63        }
64    }
65}
66
67/// Context menu set by the [`DefaultStyle!`].
68///
69/// [`DefaultStyle!`]: struct@DefaultStyle
70pub fn default_context_menu(args: menu::context::ContextMenuArgs) -> impl UiNode {
71    let id = args.anchor_id;
72    ContextMenu!(ui_vec![Button!(COPY_CMD.scoped(id)), Button!(text::cmd::SELECT_ALL_CMD.scoped(id)),])
73}
74
75/// Selection toolbar set by the [`DefaultStyle!`].
76///
77/// [`DefaultStyle!`]: struct@DefaultStyle
78pub fn default_selection_toolbar(args: text::SelectionToolbarArgs) -> impl UiNode {
79    if args.is_touch {
80        let id = args.anchor_id;
81        ContextMenu! {
82            style_fn = menu::context::TouchStyle!();
83            children = ui_vec![Button!(COPY_CMD.scoped(id)), Button!(text::cmd::SELECT_ALL_CMD.scoped(id)),]
84        }
85        .boxed()
86    } else {
87        NilUiNode.boxed()
88    }
89}