Skip to main content

azul_layout/managers/
selection.rs

1//! Clipboard content types for copy/paste operations
2//!
3//! Contains `ClipboardContent` and `StyledTextRun`, used by clipboard and
4//! changeset modules.
5//!
6//! **Rich-text status:** `StyledTextRun`, `StyledTextRunVec` and the
7//! `ClipboardContent.styled_runs` field are FFI-exported (api.json), but the
8//! rich path is only half-wired: the live clipboard producers build
9//! `styled_runs` empty (`window.rs::get_selected_content_for_clipboard`,
10//! paste in `common/event.rs`) and the platform clipboard backends write only
11//! `plain_text`. Fully wiring it means (a) extracting per-run style from the
12//! styled DOM when copying and (b) adding an HTML/RTF format to each platform's
13//! clipboard write (and reading it back on paste). `to_html()` below is the
14//! retained consumer for that future format. Until then the FFI surface is
15//! kept (it is public API) but `styled_runs` stays empty.
16
17use azul_css::{impl_option, impl_option_inner, AzString, OptionString};
18
19// Clipboard Content Extraction
20
21/// Styled text run for rich clipboard content
22#[derive(Debug, Clone, PartialEq)]
23#[repr(C)]
24pub struct StyledTextRun {
25    /// The actual text content
26    pub text: AzString,
27    /// Font family name
28    pub font_family: OptionString,
29    /// Font size in pixels
30    pub font_size_px: f32,
31    /// Text color
32    pub color: azul_css::props::basic::ColorU,
33    /// Whether text is bold
34    pub is_bold: bool,
35    /// Whether text is italic
36    pub is_italic: bool,
37}
38
39azul_css::impl_option!(StyledTextRun, OptionStyledTextRun, copy = false, [Debug, Clone, PartialEq]);
40azul_css::impl_vec!(StyledTextRun, StyledTextRunVec, StyledTextRunVecDestructor, StyledTextRunVecDestructorType, StyledTextRunVecSlice, OptionStyledTextRun);
41azul_css::impl_vec_debug!(StyledTextRun, StyledTextRunVec);
42azul_css::impl_vec_clone!(StyledTextRun, StyledTextRunVec, StyledTextRunVecDestructor);
43azul_css::impl_vec_partialeq!(StyledTextRun, StyledTextRunVec);
44
45/// Clipboard content with both plain text and styled (HTML) representation
46#[derive(Debug, Clone, PartialEq)]
47#[repr(C)]
48pub struct ClipboardContent {
49    /// Plain text representation (UTF-8)
50    pub plain_text: AzString,
51    /// Rich text runs with styling information
52    pub styled_runs: StyledTextRunVec,
53}
54
55impl_option!(
56    ClipboardContent,
57    OptionClipboardContent,
58    copy = false,
59    [Debug, Clone, PartialEq]
60);
61
62impl ClipboardContent {
63    /// Convert styled runs to HTML for rich clipboard formats.
64    ///
65    /// Retained consumer of the FFI-exported `styled_runs`: returns an empty
66    /// `<div></div>` until `styled_runs` is populated and the platform clipboard
67    /// backends gain an HTML format (see module docs). Kept as public API.
68    #[must_use] pub fn to_html(&self) -> String {
69        use core::fmt::Write as _;
70        let mut html = String::from("<div>");
71
72        for run in self.styled_runs.as_slice() {
73            html.push_str("<span style=\"");
74
75            if let Some(font_family) = run.font_family.as_ref() {
76                let _ = write!(html, "font-family: {}; ", font_family.as_str());
77            }
78            let _ = write!(html, "font-size: {}px; ", run.font_size_px);
79            let _ = write!(
80                html,
81                "color: rgba({}, {}, {}, {}); ",
82                run.color.r,
83                run.color.g,
84                run.color.b,
85                f32::from(run.color.a) / 255.0
86            );
87            if run.is_bold {
88                html.push_str("font-weight: bold; ");
89            }
90            if run.is_italic {
91                html.push_str("font-style: italic; ");
92            }
93
94            html.push_str("\">");
95            // Escape HTML entities
96            let escaped = run
97                .text
98                .as_str()
99                .replace('&', "&amp;")
100                .replace('<', "&lt;")
101                .replace('>', "&gt;");
102            html.push_str(&escaped);
103            html.push_str("</span>");
104        }
105
106        html.push_str("</div>");
107        html
108    }
109}
110