//! A text selection API built around [`iced`]'s [`Paragraph`]. //! //! [`iced`]: https://iced.rs //! [`Paragraph`]: https://docs.iced.rs/iced_graphics/text/paragraph/struct.Paragraph.html #[cfg(feature = "markdown")] pub mod markdown; pub mod operation; pub mod selectable; pub mod selection; pub mod text; use core::Element; use iced_widget::core; use iced_widget::graphics::text::Paragraph; #[cfg(feature = "markdown")] pub use markdown::view as markdown; #[doc(no_inline)] pub use text::Text; /// Creates a new [`Text`] widget with the provided content. /// /// [`Text`]: core::widget::Text /// /// This macro uses the same syntax as [`format!`], but creates a new [`Text`] widget instead. /// /// See [the formatting documentation in `std::fmt`](std::fmt) /// for details of the macro argument syntax. /// /// # Examples /// /// ```no_run,ignore /// use iced_selection::text; /// /// enum Message { /// // ... /// } /// /// fn view(_state: &State) -> Element { /// let simple = text!("Hello, world!"); /// /// let keyword = text!("Hello, {}", "world!"); /// /// let planet = "Earth"; /// let local_variable = text!("Hello, {planet}!"); /// // ... /// } /// ``` #[macro_export] macro_rules! text { ($($arg:tt)*) => { $crate::Text::new(format!($($arg)*)) }; } /// Creates some [`Rich`] text with the given spans. /// /// # Example /// ```no_run,ignore /// use iced::font; /// use iced_selection::{rich_text, span}; /// use iced::{color, never, Font}; /// /// #[derive(Debug, Clone)] /// enum Message { /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// rich_text![ /// span("I am red!").color(color!(0xff0000)), /// span(" "), /// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), /// ] /// .on_link_click(never) /// .size(20) /// .into() /// } /// ``` /// /// [`Rich`]: crate::text::Rich #[macro_export] macro_rules! rich_text { () => ( $crate::text::Rich::new() ); ($($x:expr),+ $(,)?) => ( $crate::text::Rich::from_iter([$($crate::text::Span::from($x)),+]) ); } /// Creates a new [`Span`] of text with the provided content. /// /// A [`Span`] is a fragment of some [`Rich`] text. /// /// This macro uses the same syntax as [`format!`], but creates a new [`Span`] widget instead. /// /// See [the formatting documentation in `std::fmt`](std::fmt) /// for details of the macro argument syntax. /// /// # Example /// ```no_run,ignore /// use iced::font; /// use iced_selection::{rich_text, span}; /// use iced::{color, never, Font}; /// /// #[derive(Debug, Clone)] /// enum Message { /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// rich_text![ /// span!("I am {}!", red).color(color!(0xff0000)), /// " ", /// span!("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), /// ] /// .on_link_click(never) /// .size(20) /// .into() /// } /// ``` /// /// [`Rich`]: crate::text::Rich /// [`Span`]: crate::text::Span #[macro_export] macro_rules! span { ($($arg:tt)*) => { $crate::text::Span::new(format!($($arg)*)) }; } /// Creates a new [`Text`] widget with the provided content. /// /// # Example /// ```no_run,ignore /// use iced_selection::text; /// /// enum Message { /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// text("Hello, this is iced!") /// .size(20) /// .into() /// } /// ``` pub fn text<'a, Theme, Renderer>( text: impl text::IntoFragment<'a>, ) -> Text<'a, Theme, Renderer> where Theme: text::Catalog + 'a, Renderer: core::text::Renderer, { Text::new(text) } /// Creates some [`Rich`] text with the given spans. /// /// [`Rich`]: crate::text::Rich /// /// # Example /// ```no_run,ignore /// use iced::font; /// use iced_selection::{rich_text, span}; /// use iced::{color, never, Font}; /// /// #[derive(Debug, Clone)] /// enum Message { /// LinkClicked(&'static str), /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// rich_text([ /// span("I am red!").color(color!(0xff0000)), /// span(" "), /// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), /// ]) /// .on_link_click(never) /// .size(20) /// .into() /// } /// ``` pub fn rich_text<'a, Link, Message, Theme, Renderer>( spans: impl AsRef<[text::Span<'a, Link, core::Font>]> + 'a, ) -> text::Rich<'a, Link, Message, Theme, Renderer> where Link: Clone + 'static, Theme: text::Catalog + 'a, Renderer: core::text::Renderer, { text::Rich::with_spans(spans) } /// Creates a new [`Span`] of text with the provided content. /// /// A [`Span`] is a fragment of some [`Rich`] text. /// /// [`Rich`]: crate::text::Rich /// [`Span`]: crate::text::Span /// /// # Example /// ```no_run,ignore /// use iced::font; /// use iced_selection::{rich_text, span}; /// use iced::{color, never, Font}; /// /// #[derive(Debug, Clone)] /// enum Message { /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// rich_text![ /// span("I am red!").color(color!(0xff0000)), /// " ", /// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), /// ] /// .on_link_click(never) /// .size(20) /// .into() /// } /// ``` pub fn span<'a, Link>( text: impl text::IntoFragment<'a>, ) -> text::Span<'a, Link, core::Font> { text::Span::new(text) } /// Creates some [`Selectable`] with the given content. This [`Selectable`] will make all inner /// [`Text`] or [`Rich`] change their behavior from an independent selectable to a global /// selectable which makes it so you can select across widgets and start selections out of bounds /// of widgets. This [`Selectable`] will also handle the 'ctrl + c' shortcut by getting all the /// selected text inside it and write it to the clipboard. /// /// WARNING: You should only have one [`Selectable`] on your app, preferrably as the root of your /// app to make sure that any [`Text`] or [`Rich`] is inside it. Having multiple selectables or a /// selectable along side some [`Text`] or [`Rich`] is undefined behavior and even though the /// selection might look ok for you the copied text will probably not be what you expect. /// /// [`Selectable`]: crate::selectable::Selectable /// [`Text`]: crate::Text /// [`Rich`]: crate::text::Rich /// /// # Example /// ```no_run,ignore /// use iced::font; /// use iced_selection::{rich_text, selectable, span}; /// use iced::{color, column, never, Font}; /// /// #[derive(Debug, Clone)] /// enum Message { /// LinkClicked(&'static str), /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// selectable( /// column![ /// rich_text([ /// span("I am red!").color(color!(0xff0000)), /// span(" "), /// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), /// ]) /// .on_link_click(never) /// .size(20), /// text("Hello, this is iced!"), /// ] /// ) /// .into() /// } /// ``` pub fn selectable<'a, Message, Theme, Renderer>( content: impl Into>, ) -> selectable::Selectable<'a, Message, Theme, Renderer> where Theme: 'a, Renderer: core::Renderer, { selectable::Selectable::new(content) }