fltkrs_richdisplay/
lib.rs

1//! 富文本查看器,支持图文混排,支持历史内容回顾。支持`fluid`设计器。
2//!
3//! 创建组件示例:
4//! ```rust,no_run
5//! use fltk::{app, window};
6//! use fltk::enums::{Event, Key};
7//! use fltk::prelude::{GroupExt, WidgetBase, WidgetExt, WindowExt};
8//! use log::error;
9//! use fltkrs_richdisplay::rich_text::RichText;
10//! use fltkrs_richdisplay::{RichDataOptions, UserData};
11//!
12//! #[tokio::main]
13//! async fn main() {
14//!    let app = app::App::default();
15//!    let mut win = window::Window::default().with_size(1000, 600).center_screen();
16//!    let mut rich_text = RichText::new(100, 120, 800, 400, None);
17//!    rich_text.set_cache_size(200);
18//!    win.end();
19//!    win.show();
20//!
21//!    let ud = UserData::new_text("dev@DESKTOP-PCL7MBI:\t~$ ls\r\n".to_string());
22//!    rich_text.append(ud);
23//!
24//!    app.run().unwrap();
25//! }
26//! ```
27//!
28//! 另一个支持互动的复杂示例,鼠标右键点击可互动的数据段会产生互动消息:
29//! ```rust,no_run
30//! use fltk::{app, window};
31//! use fltk::enums::{Color, Event, Font, Key};
32//! use fltk::prelude::{GroupExt, WidgetBase, WidgetExt, WindowExt};
33//! use log::error;
34//! use fltkrs_richdisplay::rich_text::RichText;
35//! use fltkrs_richdisplay::{RichDataOptions, UserData, CallbackData, DocEditType};
36//!
37//! pub enum GlobalMessage {
38//!     ContentData(UserData),
39//!     UpdateData(RichDataOptions),
40//!     DisableData(i64),
41//! }
42//!
43//! #[tokio::main]
44//! async fn main() {
45//!     let app = app::App::default();
46//!     let mut win = window::Window::default().with_size(1000, 600).center_screen();
47//!     win.make_resizable(true);
48//!
49//!     let mut rich_text = RichText::new(100, 120, 800, 400, None);
50//!
51//!     // 互动消息通道
52//!     let (action_sender, mut action_receiver) = tokio::sync::mpsc::channel::<CallbackData>(100);
53//!
54//!     // 自定义回调函数,当用户鼠标右键点击可互动的数据段时,组件会调用回调函数。
55//!     let cb_fn = {
56//!         let sender_rc = action_sender.clone();
57//!         move |user_data| {
58//!             let sender = sender_rc.clone();
59//!             tokio::spawn(async move {
60//!                 if let Err(e) = sender.send(user_data).await {
61//!                     error!("发送用户操作失败: {:?}", e);
62//!                 }
63//!             });
64//!         }
65//!     };
66//!     rich_text.set_notifier(cb_fn);
67//!     rich_text.set_cache_size(1000);
68//!
69//!     /*
70//!     启用PageUp/PageDown快捷键打开和关闭回顾区的功能支持。
71//!     使用鼠标滚轮进行打开/关闭回顾区的功能已经内置在模块包中,而PageUp/PageDown的快捷键无法被内置组件检测到,因此需要外层容器主动调用API实现。
72//!     包里提供的两个API接口为此提供支持:`RichText::auto_open_reviewer(&self)`和`RichText::auto_close_reviewer(&self)`。
73//!     */
74//!     win.handle({
75//!         let rich_text_rc = rich_text.clone();
76//!         move |_, evt| {
77//!             let mut handled = false;
78//!             match evt {
79//!                 Event::KeyDown => {
80//!                     if app::event_key_down(Key::PageDown) {
81//!                         handled = rich_text_rc.auto_close_reviewer();
82//!                     } else if app::event_key_down(Key::PageUp) {
83//!                         handled = rich_text_rc.auto_open_reviewer().unwrap();
84//!                     }
85//!                 }
86//!                 _ => {}
87//!             }
88//!             handled
89//!         }
90//!     });
91//!
92//!     // App全局消息唯一通道
93//!     let (global_sender, global_receiver) = app::channel::<GlobalMessage>();
94//!
95//!     win.end();
96//!     win.show();
97//!
98//!     let global_sender_rc = global_sender.clone();
99//!
100//!     // 互动消息处理器
101//!     tokio::spawn(async move {
102//!         while let Some(cb_data) = action_receiver.recv().await {
103//!             if let CallbackData::Data(data) = cb_data {
104//!                 if data.text.starts_with("10") {
105//!                     let toggle = !data.blink;
106//!                     let update_options = RichDataOptions::new(data.id).blink(toggle);
107//!                     global_sender_rc.send(GlobalMessage::UpdateData(update_options));
108//!                 }
109//!             }
110//!         }
111//!     });
112//!
113//!     let data = vec![
114//!         UserData::new_text("0dev@DESKTOP-PCL7MBI:\t~$ ls\r\n1分片\r\n2分片".to_string()),
115//!         UserData::new_text("3dev@DESKTOP-PCL7MBI:\t~$ ls\r\n".to_string()),
116//!         UserData::new_text("4dev@DESKTOP-PCL7MBI:\t~$ ls\r\nls -al".to_string()),
117//!         UserData::new_text("5dev@DESKTOP-PCL7MBI:\t~$ ls\r\n速度".to_string()).set_bg_color(Some(Color::Green)),
118//!         UserData::new_text("6dev@DESKTOP-PCL7MBII:\t~$ ls Downloads\r\n".to_string()).set_font_and_size(Font::Helvetica, 22),
119//!         UserData::new_text("7dev@DESKTOP-PCL7MBI:\t~$ ls\r\n".to_string()),
120//!         UserData::new_text("8dev@DESKTOP-PCL7MBI:~$ ls".to_string()).set_underline(true),
121//!         UserData::new_text("9dev@DESKTOP-PCL7MBI:~$ ls\r\n".to_string()).set_underline(true),
122//!         UserData::new_text("10 Right click me! 鼠标右键点击!\r\n".to_string()).set_font_and_size(Font::Helvetica, 20).set_clickable(true).set_blink(true),
123//!         UserData::new_text("11dev@DESKTOP-PCL7MBI:\t~$ ls\r\n".to_string()),
124//!     ];
125//!
126//!     let mut docs: Vec<DocEditType> = Vec::new();
127//!     for ud in data {
128//!         docs.push(DocEditType::Data(ud));
129//!     }
130//!     rich_text.append_batch(&mut docs);
131//!
132//!     let mut has_recent_message = false;
133//!     while app.wait() {
134//!         if let Some(msg) = global_receiver.recv() {
135//!             match msg {
136//!                 GlobalMessage::ContentData(data) => {
137//!                     has_recent_message = true;
138//!                     rich_text.append(data);
139//!                 }
140//!                 GlobalMessage::UpdateData(options) => {
141//!                     rich_text.update_data(options);
142//!                 }
143//!                 GlobalMessage::DisableData(id) => {
144//!                     rich_text.disable_data(id);
145//!                 }
146//!             }
147//!         } else {
148//!             has_recent_message = false;
149//!         }
150//!
151//!         if !has_recent_message {
152//!             app::sleep(0.001);
153//!             app::awake();
154//!         }
155//!     }
156//! }
157//! ```
158//!
159//!
160
161use std::cell::{RefCell};
162use std::cmp::{max, min, Ordering};
163use std::collections::{HashMap};
164use std::fmt::{Debug, Display, Formatter};
165use std::ops::{RangeInclusive};
166use std::path::{PathBuf};
167use std::rc::{Rc};
168use std::slice::Iter;
169use std::sync::{Arc, Weak};
170use fltk::{app, draw};
171use fltk::draw::{descent, draw_line, draw_rectf, draw_rounded_rect, draw_rounded_rectf, draw_text_n, LineStyle, measure, set_draw_color, set_font, set_line_style};
172use fltk::enums::{Color, ColorDepth, Cursor, Font};
173use fltk::prelude::{ImageExt, WidgetBase};
174use fltk::image::{RgbImage, SharedImage, SvgImage};
175
176use idgenerator_thin::YitIdHelper;
177use log::{error};
178use parking_lot::{RwLock};
179use serde::{Serialize, Serializer};
180use serde::ser::SerializeStruct;
181
182pub mod rich_text;
183pub mod rich_reviewer;
184mod rewrite_board;
185
186/// 默认内容边界到窗口之间的空白距离。
187pub(crate) const PADDING: Padding = Padding { left: 5, top: 5, right: 5, bottom: 5 };
188
189/// 图片与其他内容之间的垂直间距。
190pub const IMAGE_PADDING_H: i32 = 2;
191
192/// 图片与其他内容之间的水平间距。
193pub const IMAGE_PADDING_V: i32 = 2;
194
195/// 闪烁强度切换间隔时间,目前使用固定频率。
196pub const BLINK_INTERVAL: f64 = 0.5;
197
198/// 高亮文本背景色,查询目标时所有匹配目标的背景色。
199pub const HIGHLIGHT_BACKGROUND_COLOR: Color = Color::from_rgb(0, 0, 255);
200
201/// 高亮文本焦点边框颜色,查询目标时当前正在聚焦的目标。
202pub const HIGHLIGHT_RECT_COLOR: Color = Color::from_rgb(255, 145, 0);
203
204/// 高亮文本焦点边框对比色,当查询目标时当前正在聚焦的目标在闪烁时切换的对比颜色。
205pub const HIGHLIGHT_RECT_CONTRAST_COLOR: Color = Color::from_rgb(0, 110, 255);
206/// 高亮文本焦点边框弧度参数。
207pub const HIGHLIGHT_ROUNDED_RECT_RADIUS: i32 = 3;
208
209/// 最亮的白色。
210pub const WHITE: Color = Color::from_rgb(255, 255, 255);
211
212/// 默认字体尺寸。
213pub const DEFAULT_FONT_SIZE: i32 = 16;
214
215/// 从字体高度计算行高度使用的放大系数。
216pub const LINE_HEIGHT_FACTOR: f32 = 1.4;
217
218/// 用于衡量窗口尺寸的基本字符。若应用对窗口尺寸敏感,则建议使用等宽字体作为默认字体。`fltk`中`Font::Screen`代表等宽字体。
219pub const BASIC_UNIT_CHAR: char = 'A';
220
221/// 默认的Tab宽度,使用空格代替。
222pub const DEFAULT_TAB_WIDTH: u8 = 4;
223
224pub const MXP_IMAGE_CONTEXT_MENU_REFRESH: &str = "refresh";
225pub const MXP_IMAGE_CONTEXT_MENU_SAVE_AS: &str = "save_as";
226pub const MXP_IMAGE_CONTEXT_MENU_COPY_URL: &str = "copy_url";
227
228#[derive(Debug, Clone)]
229pub struct LoadImageOption {
230    pub data_id: i64,
231    pub file_path: Option<String>,
232    pub target_width: i32,
233    pub target_height: i32
234}
235
236impl LoadImageOption {
237    pub fn new(data_id: i64, file_path: Option<String>, target_width: i32, target_height: i32) -> Self {
238        Self {
239            data_id,
240            file_path,
241            target_width,
242            target_height,
243        }
244    }
245}
246
247#[derive(Clone)]
248pub struct CprCallback {
249    pub report: Arc<RwLock<Box<dyn FnMut(String) + Send + Sync +'static>>>
250}
251
252impl CprCallback {
253    pub fn new<F>(cb: F) -> Self where F: FnMut(String) + Send + Sync +'static {
254        Self {
255            report: Arc::new(RwLock::new(Box::new(cb)))
256        }
257    }
258}
259
260impl Serialize for CprCallback {
261    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
262        let mut state = serializer.serialize_struct("CprCallback", 1).unwrap();
263        state.serialize_field("cb", "Cursor Position Report function").unwrap();
264        state.end()
265    }
266}
267
268impl Debug for CprCallback {
269    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
270        write!(f, "CprCallback {}", Arc::<RwLock<Box<dyn FnMut(String) + Send + Sync +'static>>>::strong_count(&self.report))
271    }
272}
273
274/// 数据或操作类型。
275#[derive(Clone, Debug, Serialize)]
276pub enum DocEditType {
277    Data(UserData),
278    EraseInLine(u8),
279    EraseInDisplay(u8),
280    CursorUp(usize),
281    CursorDown(usize),
282    CursorBack(usize),
283    CursorForward(usize),
284    CursorNextLine(usize),
285    CursorPreviousLine(usize),
286    /// 光标水平移动到第n列,绝对位置。
287    CursorHorizontalAbsolute(usize),
288    /// 光标移动到第n行m列,绝对位置。
289    CursorAbsolute(usize, usize),
290    /// 显示或关闭光标。
291    ToggleCursor(String, bool),
292    /// 使缓存中符合过滤条件的数据目标过期。
293    Expire(String),
294    /// 本地光标位置控制标志:0远程控制,1本地控制,2任意字符开启输出,3特殊字符control-Q开启输出。
295    RemoteFlowControl(u8),
296    /// 通过回调函数汇报光标位置。
297    CursorPosReport(CprCallback),
298    /// 面板流结束标志。
299    PanelFlowEnd
300}
301
302impl Display for DocEditType {
303    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
304        match self {
305            DocEditType::Data(ud) => {
306                write!(f, "{}", ud.text)
307            }
308            DocEditType::CursorUp(n) => { write!(f, "\x1b[{}A", n) }
309            DocEditType::CursorDown(n) => { write!(f, "\x1b[{}B", n) }
310            DocEditType::CursorForward(n) => { write!(f, "\x1b[{}C", n) }
311            DocEditType::CursorBack(n) => { write!(f, "\x1b[{}D", n) }
312            DocEditType::CursorNextLine(n) => { write!(f, "\x1b[{}E", n) }
313            DocEditType::CursorPreviousLine(n) => { write!(f, "\x1b[{}F", n) }
314            DocEditType::CursorHorizontalAbsolute(n) => { write!(f, "\x1b[{}G", n) }
315            DocEditType::CursorAbsolute(n, m) => { write!(f, "\x1b[{};{}H", n, m) }
316            DocEditType::EraseInDisplay(n) => { write!(f, "\x1b[{}J", n) }
317            DocEditType::EraseInLine(n) => { write!(f, "\x1b[{}K", n) }
318            DocEditType::ToggleCursor(param, show) => { write!(f, "\x1b[{}{}", param, if *show { "h" } else { "l" }) }
319            DocEditType::Expire(target) => { write!(f, "<EXPIRE {}>", target)}
320            DocEditType::RemoteFlowControl(code) => {write!(f, "远程流控制子协商开关:{}>", code)}
321            DocEditType::CursorPosReport(cb) => {write!(f, "汇报光标位置 {:?}", cb)}
322            DocEditType::PanelFlowEnd => {write!(f, "面板流结束")}
323        }
324    }
325}
326
327/// 回调函数的参数类型,用于区分来源事件。
328#[derive(Debug)]
329pub enum CallbackData {
330    /// 数据互动事件产生的回调参数。
331    Data(UserData),
332    /// 主视图缩放时产生的回调参数。
333    Shape(ShapeData),
334    /// 图片点击事件的回调参数。
335    Image(ImageEventData),
336}
337
338
339/// 回调函数载体。
340/// 当用户使用鼠标点击主视图或回顾区视图上的可互动数据段时,会执行该回调函数,并将点击目标处的数据作为参数传入回调函数。
341/// 用户可自由定义回调函数的具体行为。
342#[derive(Clone)]
343pub struct Callback {
344    /// 回调函数。
345    notifier: Arc<RwLock<Box<dyn FnMut(CallbackData) + Send + Sync +'static>>>,
346}
347
348impl Callback {
349
350
351    /// 构建新的回调结构体实例。
352    ///
353    /// # Arguments
354    ///
355    /// * `notifier`: 回调函数包装。
356    ///
357    /// returns: Callback
358    ///
359    /// # Examples
360    ///
361    /// ```
362    /// use std::cell::RefCell;
363    /// use std::rc::Rc;
364    /// use log::error;
365    /// use fltkrs_richdisplay::rich_text::RichText;
366    /// use fltkrs_richdisplay::{Callback, CallbackData, UserData};
367    ///
368    /// let mut rich_text = RichText::new(100, 120, 800, 400, None);
369    /// let (sender, mut receiver) = tokio::sync::mpsc::channel::<CallbackData>(100);
370    /// let cb_fn = {
371    ///     let sender_rc = sender.clone();
372    ///     move |user_data| {
373    ///         let sender = sender_rc.clone();
374    ///         tokio::spawn(async move {
375    ///             if let Err(e) = sender.send(user_data).await {
376    ///                 error!("发送用户操作失败: {:?}", e);
377    ///             }
378    ///         });
379    ///     }
380    /// };
381    /// rich_text.set_notifier(cb_fn);
382    /// ```
383    pub fn new(notifier: Arc<RwLock<Box<dyn FnMut(CallbackData) + Send + Sync +'static>>>) -> Callback {
384        Callback { notifier }
385    }
386
387    /// 执行回调。
388    ///
389    /// # Arguments
390    ///
391    /// * `data`: 用户数据。
392    ///
393    /// returns: ()
394    ///
395    /// # Examples
396    ///
397    /// ```
398    ///
399    /// ```
400    fn notify(&mut self, data: CallbackData) {
401        let notify = &mut* self.notifier.write();
402        notify(data);
403    }
404}
405
406impl Debug for Callback {
407    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
408        write!(f, "Callback count: {}", Arc::<RwLock<Box<dyn FnMut(CallbackData) + Send + Sync +'static>>>::strong_count(&self.notifier))
409    }
410}
411
412/// 分页请求参数
413#[derive(Debug, Clone)]
414pub enum PageOptions {
415    /// 下一页,附带当前页的最后一条记录的id。
416    NextPage(i64),
417    /// 上一页,附带当前页的第一条记录的id。
418    PrevPage(i64),
419}
420
421/// 请求新页数据的回调函数载体。
422/// 当视图滚动到页面底部或顶部时,通过鼠标滚轮或按键`PageDown`或`PageUp`时,会触发执行预定义的回调函数,
423/// 若有更多可用的数据,用户应当在此时提供下一页或上一页数据。
424#[derive(Clone)]
425pub struct CallPage {
426    /// 回调函数。
427    notifier: Arc<RwLock<Box<dyn FnMut(PageOptions) + Sync + Send + 'static>>>,
428}
429
430impl Debug for CallPage {
431    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
432        write!(f, "CallPage count: {}", Arc::<RwLock<Box<(dyn FnMut(PageOptions) + Sync + Send + 'static)>>>::strong_count(&self.notifier))
433    }
434}
435
436/// 用于表示窗口尺寸变化前后差异的数据结构。
437#[derive(Debug, Clone, Copy)]
438pub struct ShapeData {
439    /// 旧的宽度。
440    pub old_width: i32,
441    /// 旧的高度。
442    pub old_height: i32,
443    /// 新的宽度。
444    pub new_width: i32,
445    /// 新的高度。
446    pub new_height: i32,
447    /// 按照默认字体设置计算出单行的列数。
448    pub new_cols: i32,
449    /// 按照默认字体设置计算出可有效显示的行数。
450    /// 在全部内容均保持默认字体的情况下,由于视图上可以显示被裁剪的行内容,因此视图中可见的行数可能大于这个数值。
451    pub new_rows: i32,
452}
453
454impl ShapeData {
455    pub fn new(old_width: i32, old_height: i32, new_width: i32, new_height: i32, new_cols: i32, new_rows: i32) -> Self {
456        Self {
457            old_width,
458            old_height,
459            new_width,
460            new_height,
461            new_cols,
462            new_rows,
463        }
464    }
465}
466
467/// 用于表示鼠标点击图片时的事件信息。
468#[derive(Debug, Clone)]
469pub struct ImageEventData {
470    /// 鼠标点击位置,相对于图片的左上角。
471    pub click_point: (i32, i32),
472    /// 图片的来源地址。
473    pub src: Option<String>,
474    /// 图片所属数据段的ID。
475    pub data_id: i64,
476    /// 执行动作。
477    pub act: String,
478    pub file: Option<PathBuf>,
479    /// 目标尺寸,可能与图片原始尺寸不同。
480    pub target_size: (i32, i32),
481}
482
483impl ImageEventData {
484    pub fn new(click_point: (i32, i32), src: Option<String>, data_id: i64, act: String, file: Option<PathBuf>, target_size: (i32, i32)) -> Self {
485        Self {
486            click_point,
487            src,
488            data_id,
489            act,
490            file,
491            target_size,
492        }
493    }
494}
495
496
497impl CallPage {
498    /// 构建新的分页回调结构体实例。
499    pub fn new(notifier: Arc<RwLock<Box<dyn FnMut(PageOptions) + Sync + Send + 'static>>>) -> Self {
500        Self { notifier }
501    }
502
503    fn notify(&mut self, opt: PageOptions) {
504        // let notify = &mut* self.notifier.borrow_mut();
505        let notify = &mut* self.notifier.write();
506        notify(opt);
507    }
508}
509
510/// 闪烁强度状态。
511#[derive(Debug, Clone,Copy, PartialEq, Eq)]
512pub(crate) enum BlinkDegree {
513    /// 正常,原色显示。
514    Normal,
515    /// 对比色或不显示。
516    Contrast,
517}
518
519/// 可视区域闪烁开关标记和状态。
520#[derive(Debug, Clone, Copy, PartialEq, Eq)]
521pub(crate) struct BlinkState {
522    /// 可视区域是否存在闪烁内容。
523    on: bool,
524    /// 应闪烁内容在下一次刷新显示时的强度。
525    next: BlinkDegree,
526
527    /// 焦点目标的边框颜色。
528    focus_boarder_color: Color,
529
530    /// 焦点目标的边框对比色。
531    focus_boarder_contrast_color: Color,
532
533    /// 焦点目标的边框线条宽度。
534    focus_boarder_width: i32,
535
536    /// 焦点目标的背景颜色。
537    focus_background_color: Color,
538}
539
540impl BlinkState {
541    pub fn new() -> BlinkState {
542        BlinkState {
543            on: false,
544            next: BlinkDegree::Normal,
545            focus_boarder_color: HIGHLIGHT_RECT_COLOR,
546            focus_boarder_contrast_color: HIGHLIGHT_RECT_CONTRAST_COLOR,
547            focus_boarder_width: 2,
548            focus_background_color: HIGHLIGHT_BACKGROUND_COLOR
549        }
550    }
551
552    pub fn off(&mut self) {
553        self.on = false;
554        // self.next = BlinkDegree::Normal;
555    }
556
557    pub fn on(&mut self) {
558        self.on = true;
559    }
560
561    pub fn toggle_when_on(&mut self) -> bool {
562        if self.on {
563            self.next = match self.next {
564                BlinkDegree::Normal => BlinkDegree::Contrast,
565                BlinkDegree::Contrast => BlinkDegree::Normal,
566            };
567            // debug!("切换对比色: {:?}", self.next);
568            true
569        } else {
570            false
571        }
572    }
573
574}
575
576/// 自定义事件。
577pub(crate) struct LocalEvent;
578impl LocalEvent {
579
580    /// 滚动事件。
581    pub const SCROLL_TO: i32 = 100;
582
583    /// 缩放事件。
584    pub const RESIZE: i32 = 101;
585
586    /// 从rich-display容器外部发起关闭回顾区的事件。
587    pub const DROP_REVIEWER_FROM_EXTERNAL: i32 = 102;
588
589    /// 从rich-display容器外部发起打开回顾区的事件。
590    pub const OPEN_REVIEWER_FROM_EXTERNAL: i32 = 103;
591}
592
593/// 矩形结构,元素0/1代表x/y坐标,表示左上角坐标;元素2/3代表w/h宽和高,w/h不为负值。
594#[derive(Debug, Clone, Eq, Hash, PartialEq, Copy)]
595pub(crate) struct Rectangle(i32, i32, i32, i32);
596
597impl PartialOrd<Self> for Rectangle {
598    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
599        if self.1 > other.1 {
600            Some(Ordering::Greater)
601        } else if self.1 < other.1 {
602            Some(Ordering::Less)
603        } else {
604            if self.0 > other.0 {
605                Some(Ordering::Greater)
606            } else if self.0 < other.0 {
607                Some(Ordering::Less)
608            } else {
609                Some(Ordering::Equal)
610            }
611        }
612    }
613}
614
615impl Ord for Rectangle {
616    fn cmp(&self, other: &Self) -> Ordering {
617        if self.1 > other.1 {
618            Ordering::Greater
619        } else if self.1 < other.1 {
620            Ordering::Less
621        } else {
622            if self.0 > other.0 {
623                Ordering::Greater
624            } else if self.0 < other.0 {
625                Ordering::Less
626            } else {
627                Ordering::Equal
628            }
629        }
630    }
631}
632
633impl Rectangle {
634    /// 构建新的矩形结构,并且保证构建出的矩形x/y在左上角,w/h大于等于0.
635    ///
636    /// # Arguments
637    ///
638    /// * `x`: 起始x坐标。
639    /// * `y`: 起始y坐标。
640    /// * `w`: 起始宽度,可以小于零。用于兼容鼠标向任意方向拖拽的场景。
641    /// * `h`: 起始高度,可以小于零。用于兼容鼠标向任意方向拖拽的场景。
642    ///
643    /// returns: Rectangle
644    ///
645    /// # Examples
646    ///
647    /// ```
648    ///
649    /// ```
650    pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self {
651        if w < 0 && h < 0 {
652            Rectangle(x + w, y + h, -w, -h)
653        } else if w < 0 {
654            Rectangle(x + w, y, -w, h)
655        } else if h < 0 {
656            Rectangle(x, y + h, w, -h)
657        } else {
658            Rectangle(x, y, w, h)
659        }
660    }
661
662    /// 构建一个空矩形。
663    pub fn zero() -> Self {
664        Rectangle::new(0, 0, 0, 0)
665    }
666
667    pub fn replace(&mut self, x: i32, y: i32, w: i32, h: i32) {
668        self.0 = x;
669        self.1 = y;
670        self.2 = w;
671        self.3 = h;
672    }
673
674    pub fn is_below(&self, another: &Self) -> bool {
675        self.1 > another.1 + another.3
676    }
677
678    pub fn tup(&self) -> (i32, i32, i32, i32) {
679        (self.0, self.1, self.2, self.3)
680    }
681
682    // /// 获得当前矩形左上角和右下角的坐标。
683    // pub(crate) fn corner(&self) -> (ClickPoint, ClickPoint) {
684    //     (ClickPoint::new(self.0, self.1), ClickPoint::new(self.0 + self.2, self.1 + self.3))
685    // }
686
687    /// 获得当前矩形左上角和右下角的空矩形。
688    pub fn corner_rect(&self) -> (Self, Self) {
689        (ClickPoint::new(self.0, self.1).as_rect(), ClickPoint::new(self.0 + self.2, self.1 + self.3).as_rect())
690    }
691
692    // /// 相对于当前矩形水平向左移动。
693    // ///
694    // /// # Arguments
695    // ///
696    // /// * `offset_x`: 相对位移量。
697    // ///
698    // /// returns: Rectangle 返回新的矩形。
699    // ///
700    // /// # Examples
701    // ///
702    // /// ```
703    // ///
704    // /// ```
705    // pub fn to_left(&self, offset_x: i32) -> Rectangle {
706    //     Rectangle::new(self.0 - offset_x, self.1, self.2, self.3)
707    // }
708
709    // /// 水平向右增加宽度。
710    // ///
711    // /// # Arguments
712    // ///
713    // /// * `add_width`:
714    // ///
715    // /// returns: ()
716    // ///
717    // /// # Examples
718    // ///
719    // /// ```
720    // ///
721    // /// ```
722    // pub fn stretch_to_right(&mut self, add_width: i32) {
723    //     self.2 += add_width;
724    // }
725
726    /// 水平向左增加宽度。
727    ///
728    /// # Arguments
729    ///
730    /// * `add_width`:
731    ///
732    /// returns: ()
733    ///
734    /// # Examples
735    ///
736    /// ```
737    ///
738    /// ```
739    pub fn stretch_to_left(&mut self, add_width: i32) {
740        self.0 -= add_width;
741        self.2 += add_width;
742    }
743
744    /// 检测当前矩形是否与另一个矩形相交。
745    ///
746    /// # Arguments
747    ///
748    /// * `another`:
749    ///
750    /// returns: bool
751    ///
752    /// # Examples
753    ///
754    /// ```
755    ///
756    /// ```
757    pub fn intersects(&self, another: &Self) -> bool {
758        self.0 < another.0 + another.2 &&
759        self.0 + self.2 > another.0 &&
760        self.1 < another.1 + another.3 &&
761        self.1 + self.3 > another.1
762    }
763
764    // /// 将矩形限制到可绘制区域内。
765    // ///
766    // /// # Arguments
767    // ///
768    // /// * `panel_width`:
769    // /// * `panel_height`:
770    // ///
771    // /// returns: ()
772    // ///
773    // /// # Examples
774    // ///
775    // /// ```
776    // ///
777    // /// ```
778    // pub fn align(mut self, panel_width: i32, panel_height: i32) -> Self {
779    //     if self.0 < PADDING.left {
780    //         self.0 = PADDING.left;
781    //     }
782    //     if self.1 < PADDING.top {
783    //         self.1 = PADDING.top;
784    //     }
785    //     if self.2 > panel_width - PADDING.right {
786    //         self.2 = panel_width - PADDING.right;
787    //     }
788    //     if self.3 > panel_height - PADDING.bottom {
789    //         self.3 = panel_height - PADDING.bottom;
790    //     }
791    //     self
792    // }
793}
794
795/// 鼠标点击位置的坐标信息和数据片段索引信息。
796#[derive(Debug, Clone, Copy)]
797pub(crate) struct ClickPoint {
798    pub x: i32,
799    pub y: i32,
800    /// 点所在分片在数据段中的索引号。
801    pub p_i: usize,
802    /// 点所在字符在分片中的索引号。
803    pub c_i: usize,
804}
805
806impl ClickPoint {
807
808    pub fn new(x: i32, y: i32) -> ClickPoint {
809        Self {x, y, p_i: 0, c_i: 0}
810    }
811
812    /// 以一个点构建一个0宽高的举行结构。
813    pub fn as_rect(&self) -> Rectangle {
814        Rectangle::new(self.x, self.y, 0, 0)
815    }
816
817    /// 依据两个点构建新的矩形结构,无需考虑两点之间的相对位置,构建出的矩形x/y始终代表左上角坐标,w/h始终大于等于0。
818    ///
819    /// # Arguments
820    ///
821    /// * `to_point`: 另一个点。
822    ///
823    /// returns: Rectangle
824    ///
825    /// # Examples
826    ///
827    /// ```
828    /// ```
829    pub fn to_rect(&self, to_point: &Self) -> Rectangle {
830        Rectangle::new(self.x, self.y, to_point.x - self.x, to_point.y - self.y)
831    }
832
833    pub fn align(&mut self, panel_width: i32, panel_height: i32, scroll_y: i32) {
834        if self.x < PADDING.left {
835            self.x = PADDING.left;
836            self.p_i = 0;
837            self.c_i = 0;
838        }
839        if self.y < PADDING.top {
840            self.y = PADDING.top;
841        }
842        if self.x > panel_width - PADDING.right {
843            self.x = panel_width - PADDING.right;
844        }
845        if self.y > panel_height + scroll_y - PADDING.bottom {
846            self.y = panel_height + scroll_y - PADDING.bottom;
847        }
848    }
849}
850
851/// 同一行内多个分片之间共享的信息。通过Rc<RefCell<ThroughLine>>进行链接。
852#[derive(Debug, Clone)]
853pub(crate) struct ThroughLine {
854    pub max_h: i32,
855    pub ys: Vec<Weak<RwLock<LinePiece>>>,
856    pub exist_image: bool,
857}
858
859impl Default for ThroughLine {
860    fn default() -> Self {
861        ThroughLine {
862            max_h: 1,
863            ys: vec![],
864            exist_image: false,
865        }
866    }
867}
868
869impl ThroughLine {
870
871    pub fn new(max_h: i32, exist_image: bool) -> Arc<RwLock<ThroughLine>> {
872        Arc::new(RwLock::new(ThroughLine { max_h, exist_image, ys: vec![] }))
873    }
874
875    /// 如果传入的高度大于已记录高度,则替换为传入高度。
876    ///
877    /// # Arguments
878    ///
879    /// * `max_h`:
880    ///
881    /// returns: &mut ThroughLine
882    ///
883    /// # Examples
884    ///
885    /// ```
886    ///
887    /// ```
888    pub fn set_max_h(&mut self, max_h: i32) -> &mut Self {
889        if max_h > self.max_h {
890            self.max_h = max_h;
891        }
892        self
893    }
894
895    pub fn add_piece(&mut self, lp: Arc<RwLock<LinePiece>>) -> &mut Self {
896        self.ys.push(Arc::downgrade(&lp));
897        self
898    }
899
900    /// 获取前一个分片的链接,或者创建新的链接。
901    ///
902    /// # Arguments
903    ///
904    /// * `x_ref`: 绘制起点的x坐标。
905    /// * `start_x`: 当前分片的绘制起点。
906    /// * `current_line_height`: 当前分片行高,或者图片高度。如果这个高度大于链接中记录的高度,则替换为新的高度。
907    /// * `last_piece`: 前一个分片。
908    /// * `image`: 当前分片是否为图形。
909    ///
910    /// returns: Rc<RefCell<ThroughLine>> 若当前分片是所在行的第一个分片则创新的链接,否则返回前一个分片的链接。
911    ///
912    /// # Examples
913    ///
914    /// ```
915    ///
916    /// ```
917    pub fn create_or_update(x_ref: i32, start_x: i32, current_line_height: i32, last_piece: Arc<RwLock<LinePiece>>, image: bool) -> Arc<RwLock<ThroughLine>> {
918        if start_x == x_ref {
919            ThroughLine::new(current_line_height, image)
920        } else {
921            if image {
922                last_piece.write().through_line.write().exist_image = true;
923            }
924            last_piece.read().through_line.clone()
925        }
926    }
927}
928
929/// 可视内容在面板容器中的边界空白。
930#[derive(Debug, Clone, Default)]
931pub(crate) struct Padding {
932    pub left: i32,
933    pub top: i32,
934    pub right: i32,
935    pub bottom: i32,
936}
937
938/// 单行文本的渲染参数,通过试算得到。
939/// 一个大段文本在试算过程中,可能被拆分为多个适配当前窗口宽度的单行文本片段,用于简化绘制过程的运算。
940#[derive(Debug, Clone)]
941pub(crate) struct LinePiece {
942    pub line: String,
943    /// 起点x坐标
944    pub x: i32,
945    /// 起点y坐标
946    pub y: i32,
947    /// 分片宽度,小于等于行宽
948    pub w: i32,
949    /// 行高
950    pub h: i32,
951    /// 虚拟行高顶部y坐标
952    pub top_y: i32,
953    /// 额外的行间距。
954    /// 目前默认值为0,未产生实际影响。
955    pub spacing: i32,
956    /// 建议下一个数据分片绘制起点x坐标
957    pub next_x: i32,
958    /// 建议下一个数据分片绘制起点y坐标
959    pub next_y: i32,
960
961    /// 字体渲染高度,小于等于行高。
962    pub font_height: i32,
963    /// 当行高与字体渲染高度不一致时,需要通过y轴偏移量矫正渲染文字的位置。
964    pub text_offset: i32,
965    /// 当行高与字体渲染高度不一致时,需要通过y轴偏移量矫正背景色渲染位置。
966    pub bg_offset: i32,
967
968    /// 在同一行内有多个数据分片的情况下, 跟踪行高信息。每次新增行时,第一个分片需要创建新的对象;在同一行其他分片只需要引用第一个分片的对象即可。
969    pub through_line: Arc<RwLock<ThroughLine>>,
970
971    /// 选中文字相对于当前片段的起始到结束位置,位置索引是以`unicode`字符计算的。
972    pub selected_range: Arc<RwLock<Option<(usize, usize)>>>,
973
974    pub font: Font,
975    pub font_size: i32,
976
977    /// 分片所在数据段的边界数据引用。
978    pub rd_bounds: Arc<RwLock<(i32, i32, i32, i32)>>,
979}
980
981impl LinePiece {
982    pub fn new(line: String, x: i32, y: i32, w: i32, h: i32, top_y: i32, spacing: i32, next_x: i32, next_y: i32, font_height: i32, font: Font, font_size: i32, through_line: Arc<RwLock<ThroughLine>>, rd_bounds: Arc<RwLock<(i32, i32, i32, i32)>>) -> Arc<RwLock<LinePiece>> {
983        let new_piece = Arc::new(RwLock::new(Self {
984            line,
985            x,
986            y,
987            w,
988            h,
989            top_y,
990            spacing,
991            next_x,
992            next_y,
993            font_height,
994            text_offset: 0,
995            bg_offset: 0,
996            through_line: through_line.clone(),
997            selected_range: Arc::new(RwLock::new(None)),
998            font,
999            font_size,
1000            rd_bounds
1001        }));
1002        through_line.write().add_piece(new_piece.clone());
1003        new_piece
1004    }
1005
1006    pub fn init_piece(text_size: i32) -> Arc<RwLock<LinePiece>> {
1007        let through_line = Arc::new(RwLock::new(Default::default()));
1008        let init_piece = Arc::new(RwLock::new(Self {
1009            line: "".to_string(),
1010            x: PADDING.left,
1011            y: PADDING.top,
1012            w: 0,
1013            h: (text_size as f32 * LINE_HEIGHT_FACTOR).ceil() as i32,
1014            top_y: PADDING.top,
1015            spacing: 0,
1016            next_x: PADDING.left,
1017            next_y: PADDING.top,
1018            font_height: 1,
1019            text_offset: 0,
1020            bg_offset: 0,
1021            through_line: through_line.clone(),
1022            selected_range: Arc::new(RwLock::new(None)),
1023            font: Font::Helvetica,
1024            font_size: DEFAULT_FONT_SIZE,
1025            rd_bounds: Arc::new(RwLock::new((PADDING.top, PADDING.top + (text_size as f32 * LINE_HEIGHT_FACTOR).ceil() as i32, PADDING.left, PADDING.left))),
1026        }));
1027        through_line.write().add_piece(init_piece.clone());
1028        init_piece
1029    }
1030
1031    pub fn select_from(&self, from: usize) {
1032        self.selected_range.write().replace((from, self.line.chars().count()));
1033    }
1034
1035    pub fn select_to(&self, to: usize) {
1036        self.selected_range.write().replace((0, to));
1037    }
1038
1039    pub fn select_range(&self, from: usize, to: usize) {
1040        self.selected_range.write().replace((from, to));
1041    }
1042
1043    pub fn deselect(&self) {
1044        self.selected_range.write().take();
1045    }
1046
1047    pub fn select_all(&self) {
1048        self.selected_range.write().replace((0, self.line.chars().count()));
1049    }
1050
1051    pub fn copy_selection(&self, selection: &mut String) {
1052        if let Some((from, to)) = *self.selected_range.read() {
1053            self.line.chars().skip(from).take(max(to, from) - from).for_each(|c| {
1054                selection.push(c);
1055            });
1056        }
1057    }
1058
1059    ///
1060    ///
1061    /// # Arguments
1062    ///
1063    /// * `offset_y`:
1064    ///
1065    /// returns: Rectangle
1066    ///
1067    /// # Examples
1068    ///
1069    /// ```
1070    ///
1071    /// ```
1072    pub fn rect(&self, offset_x: i32, offset_y: i32) -> Rectangle {
1073        Rectangle::new(self.x + offset_x, self.y + offset_y, self.w, self.h)
1074    }
1075
1076    /// 获取当前片段右侧的虚拟光标,虚拟光标是一个零宽度的片段。
1077    pub fn get_cursor(&self) -> LinePiece {
1078        // 为光标信息附加文本末尾的字符,用于辅助检测换行逻辑,控制行高。
1079        let line = if let Some(c) = self.line.chars().last() {
1080            c.to_string()
1081        } else {
1082            String::new()
1083        };
1084        Self {
1085            line,
1086            x: self.next_x,
1087            y: self.next_y,
1088            w: 0,
1089            h: self.h,
1090            top_y: if self.next_y > self.y { self.next_y } else { self.top_y },
1091            spacing: 0,
1092            next_x: self.next_x,
1093            next_y: self.next_y,
1094            font_height: self.font_height,
1095            text_offset: 0,
1096            bg_offset: 0,
1097            through_line: self.through_line.clone(),
1098            selected_range: Arc::new(RwLock::new(None)),
1099            font: self.font,
1100            font_size: self.font_size,
1101            rd_bounds: Arc::new(RwLock::new((self.next_y, self.next_y + self.h, self.next_x, self.next_x))),
1102        }
1103    }
1104
1105    // /// 相对移动虚拟光标。
1106    // ///
1107    // /// # Arguments
1108    // ///
1109    // /// * `offset_x`: 水平移动。
1110    // /// * `offset_y`: 垂直移动。
1111    // ///
1112    // /// returns: ()
1113    // ///
1114    // /// # Examples
1115    // ///
1116    // /// ```
1117    // ///
1118    // /// ```
1119    // pub fn move_cursor(&mut self, offset_x: i32, offset_y: i32) {
1120    //     self.x += offset_x;
1121    //     self.y += offset_y;
1122    //     self.next_x += offset_x;
1123    //     self.next_y += offset_y;
1124    // }
1125
1126    /// 绝对移动虚拟光标。
1127    ///
1128    /// # Arguments
1129    ///
1130    /// * `x`: 水平移动到x位置。
1131    /// * `y`: 垂直移动到y位置。
1132    ///
1133    /// returns: ()
1134    ///
1135    /// # Examples
1136    ///
1137    /// ```
1138    ///
1139    /// ```
1140    pub fn move_cursor_to(&mut self, x: i32, y: i32) {
1141        self.x = x;
1142        self.y = y;
1143        self.next_x = x;
1144        self.next_y = y;
1145    }
1146
1147    pub fn calc_offset(&mut self) {
1148        (self.text_offset, _) = calc_v_center_offset(self.h, self.font_size);
1149        (self.bg_offset, _) = calc_v_center_offset(self.h, self.font_height);
1150        // debug!("text_offset: {}, bg_offset: {}, h: {}, font_size: {}, font_height: {}, line: {}", self.text_offset, self.bg_offset, self.h, self.font_size, self.font_height, self.line);
1151
1152        #[cfg(target_os = "windows")]
1153        {
1154            self.text_offset = (self.text_offset as f32  / 2.0f32).floor() as i32;
1155        }
1156    }
1157}
1158
1159pub(crate) trait LinedData {
1160    /// 设置绘制区域顶部和底部边界y坐标,以及起始x坐标。
1161    ///
1162    /// # Arguments
1163    ///
1164    /// * `start_point`:
1165    ///
1166    /// returns: ()
1167    ///
1168    /// # Examples
1169    ///
1170    /// ```
1171    ///
1172    /// ```
1173    fn set_v_bounds(&mut self, top_y: i32, bottom_y: i32, start_x: i32, end_x: i32);
1174
1175    /// 表明是否纯文本数据段。
1176    fn is_text_data(&self) -> bool;
1177
1178    /// 是否可点击互动。
1179    fn clickable(&self) -> bool;
1180
1181    /// 设置可点击互动。
1182    fn set_clickable(&mut self, clickable: bool);
1183
1184    /// 是否已失效
1185    fn is_expired(&self) -> bool;
1186
1187    /// 设置失效状态。
1188    ///
1189    /// # Arguments
1190    ///
1191    /// * `expire`: 指定是否失效。
1192    ///
1193    /// returns: ()
1194    ///
1195    /// # Examples
1196    ///
1197    /// ```
1198    ///
1199    /// ```
1200    fn set_expire(&mut self, expire: bool);
1201
1202    /// 设置文本数据。
1203    ///
1204    /// # Arguments
1205    ///
1206    /// * `text_data`:
1207    ///
1208    /// returns: ()
1209    ///
1210    /// # Examples
1211    ///
1212    /// ```
1213    ///
1214    /// ```
1215    fn set_text_data(&mut self, text_data: &str);
1216
1217    /// 设置二
1218    ///
1219    /// # Arguments
1220    ///
1221    /// * `binary_data`:
1222    ///
1223    /// returns: ()
1224    ///
1225    /// # Examples
1226    ///
1227    /// ```
1228    ///
1229    /// ```
1230    fn set_binary_data(&mut self, binary_data: Vec<u8>);
1231
1232
1233    /// 检测是否位于可视窗口范围内。
1234    ///
1235    /// # Arguments
1236    ///
1237    /// * `top_y`: 面板顶部y轴偏移量。
1238    /// * `bottom_y`: 面板底部y轴偏移量。
1239    ///
1240    /// returns: bool
1241    ///
1242    /// # Examples
1243    ///
1244    /// ```
1245    ///
1246    /// ```
1247    fn is_visible(&self, top_y: i32, bottom_y: i32) -> bool;
1248
1249
1250    /// 绘制内容。
1251    ///
1252    /// # Arguments
1253    ///
1254    /// * `offset_y`: 面板相对于数据的y轴偏移量。
1255    /// * `blink_state`: 面板范围内的闪烁状态。
1256    ///
1257    /// returns: ()
1258    ///
1259    /// # Examples
1260    ///
1261    /// ```
1262    ///
1263    /// ```
1264    fn draw(&self, offset_y: i32, blink_state: &BlinkState);
1265
1266    /// 试算当前内容绘制后所占高度信息。
1267    /// 试算功能自动处理文本超宽时截断换行的逻辑。
1268    ///
1269    /// # Arguments
1270    ///
1271    /// * `last_piece`: 前一个数据片段,用于计算当前数据段的绘制坐标。每个数据段和数据片段都是按照缓存数据的顺序依次计算得到。
1272    /// * `max_width`: 可视区域最大宽度,不含padding宽度。
1273    ///
1274    /// returns: ()
1275    ///
1276    /// # Examples
1277    ///
1278    /// ```
1279    ///
1280    /// ```
1281    fn estimate(&mut self, last_piece: Arc<RwLock<LinePiece>>, max_width: i32, basic_char: char) -> Arc<RwLock<LinePiece>>;
1282
1283}
1284
1285/// 数据段类型,当前支持文本和图片两种。
1286#[derive(Clone, Debug, PartialEq, Serialize)]
1287pub enum DataType {
1288    Text,
1289    Image,
1290}
1291
1292#[derive(Clone, Debug, Default, Serialize)]
1293pub struct ActionItem {
1294    pub desc: String,
1295    pub cmd: String,
1296}
1297
1298impl ActionItem {
1299    pub fn new(desc: &str, cmd: &str) -> Self {
1300        Self {
1301            desc: desc.to_string(),
1302            cmd: cmd.to_string(),
1303        }
1304    }
1305}
1306
1307/// 互动行为定义。
1308#[derive(Clone, Debug, Default, Serialize)]
1309pub struct Action {
1310    /// 互动操作提示信息,当鼠标指向时会弹出该提示,类似于`HTML`标签的`title`属性。
1311    pub title: String,
1312    /// 互动操作具体类型,由上层应用定义其具体含义。
1313    pub kind: u8,
1314    /// 互动操作的动作列表,当鼠标点击时弹出该列表,列表中每个元素的格式为(动作描述,动作指令)。
1315    /// 弹出列表中可见的是动作描述,当用户选择某项动作时将反馈动作指令给上层应用控制器。
1316    pub items: Vec<ActionItem>,
1317    /// 用户选择的动作指令。
1318    pub active: Option<String>,
1319    /// 动作所属类别名称。
1320    pub category: Option<String>,
1321}
1322
1323/// 用户提供的数据段结构。。
1324#[derive(Clone, Debug)]
1325pub struct UserData {
1326    /// 数据ID,在初始化新实例时可随意赋值。当源自RichData时,为RichData的ID值。
1327    pub id: i64,
1328    pub text: String,
1329    pub font: Font,
1330    pub font_size: i32,
1331    pub fg_color: Color,
1332    pub bg_color: Option<Color>,
1333    pub underline: bool,
1334    /// 前景色序号,从1到8对应ANSI/CSI/SGR的黑、红、绿、黄、蓝、品红、青、白的颜色序列。
1335    pub fg_color_index: u8,
1336    /// 背景色序号,从1到8对应ANSI/CSI/SGR的黑、红、绿、黄、蓝、品红、青、白的颜色序列。
1337    pub bg_color_index: u8,
1338    /// 显示效果是否加强,对应与ANSI/CSI的`0`和`1`参数。
1339    pub strong: bool,
1340    /// 文字大小编号,从1到7对应MXP协议中的SMALL、H6、H5、H4、H3、H2、H1。
1341    pub font_size_index: u8,
1342    pub clickable: bool,
1343    pub expired: bool,
1344    pub blink: bool,
1345    pub disabled: bool,
1346    pub strike_through: bool,
1347    pub data_type: DataType,
1348    pub image: Option<RgbImage>,
1349    /// 原始宽度
1350    pub image_width: i32,
1351    /// 原始高度
1352    pub image_height: i32,
1353    /// 希望绘制的目标宽度
1354    pub image_target_width: i32,
1355    /// 希望绘制的目标高度
1356    pub image_target_height: i32,
1357    /// 图片来源地址
1358    pub image_src_url: Option<String>,
1359    /// 图片文件临时保存路径。
1360    pub image_file_path: Option<PathBuf>,
1361    pub(crate) custom_font_text: bool,
1362    pub custom_font_color: bool,
1363    /// 互动属性。
1364    pub action: Option<Action>,
1365}
1366
1367impl Serialize for UserData {
1368    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
1369        let mut state = serializer.serialize_struct("UserData", 26).unwrap();
1370        state.serialize_field("id", &self.id).unwrap();
1371        state.serialize_field("text", &self.text).unwrap();
1372        state.serialize_field("font", &format!("{}({})", &self.font.get_name(), &self.font.bits())).unwrap();
1373        state.serialize_field("font_size", &self.font_size).unwrap();
1374        state.serialize_field("fg_color", &self.fg_color.to_hex_str()).unwrap();
1375        state.serialize_field("bg_color", &self.bg_color.map(|c| c.to_hex_str())).unwrap();
1376        state.serialize_field("underline", &self.underline).unwrap();
1377        state.serialize_field("fg_color_index", &self.fg_color_index).unwrap();
1378        state.serialize_field("bg_color_index", &self.bg_color_index).unwrap();
1379        state.serialize_field("strong", &self.strong).unwrap();
1380        state.serialize_field("font_size_index", &self.font_size_index).unwrap();
1381        state.serialize_field("clickable", &self.clickable).unwrap();
1382        state.serialize_field("expired", &self.expired).unwrap();
1383        state.serialize_field("blink", &self.blink).unwrap();
1384        state.serialize_field("disabled", &self.disabled).unwrap();
1385        state.serialize_field("strike_through", &self.strike_through).unwrap();
1386        state.serialize_field("data_type", &self.data_type).unwrap();
1387        state.serialize_field("image", &self.image.as_ref().map(|_| "image")).unwrap();
1388        state.serialize_field("image_width", &self.image_width).unwrap();
1389        state.serialize_field("image_height", &self.image_height).unwrap();
1390        state.serialize_field("image_target_width", &self.image_target_width).unwrap();
1391        state.serialize_field("image_target_height", &self.image_target_height).unwrap();
1392        state.serialize_field("image_src_url", &self.image_src_url).unwrap();
1393        state.serialize_field("image_file_path", &self.image_file_path).unwrap();
1394        state.serialize_field("custom_font_text", &self.custom_font_text).unwrap();
1395        state.serialize_field("custom_font_color", &self.custom_font_color).unwrap();
1396        state.serialize_field("action", &self.action.as_ref().map(|a| a)).unwrap();
1397        state.end()
1398    }
1399}
1400
1401impl From<&RichData> for UserData {
1402    fn from(data: &RichData) -> Self {
1403        Self {
1404            id: data.id,
1405            text: data.text.clone(),
1406            font: data.font,
1407            font_size: data.font_size,
1408            fg_color: data.fg_color,
1409            bg_color: data.bg_color.clone(),
1410            underline: data.underline,
1411            fg_color_index: 0,
1412            bg_color_index: 0,
1413            strong: false,
1414            font_size_index: 0,
1415            clickable: data.clickable,
1416            expired: data.expired,
1417            blink: data.blink,
1418            disabled: data.disabled,
1419            strike_through: data.strike_through,
1420            data_type: data.data_type.clone(),
1421            image: None,
1422            image_width: data.image_width,
1423            image_height: data.image_height,
1424            image_target_width: data.image_target_width,
1425            image_target_height: data.image_target_height,
1426            image_src_url: data.image_src_url.clone(),
1427            image_file_path: data.image_file_path.clone(),
1428            custom_font_text: false,
1429            custom_font_color: false,
1430            action: data.action.clone(),
1431        }
1432    }
1433}
1434
1435impl UserData {
1436    pub fn new_text(text: String) -> Self {
1437        Self {
1438            id: YitIdHelper::next_id(),
1439            text,
1440            font: Font::Helvetica,
1441            font_size: DEFAULT_FONT_SIZE,
1442            fg_color: Color::White,
1443            bg_color: None,
1444            underline: false,
1445            fg_color_index: 0,
1446            bg_color_index: 0,
1447            strong: false,
1448            font_size_index: 0,
1449            clickable: false,
1450            expired: false,
1451            blink: false,
1452            disabled: false,
1453            strike_through: false,
1454            data_type: DataType::Text,
1455            image: None,
1456            image_width: 0,
1457            image_height: 0,
1458            image_target_width: 0,
1459            image_target_height: 0,
1460            image_src_url: None,
1461            image_file_path: None,
1462            custom_font_text: false,
1463            custom_font_color: false,
1464            action: None,
1465        }
1466    }
1467
1468    pub fn new_text_with_id(id: i64, text: String) -> Self {
1469        Self {
1470            id,
1471            text,
1472            font: Font::Helvetica,
1473            font_size: DEFAULT_FONT_SIZE,
1474            fg_color: Color::White,
1475            bg_color: None,
1476            underline: false,
1477            fg_color_index: 0,
1478            bg_color_index: 0,
1479            strong: false,
1480            font_size_index: 0,
1481            clickable: false,
1482            expired: false,
1483            blink: false,
1484            disabled: false,
1485            strike_through: false,
1486            data_type: DataType::Text,
1487            image: None,
1488            image_width: 0,
1489            image_height: 0,
1490            image_target_width: 0,
1491            image_target_height: 0,
1492            image_src_url: None,
1493            image_file_path: None,
1494            custom_font_text: false,
1495            custom_font_color: false,
1496            action: None,
1497        }
1498    }
1499
1500    /// 创建新的图形类型的数据段。
1501    /// 如果传入的图形源自`SvgImage`,则必须在调用本方法之前首先执行`SvgImage::normalize()`方法进行初始化。
1502    ///
1503    /// # Arguments
1504    ///
1505    /// * `image`: RGB图像对象。
1506    /// * `original_width`: 原始宽度。
1507    /// * `original_height`: 原始高度。
1508    /// * `target_width`: 目标宽度,可能与原始宽度不同。
1509    /// * `target_height`: 目标高度,可能与原始高度不同。
1510    /// * `src`: 图像来源地址。
1511    ///
1512    /// returns: UserData
1513    ///
1514    /// # Examples
1515    ///
1516    /// ```
1517    /// use fltk::image::{SvgImage};
1518    /// use fltk::prelude::ImageExt;
1519    /// use fltkrs_richdisplay::UserData;
1520    ///
1521    /// let mut svg = SvgImage::load("res/test.svg").unwrap();
1522    /// svg.normalize();
1523    /// let image = svg.to_rgb().unwrap();
1524    /// let _data = UserData::new_image(image, 100, 100, 100, 100, Some("res/test.svg".to_string()));
1525    /// ```
1526    pub fn new_image(image: RgbImage, origin_width: i32, origin_height: i32, target_width: i32, target_height: i32, src: Option<String>) -> Self {
1527        Self {
1528            id: YitIdHelper::next_id(),
1529            text: String::new(),
1530            font: Font::Helvetica,
1531            font_size: DEFAULT_FONT_SIZE,
1532            fg_color: Color::White,
1533            bg_color: None,
1534            underline: false,
1535            fg_color_index: 0,
1536            bg_color_index: 0,
1537            strong: false,
1538            font_size_index: 0,
1539            clickable: false,
1540            expired: false,
1541            blink: false,
1542            disabled: false,
1543            strike_through: false,
1544            data_type: DataType::Image,
1545            image: Some(image),
1546            image_width: origin_width,
1547            image_height: origin_height,
1548            image_target_width: target_width,
1549            image_target_height: target_height,
1550            image_src_url: src,
1551            image_file_path: None,
1552            custom_font_text: false,
1553            custom_font_color: false,
1554            action: None,
1555        }
1556    }
1557
1558    pub fn set_font_and_size(mut self, font: Font, size: i32) -> Self {
1559        self.font = font;
1560        self.font_size = size;
1561        self.custom_font_text = true;
1562        self
1563    }
1564
1565    /// 设置字体和大小,同时确认自定义字体标记。非流式调用接口。
1566    ///
1567    /// # Arguments
1568    ///
1569    /// * `font`:
1570    /// * `size`:
1571    ///
1572    /// returns: ()
1573    ///
1574    /// # Examples
1575    ///
1576    /// ```
1577    ///
1578    /// ```
1579    pub fn set_font_and_size2(&mut self, font: Font, size: i32) {
1580        self.font = font;
1581        self.font_size = size;
1582        self.custom_font_text = true;
1583    }
1584
1585    pub fn set_font_size_index(mut self, index: u8) -> Self {
1586        self.font_size_index = index;
1587        self
1588    }
1589
1590    pub fn set_fg_color(mut self, fg_color: Color) -> Self {
1591        self.fg_color = fg_color;
1592        self.custom_font_color = true;
1593        self
1594    }
1595
1596    pub fn set_fg_color_index(mut self, index: u8) -> Self {
1597        self.fg_color_index = index;
1598        self
1599    }
1600
1601    pub fn set_bg_color(mut self, bg_color: Option<Color>) -> Self {
1602        self.bg_color = bg_color;
1603        self
1604    }
1605
1606    pub fn set_bg_color_index(mut self, index: u8) -> Self {
1607        self.bg_color_index = index;
1608        self
1609    }
1610
1611    pub fn set_strong(mut self, strong: bool) -> Self {
1612        self.strong = strong;
1613        self
1614    }
1615
1616    pub fn set_underline(mut self, u: bool) -> Self {
1617        self.underline = u;
1618        self
1619    }
1620
1621    pub fn set_clickable(mut self, clickable: bool) -> Self {
1622        self.clickable = clickable;
1623        self
1624    }
1625
1626    pub fn set_blink(mut self, blink: bool) -> Self {
1627        self.blink = blink;
1628        self
1629    }
1630
1631    pub fn set_disabled(mut self, disabled: bool) -> Self {
1632        self.disabled = disabled;
1633        self
1634    }
1635
1636    /// 设置数据段互动行为。
1637    ///
1638    /// # Arguments
1639    ///
1640    /// * `action`: 互动操作。
1641    ///
1642    /// returns: UserData
1643    ///
1644    /// # Examples
1645    ///
1646    /// ```
1647    ///
1648    /// ```
1649    pub fn set_action(mut self, action: Action) -> Self {
1650        self.action = Some(action);
1651        self.clickable = true;
1652        self.underline = true;
1653        self.expired = false;
1654        self
1655    }
1656
1657    /// 更改当前数据段的互动行为。
1658    ///
1659    /// # Arguments
1660    ///
1661    /// * `action`: 互动操作。
1662    ///
1663    /// returns: ()
1664    ///
1665    /// # Examples
1666    ///
1667    /// ```
1668    ///
1669    /// ```
1670    pub fn change_action(&mut self, action: Option<Action>) {
1671        if action.is_some() {
1672            self.action = action;
1673            self.clickable = true;
1674            self.underline = true;
1675            self.expired = false;
1676        } else {
1677            self.action = None;
1678            self.clickable = false;
1679            self.underline = false;
1680            self.expired = true;
1681        }
1682    }
1683
1684    /// 为图片设置简短的文字描述,居中显示。
1685    /// 若文字较长请使用换行符`'\n'`进行换行,避免文字超出图片边界。
1686    ///
1687    /// # Arguments
1688    ///
1689    /// * `text`:
1690    ///
1691    /// returns: UserData
1692    ///
1693    /// # Examples
1694    ///
1695    /// ```
1696    ///
1697    /// ```
1698    pub fn set_text(mut self, text: String) -> Self {
1699        self.text = text;
1700        self
1701    }
1702
1703    pub fn set_image_file_path(mut self, path: Option<PathBuf>) -> Self {
1704        self.image_file_path = path;
1705        self
1706    }
1707}
1708
1709
1710/// 计算两个重叠垂线居中对齐后,短线相对于长线的上端和下端的偏移量。
1711///
1712/// # Arguments
1713///
1714/// * `line_height`:
1715/// * `font_height`:
1716///
1717/// returns: (i32, i32)
1718///
1719/// # Examples
1720///
1721/// ```
1722///
1723/// ```
1724pub(crate) fn calc_v_center_offset(line_height: i32, font_height: i32) -> (i32, i32) {
1725    let up = (line_height - font_height) / 2;
1726    let down = (line_height + font_height) / 2;
1727    (up, down)
1728}
1729
1730/// 检测鼠标是否进入可交互的内容区域中。
1731///
1732/// # Arguments
1733///
1734/// * `clickable_data_rc`:
1735///
1736/// returns: (bool, usize) 返回元组(是否进入, 数据段序号)。
1737///
1738/// # Examples
1739///
1740/// ```
1741///
1742/// ```
1743pub(crate) fn mouse_enter(clickable_data_rc: Arc<RwLock<HashMap<Rectangle, usize>>>) -> (bool, usize) {
1744    for (area, idx) in clickable_data_rc.read().iter() {
1745        let (x, y, w, h) = area.tup();
1746        if app::event_inside(x, y, w, h) {
1747            return (true, *idx);
1748        }
1749    }
1750    return (false, 0);
1751}
1752
1753/// 更新数据内容的属性。用于用户互动操作反馈。
1754///
1755/// # Arguments
1756///
1757/// * `options`:
1758/// * `rd`:
1759///
1760/// returns: ()
1761///
1762/// # Examples
1763///
1764/// ```
1765///
1766/// ```
1767pub(crate) fn update_data_properties(options: RichDataOptions, rd: &mut RichData) {
1768    if let Some(clickable) = options.clickable {
1769        rd.clickable = clickable;
1770        if !clickable {
1771            draw::set_cursor(Cursor::Default);
1772        }
1773    }
1774    if let Some(underline) = options.underline {
1775        rd.underline = underline;
1776    }
1777    if let Some(expired) = options.expired {
1778        rd.expired = expired;
1779    }
1780    if let Some(text) = options.text {
1781        rd.text = text;
1782    }
1783    if let Some(fg_color) = options.fg_color {
1784        rd.fg_color = fg_color;
1785    }
1786    if let Some(bg_color) = options.bg_color {
1787        rd.bg_color = Some(bg_color);
1788    }
1789    if let Some(strike_through) = options.strike_through {
1790        rd.strike_through = strike_through;
1791    }
1792    if let Some(blink) = options.blink {
1793        rd.blink = blink;
1794    }
1795    if let Some(image) = options.image {
1796        if let Some(image_color_depth) = options.image_color_depth {
1797            rd.image_color_depth = image_color_depth;
1798        }
1799        if let Some(image_target_width) = options.image_target_width {
1800            rd.image_target_width = image_target_width;
1801        }
1802        if let Some(image_target_height) = options.image_target_height {
1803            rd.image_target_height = image_target_height;
1804        }
1805        if let Some(image_width) = options.image_width {
1806            rd.image_width = image_width;
1807        }
1808        if let Some(image_height) = options.image_height {
1809            rd.image_height = image_height;
1810        }
1811        if rd.image_inactive.is_some() {
1812            let inactive = gray_image(&image, rd.image_width, rd.image_height, rd.image_color_depth);
1813            rd.image_inactive.replace(inactive);
1814        }
1815        rd.image.replace(image);
1816    }
1817
1818    if let Some(image_file_path) = options.image_file_path {
1819        rd.image_file_path.replace(image_file_path);
1820    }
1821    if let Some(action) = options.action {
1822        if action.items.is_empty() {
1823            rd.action = None;
1824        } else {
1825            rd.action.replace(action);
1826        }
1827    }
1828
1829    if let Some(disabled) = options.disabled {
1830        rd.disabled = disabled;
1831
1832        if rd.data_type == DataType::Image {
1833            if rd.disabled && rd.image_inactive.is_none() {
1834                if let Some(ref rgb_data) = rd.image {
1835                    rd.image_inactive.replace(gray_image(rgb_data, rd.image_width, rd.image_height, rd.image_color_depth));
1836                }
1837            }
1838        }
1839    }
1840}
1841
1842/// 禁用数据内容。
1843/// 当前的实现为:图形内容增加灰色遮罩层,文本内容增加删除线。
1844///
1845/// # Arguments
1846///
1847/// * `rd`:
1848///
1849/// returns: ()
1850///
1851/// # Examples
1852///
1853/// ```
1854///
1855/// ```
1856pub(crate) fn disable_data(rd: &mut RichData) {
1857    rd.set_clickable(false);
1858    draw::set_cursor(Cursor::Default);
1859
1860    match rd.data_type {
1861        DataType::Image => {
1862            if rd.image_inactive.is_none() {
1863                if let Some(ref rgb_data) = rd.image {
1864                    rd.image_inactive.replace(gray_image(rgb_data, rd.image_target_width, rd.image_target_height, rd.image_color_depth));
1865                }
1866            }
1867        }
1868        DataType::Text => {
1869            rd.strike_through = true;
1870        }
1871    }
1872}
1873
1874/// 从影像中提取`RGB`数据,不会损失alpha通道数据。若传入`None`则返回一个对应大小且色深为`L8`的黑板。
1875///
1876/// # Arguments
1877///
1878/// * `rgb_image`: RGB影像。
1879/// * `target_width`: 希望的影像宽度,像素数。
1880/// * `target_height`: 希望的影像的高度,像素数。
1881///
1882/// returns: (Option<Vec<u8>>, ColorDepth, i32, i32) 返回(RGB数据, 颜色深度, 影像原宽度, 影像原高度)。
1883///
1884/// # Examples
1885///
1886/// ```
1887///
1888/// ```
1889pub fn image_to_rgb_data(rgb_image: &Option<RgbImage>, target_width: i32, target_height: i32) -> (Option<Vec<u8>>, ColorDepth, i32, i32) {
1890    if let Some(image) = rgb_image {
1891        (Some(image.to_rgb_data()), image.depth(), image.w(), image.h())
1892    } else {
1893        (Some(vec![0u8; target_width as usize * target_height as usize]), ColorDepth::L8, target_width, target_height)
1894    }
1895}
1896
1897/// 依据RGB格式的图片计算其L8格式的灰度数据。
1898///
1899/// # Arguments
1900///
1901/// * `origin_rgb_data`: 原本的RGB格式的图片数据。
1902///
1903/// returns: Option<Vec<u8>> 灰度计算后的图片数据,格式为`L8`或`LA8`。
1904///
1905/// # Examples
1906///
1907/// ```
1908///
1909/// ```
1910pub fn gray_image(rgb_data: &Vec<u8>, w: i32, h: i32, depth: ColorDepth) -> Vec<u8> {
1911    let pixels = w as usize * h as usize;
1912    match depth {
1913        // ColorDepth::L8 => {
1914        //     // 8位灰度,每个像素占据1个字节。
1915        //     let la_data = origin_image.to_rgb_data();
1916        //     let mut gray_data: Vec<u8> = Vec::with_capacity(pixels);
1917        //     for i in 0..pixels {
1918        //         let g = (la_data[i] as f32 / 2.0).floor() as u8;
1919        //         gray_data.push(g);
1920        //     }
1921        //     Some(gray_data)
1922        // }
1923        // ColorDepth::La8 => {
1924        //     // 8位灰度带有alpha通道,每个像素占据2个8位字节,每个像素的第二个字节表示alpha通道值。
1925        //     let la_data = origin_image.to_rgb_data();
1926        //     let mut gray_data: Vec<u8> = Vec::with_capacity(pixels);
1927        //     for i in 0..pixels {
1928        //         let j = i * 2;
1929        //         let c = la_data[j];
1930        //         let g = (c as f32 / 2.0).floor() as u8;
1931        //         gray_data.push(g);
1932        //     }
1933        //     Some(gray_data)
1934        // }
1935
1936        ColorDepth::Rgb8 => {
1937            // 8位RGB色,每个像素占据3个8位字节.
1938            let mut gray_data: Vec<u8> = Vec::with_capacity(pixels);
1939            for i in 0..pixels {
1940                let j = i * 3;
1941                let (r, g, b) = (rgb_data[j], rgb_data[j + 1], rgb_data[j + 2]);
1942                let gray = (306 * r as u32 + 601 * g as u32 + 116 * b as u32) >> 10;
1943                gray_data.push(gray as u8);
1944            }
1945            gray_data
1946        }
1947        ColorDepth::Rgba8 => {
1948            // 8位RGB色带有alpha通道,每个像素占据4个8位字节.每个像素的第四个字节表示alpha通道值。
1949            let mut gray_data: Vec<u8> = Vec::with_capacity(pixels * 2);
1950            for i in 0..pixels {
1951                let j = i * 4;
1952                let (r, g, b) = (rgb_data[j], rgb_data[j + 1], rgb_data[j + 2]);
1953                let gray = (306 * r as u32 + 601 * g as u32 + 116 * b as u32) >> 10;
1954                gray_data.push(gray as u8);
1955                gray_data.push(rgb_data[j + 3]);
1956            }
1957            gray_data
1958        }
1959        ColorDepth::L8 => {
1960            // 固定生成L8格式的灰色图片数据
1961            vec![128u8; pixels]
1962        }
1963        ColorDepth::La8 => {
1964            // 固定生成La8格式的灰色图片数据
1965            let mut gray_data: Vec<u8> = Vec::with_capacity(pixels * 2);
1966            for i in 0..pixels {
1967                gray_data.push(128u8);
1968                gray_data.push(rgb_data[i * 2 + 1]);
1969            }
1970            gray_data
1971        }
1972    }
1973}
1974
1975/// 组件内部使用的数据段结构。
1976#[derive(Debug, Clone)]
1977pub(crate) struct RichData {
1978    /// 数据ID。
1979    pub id: i64,
1980    pub text: String,
1981    pub font: Font,
1982    pub font_size: i32,
1983    pub fg_color: Color,
1984    pub bg_color: Option<Color>,
1985    underline: bool,
1986    clickable: bool,
1987    expired: bool,
1988    /// 闪烁片段列表
1989    blink: bool,
1990    disabled: bool,
1991    pub strike_through: bool,
1992    pub line_height: i32,
1993    /// 当前内容在面板垂直高度中的起始和截至y坐标,以及起始和结尾x坐标。
1994    v_bounds: Arc<RwLock<(i32, i32, i32, i32)>>,
1995
1996    /// 对当前数据进行试算后,分割成适配单行宽度的分片保存起来。不支持多线程。
1997    pub(crate) line_pieces: Vec<Arc<RwLock<LinePiece>>>,
1998    data_type: DataType,
1999    /// 格式为RGB格式(L8/LA8/RGB8/RGBA8)的图片数据。
2000    image: Option<Vec<u8>>,
2001    image_color_depth: ColorDepth,
2002    /// 原始宽度
2003    image_width: i32,
2004    /// 原始高度
2005    image_height: i32,
2006    /// 希望绘制的目标宽度
2007    image_target_width: i32,
2008    /// 希望绘制的目标高度
2009    image_target_height: i32,
2010    /// 色深为L8的灰度数据。
2011    image_inactive: Option<Vec<u8>>,
2012    /// 图片来源地址。
2013    image_src_url: Option<String>,
2014    image_file_path: Option<PathBuf>,
2015    /// 多行片段之间的水平空白距离。
2016    piece_spacing: i32,
2017
2018    pub(crate) search_result_positions: Option<Vec<(usize, usize)>>,
2019    pub(crate) search_highlight_pos: Option<usize>,
2020
2021    /// 互动属性。
2022    pub action: Option<Action>,
2023    /// 是否来自光标定位面板的数据。
2024    rewrite_board_data: bool
2025}
2026
2027impl From<UserData> for RichData {
2028    fn from(data: UserData) -> Self {
2029        match data.data_type {
2030            DataType::Text => {
2031                RichData {
2032                    id: data.id,
2033                    text: data.text,
2034                    font: data.font,
2035                    font_size: data.font_size,
2036                    fg_color: data.fg_color,
2037                    bg_color: data.bg_color,
2038                    underline: data.underline,
2039                    clickable: data.clickable,
2040                    expired: data.expired,
2041                    blink: data.blink,
2042                    disabled: false,
2043                    strike_through: data.strike_through,
2044                    line_height: 1,
2045                    v_bounds: Arc::new(RwLock::new((0, 0, 0, 0))),
2046                    line_pieces: vec![],
2047                    data_type: DataType::Text,
2048                    image: None,
2049                    image_color_depth: ColorDepth::L8,
2050                    image_width: 0,
2051                    image_height: 0,
2052                    image_target_width: 0,
2053                    image_target_height: 0,
2054                    image_inactive: None,
2055                    image_src_url: None,
2056                    image_file_path: None,
2057                    piece_spacing: 0,
2058                    search_result_positions: None,
2059                    search_highlight_pos: None,
2060                    action: data.action,
2061                    rewrite_board_data: false,
2062                }
2063            },
2064            DataType::Image => {
2065                let (rgb_data, depth, image_width, image_height) = image_to_rgb_data(&data.image, data.image_target_width, data.image_target_height);
2066                RichData {
2067                    id: data.id,
2068                    text: data.text,
2069                    font: data.font,
2070                    font_size: data.font_size,
2071                    fg_color: data.fg_color,
2072                    bg_color: data.bg_color,
2073                    underline: data.underline,
2074                    clickable: data.clickable,
2075                    expired: data.expired,
2076                    blink: data.blink,
2077                    disabled: false,
2078                    strike_through: data.strike_through,
2079                    line_height: 1,
2080                    v_bounds: Arc::new(RwLock::new((0, 0, 0, 0))),
2081                    line_pieces: Vec::with_capacity(0),
2082                    data_type: DataType::Image,
2083                    image: rgb_data,
2084                    image_color_depth: depth,
2085                    image_width,
2086                    image_height,
2087                    image_target_width: data.image_target_width,
2088                    image_target_height: data.image_target_height,
2089                    image_inactive: None,
2090                    image_src_url: data.image_src_url,
2091                    image_file_path: data.image_file_path,
2092                    piece_spacing: 0,
2093                    search_result_positions: None,
2094                    search_highlight_pos: None,
2095                    action: data.action,
2096                    rewrite_board_data: false,
2097                }
2098            }
2099        }
2100    }
2101}
2102
2103impl RichData {
2104    pub(crate) fn empty() -> Self {
2105        RichData {
2106            id: YitIdHelper::next_id(),
2107            text: String::new(),
2108            font: Font::Helvetica,
2109            font_size: 0,
2110            fg_color: Color::White,
2111            bg_color: None,
2112            underline: false,
2113            clickable: false,
2114            expired: false,
2115            blink: false,
2116            disabled: false,
2117            strike_through: false,
2118            line_height: 1,
2119            v_bounds: Arc::new(RwLock::new((0, 0, 0, 0))),
2120            line_pieces: Vec::with_capacity(0),
2121            data_type: DataType::Text,
2122            image: None,
2123            image_color_depth: ColorDepth::L8,
2124            image_width: 0,
2125            image_height: 0,
2126            image_target_width: 0,
2127            image_target_height: 0,
2128            image_inactive: None,
2129            image_src_url: None,
2130            image_file_path: None,
2131            piece_spacing: 0,
2132            search_result_positions: None,
2133            search_highlight_pos: None,
2134            action: None,
2135            rewrite_board_data: false,
2136        }
2137    }
2138
2139    pub(crate) fn set_piece_spacing(&mut self, piece_spacing: i32) {
2140        self.piece_spacing = piece_spacing;
2141    }
2142    
2143    /// 处理超宽的数据单元,自动换行。
2144    ///
2145    /// # Arguments
2146    ///
2147    /// * `text`:
2148    /// * `last_piece`:
2149    /// * `max_width`:
2150    /// * `padding`:
2151    /// * `measure_width`:
2152    ///
2153    /// returns: ()
2154    ///
2155    /// # Examples
2156    ///
2157    /// ```
2158    ///
2159    /// ```
2160    pub fn wrap_text_for_estimate(&mut self, text: &str, last_piece: Arc<RwLock<LinePiece>>, max_width: i32, measure_width: i32, font_height: i32) -> Arc<RwLock<LinePiece>> {
2161        let original = last_piece.clone();
2162        let last_piece = last_piece.read().clone();
2163        let tw = Rc::new(RefCell::new(0));
2164        let text_len = text.chars().count();
2165        let (font, font_size) = (self.font, self.font_size);
2166        if let Ok(stop_pos) = (0..text_len).collect::<Vec<usize>>().binary_search_by({
2167            let x = last_piece.next_x + self.piece_spacing;
2168            let tw_rc = tw.clone();
2169            move |pos| {
2170                let (tw1, _) = measure(text.chars().take(*pos).collect::<String>().as_str(), false);
2171                if x + tw1 <= max_width {
2172                    if *pos == text_len - 1 {
2173                        tw_rc.replace(tw1);
2174                        Ordering::Equal
2175                    } else {
2176                        let (tw2, _) = measure(text.chars().take(*pos + 1).collect::<String>().as_str(), false);
2177                        if x + tw2 > max_width {
2178                            tw_rc.replace(tw1);
2179                            Ordering::Equal
2180                        } else {
2181                            Ordering::Less
2182                        }
2183                    }
2184                } else {
2185                    Ordering::Greater
2186                }
2187            }
2188        }) {
2189            // 出现超宽
2190            let w = *tw.borrow();
2191            // 换行处理
2192            let next_x = PADDING.left;
2193            let through_line = ThroughLine::create_or_update(PADDING.left, last_piece.next_x, font_height, original.clone(), false);
2194            let line_max_h = through_line.read().max_h;
2195            let max_h = max(line_max_h, font_height);
2196            let mut next_y = last_piece.next_y + max_h + last_piece.spacing;
2197            if through_line.read().exist_image {
2198                next_y += IMAGE_PADDING_V * 2;
2199            }
2200
2201            let y = last_piece.next_y;
2202            let top_y = last_piece.next_y;
2203            let new_piece = LinePiece::new(text.chars().take(stop_pos).collect::<String>(), last_piece.next_x, y, w, font_height, top_y, last_piece.spacing, next_x, next_y, font_height, font, font_size,  through_line.clone(), self.v_bounds.clone());
2204            self.line_pieces.push(new_piece.clone());
2205
2206            let rest_str = text.chars().skip(stop_pos).collect::<String>();
2207            let rest_width = measure_width - w;
2208
2209            if rest_width > max_width {
2210                // 剩余部分的宽度仍然大于一整行宽度
2211                self.wrap_text_for_estimate(rest_str.as_str(), new_piece.clone(), max_width, rest_width, font_height)
2212            } else {
2213                let rest_x = next_x;
2214                let rest_y = next_y;
2215                let top_y = next_y;
2216                let mut rest_next_x = rest_x + rest_width + self.piece_spacing;
2217                let mut rest_next_y = next_y;
2218                if rest_str.ends_with("\n") {
2219                    rest_next_x = PADDING.left;
2220                    rest_next_y += font_height + last_piece.spacing;
2221                }
2222
2223                let through_line = ThroughLine::create_or_update(PADDING.left, rest_x, font_height, original.clone(), false);
2224                let new_piece = LinePiece::new(rest_str, rest_x, rest_y, rest_width, font_height, top_y, last_piece.spacing, rest_next_x, rest_next_y, font_height, font, font_size, through_line, self.v_bounds.clone());
2225                self.line_pieces.push(new_piece.clone());
2226                new_piece
2227            }
2228        } else {
2229            // 从行首开始
2230            let through_line = ThroughLine::create_or_update(PADDING.left, PADDING.left, self.line_height, original.clone(), false);
2231            let y = last_piece.next_y + last_piece.through_line.read().max_h + last_piece.spacing;
2232            let new_piece = LinePiece::new(text.to_string(), PADDING.left, y, measure_width, self.line_height, y, last_piece.spacing, PADDING.left, y, font_height, font, font_size, through_line, self.v_bounds.clone());
2233            self.wrap_text_for_estimate(text, new_piece, max_width, measure_width, font_height)
2234        }
2235    }
2236
2237}
2238
2239
2240impl LinedData for RichData {
2241    fn set_v_bounds(&mut self, top_y: i32, bottom_y: i32, start_x: i32, end_x: i32,) {
2242        *self.v_bounds.write() = (top_y, bottom_y, start_x, end_x);
2243    }
2244
2245    fn is_text_data(&self) -> bool {
2246        self.image.is_none()
2247    }
2248
2249    fn clickable(&self) -> bool {
2250        self.clickable && !self.expired
2251    }
2252
2253    fn set_clickable(&mut self, clickable: bool) {
2254        self.clickable = clickable;
2255    }
2256
2257    fn is_expired(&self) -> bool {
2258        self.expired
2259    }
2260
2261    fn set_expire(&mut self, expire: bool) {
2262        self.expired = expire;
2263    }
2264
2265    fn set_text_data(&mut self, text_data: &str) {
2266        self.text.clear();
2267        self.text.push_str(text_data);
2268    }
2269
2270    fn set_binary_data(&mut self, _: Vec<u8>) {}
2271
2272    fn is_visible(&self, top_y: i32, bottom_y: i32) -> bool {
2273        let b = *self.v_bounds.read();
2274        !(b.1 < top_y || b.0 > bottom_y)
2275    }
2276
2277    fn draw(&self, offset_y: i32, blink_state: &BlinkState) {
2278        match self.data_type {
2279            DataType::Text => {
2280                let mut processed_search_len = 0usize;
2281                set_font(self.font, self.font_size);
2282                for piece in self.line_pieces.iter() {
2283                    let piece = &*piece.read();
2284                    let text = piece.line.trim_end_matches('\n');
2285                    if text.is_empty() {
2286                        continue;
2287                    }
2288
2289                    let y = piece.y - offset_y;
2290
2291                    if !self.blink || blink_state.next == BlinkDegree::Normal {
2292                        if let Some(bg_color) = &self.bg_color {
2293                            // 绘制文字背景色
2294                            // debug!("绘制文字背景色: {}", bg_color.to_hex_str());
2295                            set_draw_color(*bg_color);
2296                            draw_rectf(piece.x, y - piece.spacing + piece.bg_offset, piece.w, piece.font_height);
2297                        }
2298                    }
2299
2300                    if let Some((from, to)) = *piece.selected_range.read() {
2301                        // 绘制选中背景色
2302                        let sel_color = if let Some(bg_color) = &self.bg_color {
2303                            if *bg_color == Color::Blue || *bg_color == Color::DarkBlue {
2304                                Color::DarkMagenta
2305                            } else {
2306                                Color::Selection
2307                            }
2308                        } else {
2309                            Color::Selection
2310                        };
2311                        set_draw_color(sel_color);
2312                        let (skip_width, _) = measure(piece.line.chars().take(from).collect::<String>().as_str(), false);
2313                        let (fill_width, _) = measure(piece.line.chars().skip(from).take(max(to, from) - from).collect::<String>().as_str(), false);
2314
2315                        draw_rectf(piece.x + skip_width, y + piece.bg_offset, fill_width, piece.font_height);
2316                    }
2317
2318                    // 绘制查找焦点框
2319                    if let Some(ref pos_vec) = self.search_result_positions {
2320                        let rect_color = if blink_state.next == BlinkDegree::Normal {
2321                            blink_state.focus_boarder_color
2322                        } else {
2323                            blink_state.focus_boarder_contrast_color
2324                        };
2325                        let pl = piece.line.chars().count();
2326                        let range = processed_search_len..(processed_search_len + pl);
2327                        pos_vec.iter().enumerate().for_each(|(pos_i, (pos_from, pos_to))| {
2328                            if range.contains(pos_from) {
2329                                let start_index_of_piece = pos_from - processed_search_len;
2330                                let (skip_width, _) = measure(piece.line.chars().take(start_index_of_piece).collect::<String>().as_str(), false);
2331                                let (fill_width, _) = measure(piece.line.chars().skip(start_index_of_piece).take(pos_to - pos_from).collect::<String>().as_str(), false);
2332
2333                                set_draw_color(blink_state.focus_background_color);
2334                                #[cfg(not(target_os = "windows"))]
2335                                {
2336                                    // draw_rectf(piece.x + skip_width, y - piece.spacing + 2, fill_width, piece.font_height);
2337                                    draw_rounded_rectf(piece.x + skip_width, y - piece.spacing + 2, fill_width, piece.font_height, HIGHLIGHT_ROUNDED_RECT_RADIUS);
2338                                    if let Some(h_i) = self.search_highlight_pos {
2339                                        if h_i == pos_i {
2340                                            // debug!("blink1: {:?}", blink_state);
2341                                            set_draw_color(rect_color);
2342                                            set_line_style(LineStyle::Solid, blink_state.focus_boarder_width);
2343                                            // draw_rect_with_color(piece.x + skip_width, y - piece.spacing + 2, fill_width, piece.font_height, rect_color);
2344                                            draw_rounded_rect(piece.x + skip_width, y - piece.spacing + 2, fill_width, piece.font_height, HIGHLIGHT_ROUNDED_RECT_RADIUS);
2345                                            set_line_style(LineStyle::Solid, 0);
2346                                        }
2347                                    }
2348                                }
2349
2350                                #[cfg(target_os = "windows")]
2351                                {
2352                                    // draw_rectf(piece.x + skip_width, y - piece.spacing, fill_width, piece.font_height);
2353                                    draw_rounded_rectf(piece.x + skip_width, y - piece.spacing, fill_width, piece.font_height, HIGHLIGHT_ROUNDED_RECT_RADIUS);
2354                                    if let Some(h_i) = self.search_highlight_pos {
2355                                        if h_i == pos_i {
2356                                            set_draw_color(rect_color);
2357                                            set_line_style(LineStyle::Solid, blink_state.focus_boarder_width);
2358                                            // draw_rect_with_color(piece.x + skip_width, y - piece.spacing, fill_width, piece.font_height, rect_color);
2359                                            draw_rounded_rect(piece.x + skip_width, y - piece.spacing, fill_width, piece.font_height, HIGHLIGHT_ROUNDED_RECT_RADIUS);
2360                                            set_line_style(LineStyle::Solid, 0);
2361                                        }
2362                                    }
2363                                }
2364
2365                            } else if range.contains(pos_to) {
2366                                let (fill_width, _) = measure(piece.line.chars().take(pos_to - processed_search_len).collect::<String>().as_str(), false);
2367
2368                                set_draw_color(blink_state.focus_background_color);
2369                                // draw_rectf(piece.x, y - piece.spacing, fill_width, piece.font_height);
2370                                draw_rounded_rectf(piece.x, y - piece.spacing, fill_width, piece.font_height, HIGHLIGHT_ROUNDED_RECT_RADIUS);
2371                                if let Some(h_i) = self.search_highlight_pos {
2372                                    if h_i == pos_i {
2373                                        set_draw_color(rect_color);
2374                                        set_line_style(LineStyle::Solid, blink_state.focus_boarder_width);
2375                                        // draw_rect_with_color(piece.x, y - piece.spacing, fill_width, piece.font_height, rect_color);
2376                                        draw_rounded_rect(piece.x, y - piece.spacing, fill_width, piece.font_height, HIGHLIGHT_ROUNDED_RECT_RADIUS);
2377                                        set_line_style(LineStyle::Solid, 0);
2378                                    }
2379                                }
2380                            }
2381                        });
2382                        processed_search_len += pl;
2383                    }
2384
2385                    if self.blink && blink_state.next == BlinkDegree::Contrast {
2386                        set_draw_color(get_lighter_or_darker_color(self.fg_color));
2387                    } else {
2388                        set_draw_color(self.fg_color);
2389                    }
2390
2391                    if self.underline {
2392                        // 绘制下划线
2393                        // let line_y = y + piece.font_height + piece.bg_offset - 1;
2394                        let line_y = y + piece.font_size + piece.text_offset + 2;
2395                        draw_line(piece.x, line_y, piece.x + piece.w - 2, line_y);
2396                    }
2397
2398                    // 绘制文本,使用draw_text_n()函数可以正确渲染'@'字符而无需转义处理。
2399                    draw_text_n(text, piece.x, y + self.font_size + piece.text_offset);
2400
2401                    if self.strike_through {
2402                        // 绘制删除线
2403                        let line_y = y + ((piece.font_height as f32 / 2f32).floor() as i32);
2404                        draw_line(piece.x, line_y, piece.x + piece.w - 4, line_y);
2405                    }
2406
2407                    // {
2408                    //     // 绘制上边界,用来辅助调试绘制内容的偏移效果
2409                    //     draw_line(piece.x, piece.y, piece.x + piece.w, piece.y);
2410                    // }
2411                }
2412            },
2413            DataType::Image => {
2414                if let Some(piece) = self.line_pieces.last() {
2415                    let piece = &*piece.read();
2416                    if !self.disabled {
2417                        if !self.blink || blink_state.next == BlinkDegree::Normal {
2418                            if let Some(img) = &self.image {
2419                                // debug!("绘制图像:x:{}, y:{}, w:{}, h:{}", piece.x, piece.y - offset_y, piece.w, piece.h);
2420                                match RgbImage::new(img, self.image_width, self.image_height, self.image_color_depth) {
2421                                    Ok(mut rgb_img) => {
2422                                        if self.image_width != self.image_target_width || self.image_height != self.image_target_height {
2423                                            rgb_img.scale(self.image_target_width, self.image_target_height, false, true);
2424                                        }
2425                                        rgb_img.draw(piece.x, piece.y - offset_y, piece.w, piece.h);
2426                                    }
2427                                    Err(e) => {
2428                                        error!("create rgb image error: {:?}", e);
2429                                    }
2430                                }
2431                            }
2432                            if !self.text.is_empty() {
2433                                // 在图像上居中绘制文字
2434                                set_font(self.font, self.font_size);
2435                                set_draw_color(self.fg_color);
2436                                let lines = self.text.split("\n").count() as i32;
2437                                let total_height = self.font_size * lines;
2438                                let img_y_center = piece.y - offset_y + piece.h / 2;
2439                                let first_line_y = img_y_center - total_height / 2;
2440
2441                                for (idx, line) in self.text.replace("\r", "").split("\n").enumerate() {
2442                                    let (tw, _) = measure(line, false);
2443                                    let text_x = piece.x + piece.w / 2 - tw / 2;
2444                                    let text_y = first_line_y + idx as i32 * self.font_size;
2445                                    draw_text_n(line, text_x, text_y + self.font_size);
2446                                }
2447                            }
2448                        }
2449                    } else {
2450                        if !self.blink || blink_state.next == BlinkDegree::Normal {
2451                            if let Some(img) = &self.image_inactive {
2452                                let depth = match self.image_color_depth {
2453                                    ColorDepth::Rgb8 | ColorDepth::L8 => {
2454                                        ColorDepth::L8
2455                                    }
2456                                    ColorDepth::Rgba8 | ColorDepth::La8 => {
2457                                        ColorDepth::La8
2458                                    }
2459                                };
2460                                match RgbImage::new(img, self.image_width, self.image_height, depth) {
2461                                    Ok(mut rgb_img) => {
2462                                        if self.image_width != self.image_target_width || self.image_height != self.image_target_height {
2463                                            rgb_img.scale(self.image_target_width, self.image_target_height, false, true);
2464                                        }
2465                                        rgb_img.draw(piece.x, piece.y - offset_y, piece.w, piece.h);
2466                                    }
2467                                    Err(e) => {
2468                                        error!("create rgb image error: {:?}", e);
2469                                    }
2470                                }
2471                                // if let Err(e) = draw_image(img.as_slice(), piece.x, piece.y - offset_y, self.image_width, piece.h, depth) {
2472                                //     error!("draw gray image error: {:?}", e);
2473                                // }
2474
2475                                if !self.text.is_empty() {
2476                                    // 在图像上居中绘制文字
2477                                    set_font(self.font, self.font_size);
2478                                    set_draw_color(Color::Light1);
2479                                    let lines = self.text.split("\n").count() as i32;
2480                                    let total_height = self.font_size * lines;
2481                                    let img_y_center = piece.y - offset_y + piece.h / 2;
2482                                    let first_line_y = img_y_center - total_height / 2;
2483
2484                                    for (idx, line) in self.text.replace("\r", "").split("\n").enumerate() {
2485                                        let (tw, _) = measure(line, false);
2486                                        let text_x = piece.x + piece.w / 2 - tw / 2;
2487                                        let text_y = first_line_y + idx as i32 * self.font_size;
2488                                        draw_text_n(line, text_x, text_y + self.font_size);
2489                                    }
2490                                }
2491                            }
2492                        }
2493                    }
2494                }
2495
2496            },
2497        }
2498    }
2499
2500    /// 试算当前内容绘制后所占高度信息。
2501    /// 试算功能自动处理文本超宽时截断换行的逻辑。
2502    ///
2503    /// # Arguments
2504    ///
2505    /// * `last_piece`: 前一个数据片段,用于计算当前数据段的绘制坐标。每个数据段和数据片段都是按照缓存数据的顺序依次计算得到。
2506    /// * `max_width`: 可视区域最大宽度,不含padding宽度。
2507    ///
2508    /// returns: ()
2509    ///
2510    /// # Examples
2511    ///
2512    /// ```
2513    ///
2514    /// ```
2515    fn estimate(&mut self, last_piece: Arc<RwLock<LinePiece>>, max_width: i32, basic_char: char) -> Arc<RwLock<LinePiece>> {
2516        let mut ret = last_piece.clone();
2517        let mut last_line_piece = last_piece.read().clone();
2518        let (top_y, start_x) = (last_line_piece.next_y, last_line_piece.next_x);
2519        let (font, font_size) = (self.font, self.font_size);
2520        self.line_pieces.clear();
2521        match self.data_type {
2522            DataType::Text => {
2523                set_font(self.font, self.font_size);
2524
2525                // 字体渲染高度,小于等于行高度。
2526                let ref_font_height = (self.font_size as f32 * LINE_HEIGHT_FACTOR).ceil() as i32;
2527
2528                let current_line_spacing = min(last_line_piece.spacing, descent());
2529
2530                /*
2531                对含有换行符和不含换行符的文本进行不同处理。
2532                 */
2533                let text = self.text.clone();
2534                if text.contains('\n') {
2535                    // 以换行符为节点拆分成多段处理。
2536                    for line in text.split_inclusive("\n") {
2537                        let (tw, th) = measure(line, false);
2538                        let mut current_line_height = max(ref_font_height, th);
2539                        self.line_height = current_line_height;
2540
2541                        let mut next_x = last_line_piece.next_x + tw;
2542                        if next_x > max_width {
2543                            // 超出横向右边界
2544                            ret = self.wrap_text_for_estimate(line, ret.clone(), max_width, tw, ref_font_height);
2545                        } else {
2546                            let new_piece: Arc<RwLock<LinePiece>>;
2547                            if let Some(lp) = self.line_pieces.last_mut() {
2548                                let lp = &mut *lp.write();
2549                                let mut next_y = lp.next_y;
2550                                // 最后一段可能带有换行符'\n'。
2551                                if line.ends_with("\n") {
2552                                    next_y += current_line_height;
2553                                    next_x = PADDING.left;
2554                                }
2555                                let y = lp.next_y;
2556                                let piece_top_y = lp.next_y;
2557                                let through_line = ThroughLine::create_or_update(PADDING.left, lp.next_x, current_line_height, ret.clone(), false);
2558                                new_piece = LinePiece::new(line.to_string(), lp.next_x, y, tw, current_line_height, piece_top_y, lp.spacing, next_x, next_y, ref_font_height, font, font_size, through_line, self.v_bounds.clone());
2559
2560                            } else {
2561                                let mut next_y = last_line_piece.next_y;
2562                                // 最后一段可能带有换行符'\n'。
2563                                if line.ends_with("\n") {
2564                                    // if !last_piece.line.ends_with("\n") && !last_piece.line.is_empty() {
2565                                    //     current_line_height = max(current_line_height, last_piece.h);
2566                                    // }
2567                                    if !last_line_piece.line.ends_with("\n") {
2568                                        current_line_height = max(current_line_height, last_line_piece.h);
2569                                    }
2570                                    next_y += current_line_height;
2571                                    next_x = PADDING.left;
2572                                }
2573                                let y = last_line_piece.next_y;
2574                                let piece_top_y = last_line_piece.next_y;
2575                                let through_line = ThroughLine::create_or_update(PADDING.left, last_line_piece.next_x, current_line_height, ret.clone(), false);
2576                                new_piece = LinePiece::new(line.to_string(), last_line_piece.next_x, y, tw, self.line_height, piece_top_y, last_line_piece.spacing, next_x, next_y, ref_font_height, font, font_size, through_line, self.v_bounds.clone());
2577                            }
2578                            self.line_pieces.push(new_piece.clone());
2579                            ret = new_piece;
2580                        }
2581                        last_line_piece = ret.read().clone();
2582                    }
2583
2584                } else {
2585                    let (_, th) = measure(basic_char.to_string().as_str(), false);
2586                    self.line_height = max(ref_font_height, th);
2587
2588                    let line = text.as_str();
2589                    let (tw, _) = measure(line, false);
2590                    let next_x = start_x + tw + self.piece_spacing;
2591                    if next_x > max_width {
2592                        // 超出横向右边界
2593                        ret = self.wrap_text_for_estimate(line, ret.clone(), max_width, tw, ref_font_height);
2594                    } else {
2595                        let y = top_y;
2596                        let through_line = ThroughLine::create_or_update(PADDING.left, start_x, ref_font_height, ret, false);
2597                        let next_y = top_y;
2598                        let new_piece = LinePiece::new(self.text.clone(), start_x, y, tw, ref_font_height, top_y, current_line_spacing, next_x, next_y, ref_font_height, font, font_size, through_line, self.v_bounds.clone());
2599                        self.line_pieces.push(new_piece.clone());
2600                        ret = new_piece;
2601                    }
2602                }
2603            }
2604            DataType::Image => {
2605                let h = self.image_target_height + IMAGE_PADDING_V * 2;
2606                if start_x + self.image_target_width > max_width {
2607                    // 本行超宽,直接定位到下一行
2608                    let x = PADDING.left + IMAGE_PADDING_H;
2609                    let y = top_y + last_line_piece.through_line.read().max_h + IMAGE_PADDING_V;
2610                    let next_x = x + self.image_target_width + IMAGE_PADDING_H;
2611                    let next_y = y - IMAGE_PADDING_V;
2612                    let piece_top_y = y - IMAGE_PADDING_V;
2613                    let through_line = ThroughLine::new(self.image_target_height * IMAGE_PADDING_V * 2, true);
2614                    let new_piece = LinePiece::new("".to_string(), x, y, self.image_target_width, self.image_target_height, piece_top_y, last_line_piece.spacing, next_x, next_y, 1, font, font_size, through_line, self.v_bounds.clone());
2615                    self.line_pieces.push(new_piece.clone());
2616                    ret = new_piece;
2617                } else {
2618                    let x = start_x + IMAGE_PADDING_H;
2619                    let next_x = start_x + self.image_target_width + IMAGE_PADDING_H * 2 + self.piece_spacing;
2620                    if last_line_piece.line.ends_with("\n") {
2621                        // 定位在行首
2622                        let y = top_y + IMAGE_PADDING_V;
2623                        let piece_top_y = y - IMAGE_PADDING_V;
2624                        let through_line = ThroughLine::new(self.image_target_height * IMAGE_PADDING_V * 2, true);
2625                        let new_piece = LinePiece::new("".to_string(), x, y, self.image_target_width, self.image_target_height, piece_top_y, last_line_piece.spacing, next_x, top_y, 1, font, font_size, through_line, self.v_bounds.clone());
2626                        self.line_pieces.push(new_piece.clone());
2627                        ret = new_piece;
2628                    } else {
2629                        // 在本行已有其他内容,需要与前一个片段协调行高
2630                        let current_line_height = max(last_line_piece.h, h);
2631                        let mut raw_y = top_y + IMAGE_PADDING_V;
2632                        if current_line_height > last_line_piece.h {
2633                            // 图形比前一个分片行高要高
2634                            last_line_piece.through_line.write().set_max_h(current_line_height);
2635                        } else {
2636                            // 图形的高度小于等于前一个分片的行高,需要计算垂直居中位置
2637                            let (up, _) = calc_v_center_offset(current_line_height, h);
2638                            raw_y += up;
2639                        }
2640                        let y = raw_y;
2641                        let piece_top_y = y - IMAGE_PADDING_V;
2642                        let through_line = ThroughLine::create_or_update(PADDING.left + IMAGE_PADDING_H, x, self.image_target_height * IMAGE_PADDING_V * 2, ret, true);
2643                        let new_piece = LinePiece::new("".to_string(), x, y, self.image_target_width, self.image_target_height, piece_top_y, last_line_piece.spacing, next_x, top_y + IMAGE_PADDING_V, 1, font, font_size, through_line, self.v_bounds.clone());
2644                        self.line_pieces.push(new_piece.clone());
2645                        ret = new_piece;
2646                    }
2647                }
2648            }
2649        }
2650
2651        let (mut _is_first_line, mut bound_start_x, mut bound_end_x) = (true, 0, 0);
2652        let mut to_be_updated: Vec<(Arc<RwLock<LinePiece>>, i32)> = Vec::new();
2653        for line_piece in self.line_pieces.iter() {
2654            let lp = &*line_piece.read();
2655            if _is_first_line {
2656                bound_start_x = lp.x;
2657                _is_first_line = false;
2658            }
2659
2660            let tl = &mut *lp.through_line.write();
2661            let mut max_h = 1;
2662            // 找出最大的行高
2663            for l in tl.ys.iter() {
2664                if let Some(l) = l.upgrade() {
2665                    if l.read().h > max_h {
2666                        max_h = l.read().h;
2667                    }
2668                }
2669            }
2670            tl.max_h = max_h;
2671            // 收集同一行内低于最大高度的分片。因为borrow作用域的问题,无法在一个for循环内直接处理,只能先收集再处理。
2672            for one_piece in tl.ys.iter() {
2673                if let Some(p) = one_piece.upgrade() {
2674                    let lh = p.read().h;
2675                    if lh < max_h {
2676                        to_be_updated.push((p.clone(), max_h));
2677                    }
2678                }
2679            }
2680        }
2681
2682        // 计算文字片段字体高度小于行高时的渲染偏移量
2683        self.line_pieces.iter_mut().for_each(|lp| lp.write().calc_offset());
2684
2685        // 重新计算同一行内低于最大行高的片段的y坐标
2686        for (lp, max_h) in to_be_updated {
2687            let y = lp.read().y;
2688            let piece_top_y = lp.read().top_y;
2689            let h = lp.read().h;
2690
2691            if lp.read().line.ends_with("\n") {
2692                let mut padding_v = 0;
2693                if lp.read().through_line.read().exist_image {
2694                    padding_v = IMAGE_PADDING_V;
2695                }
2696                lp.write().next_y = y + max_h + padding_v;
2697            }
2698
2699            let (up_offset, _) = calc_v_center_offset(max_h, h);
2700            lp.write().y = piece_top_y + up_offset;
2701            let lpm = &mut*lp.write();
2702            let mut vb = *lpm.rd_bounds.write();
2703            vb.1 = piece_top_y + up_offset + lpm.h;
2704            *lpm.rd_bounds.write() = vb;
2705        }
2706
2707        // let mut pic_y = 0;
2708        let v_b_top_y = if let Some(first_piece) = self.line_pieces.first() {
2709            let fp = &*first_piece.read();
2710            fp.top_y
2711            // pic_y = fp.y;
2712        } else {
2713            top_y
2714        };
2715        let v_b_bottom_y = if let Some(last_piece) = self.line_pieces.last() {
2716            let lp = &*last_piece.read();
2717            let bottom_y = lp.top_y + lp.through_line.read().max_h;
2718            bound_end_x = lp.x + lp.w;
2719            bottom_y
2720        } else {
2721            v_b_top_y
2722        };
2723        // debug!("estimated v_b_top_y: {v_b_top_y}, v_b_bottom_y: {v_b_bottom_y}, bound_start_x: {bound_start_x}, bound_end_x: {bound_end_x}, text: {}", self.text);
2724        self.set_v_bounds(v_b_top_y, v_b_bottom_y, bound_start_x, bound_end_x);
2725        ret
2726    }
2727}
2728
2729/// 数据片段调整属性。
2730#[derive(Debug, Clone)]
2731pub struct RichDataOptions {
2732    pub id: i64,
2733    pub clickable: Option<bool>,
2734    pub underline: Option<bool>,
2735    pub expired: Option<bool>,
2736    pub text: Option<String>,
2737    pub fg_color: Option<Color>,
2738    pub bg_color: Option<Color>,
2739    pub strike_through: Option<bool>,
2740    pub blink: Option<bool>,
2741    pub disabled: Option<bool>,
2742    pub image: Option<Vec<u8>>,
2743    image_width: Option<i32>,
2744    image_height: Option<i32>,
2745    pub image_target_width: Option<i32>,
2746    pub image_target_height: Option<i32>,
2747    pub image_color_depth: Option<ColorDepth>,
2748    /// 图片文件临时存储路径。
2749    pub image_file_path: Option<PathBuf>,
2750    pub action: Option<Action>,
2751}
2752
2753impl RichDataOptions {
2754    pub fn new(id: i64) -> RichDataOptions {
2755        RichDataOptions {
2756            id,
2757            clickable: None,
2758            underline: None,
2759            expired: None,
2760            text: None,
2761            fg_color: None,
2762            bg_color: None,
2763            strike_through: None,
2764            blink: None,
2765            disabled: None,
2766            image: None,
2767            image_width: None,
2768            image_height: None,
2769            image_target_width: None,
2770            image_target_height: None,
2771            image_color_depth: None,
2772            image_file_path: None,
2773            action: None,
2774        }
2775    }
2776
2777    pub fn clickable(mut self, clickable: bool) -> RichDataOptions {
2778        self.clickable = Some(clickable);
2779        self
2780    }
2781
2782    pub fn underline(mut self, underline: bool) -> RichDataOptions {
2783        self.underline = Some(underline);
2784        self
2785    }
2786
2787    pub fn expired(mut self, expired: bool) -> RichDataOptions {
2788        self.expired = Some(expired);
2789        self
2790    }
2791
2792    pub fn text(mut self, text: String) -> RichDataOptions {
2793        self.text = Some(text);
2794        self
2795    }
2796
2797    pub fn fg_color(mut self, fg_color: Color) -> RichDataOptions {
2798        self.fg_color = Some(fg_color);
2799        self
2800    }
2801
2802    pub fn bg_color(mut self, bg_color: Color) -> RichDataOptions {
2803        self.bg_color = Some(bg_color);
2804        self
2805    }
2806
2807    pub fn strike_through(mut self, strike_through: bool) -> RichDataOptions {
2808        self.strike_through = Some(strike_through);
2809        self
2810    }
2811
2812    pub fn blink(mut self, blink: bool) -> RichDataOptions {
2813        self.blink = Some(blink);
2814        self
2815    }
2816
2817    pub fn disabled(mut self, disabled: bool) -> RichDataOptions {
2818        self.disabled = Some(disabled);
2819        self
2820    }
2821
2822    /// 设置影像更新参数。
2823    ///
2824    /// # Arguments
2825    ///
2826    /// * `image`: 新的影像对象。若为 `None` 表示不替换原有的影像,只是变更目标宽高。
2827    /// * `target_width`: 目标宽度。
2828    /// * `target_height`: 目标高度。
2829    ///
2830    /// returns: RichDataOptions
2831    ///
2832    /// # Examples
2833    ///
2834    /// ```
2835    ///
2836    /// ```
2837    pub fn image(mut self, image: Option<RgbImage>, target_width: i32, target_height: i32) -> RichDataOptions {
2838        if image.is_some() {
2839            let (img_data, depth, width, height) = image_to_rgb_data(&image, target_width, target_height);
2840            self.image = img_data;
2841            self.image_width = Some(width);
2842            self.image_height = Some(height);
2843            self.image_target_width = Some(target_width);
2844            self.image_target_height = Some(target_height);
2845            self.image_color_depth = Some(depth);
2846            self
2847        } else {
2848            self.image_target_width = Some(target_width);
2849            self.image_target_height = Some(target_height);
2850            self
2851        }
2852    }
2853
2854    pub fn image_file_path(mut self, image_file_path: PathBuf) -> RichDataOptions {
2855        self.image_file_path = Some(image_file_path);
2856        self
2857    }
2858
2859    pub fn change_action(mut self, action: Action) -> RichDataOptions {
2860        self.action = Some(action);
2861        self
2862    }
2863}
2864
2865/// 碰撞检测,检查两个矩形区域是否出现交叉。
2866///
2867/// # Arguments
2868///
2869/// * `a`: 目标区域。
2870/// * `b`: 当前区域,或者鼠标拖拽选择区域。
2871///
2872/// returns: bool
2873///
2874/// # Examples
2875///
2876/// ```
2877///
2878/// ```
2879pub(crate) fn is_overlap(target_area: &Rectangle, selection_area: &Rectangle) -> bool {
2880    let mut overlap = target_area.0 <= (selection_area.0 + selection_area.2);
2881    overlap &= (target_area.0 + target_area.2) >= selection_area.0;
2882    overlap &= target_area.1 <= (selection_area.1 + selection_area.3);
2883    overlap &= (target_area.1 + target_area.3) >= selection_area.1;
2884    // debug!("overlap: {overlap}");
2885    overlap
2886    // target_area.0 < (selection_area.0 + selection_area.2)
2887    //     && (target_area.0 + target_area.2) > selection_area.0
2888    //     && target_area.1 < (selection_area.1 + selection_area.3)
2889    //     && (target_area.1 + target_area.3) > selection_area.1
2890}
2891
2892
2893/// 复制选中片段的内容。
2894///
2895/// # Arguments
2896///
2897/// * `it`:
2898/// * `selection`:
2899///
2900/// returns: ()
2901///
2902/// # Examples
2903///
2904/// ```
2905///
2906/// ```
2907fn copy_pieces(it: Iter<Weak<RwLock<LinePiece>>>, selection: &mut String) {
2908    for p in it {
2909        if let Some(p) = p.upgrade() {
2910            let lp = &*p.read();
2911            lp.copy_selection(selection);
2912        }
2913    }
2914}
2915
2916/// 清除数据片段的选中属性。
2917///
2918/// # Arguments
2919///
2920/// * `selected_pieces`:
2921///
2922/// returns: ()
2923///
2924/// # Examples
2925///
2926/// ```
2927///
2928/// ```
2929pub(crate) fn clear_selected_pieces(selected_pieces: Arc<RwLock<Vec<Weak<RwLock<LinePiece>>>>>) {
2930    for piece in selected_pieces.read().iter() {
2931        if let Some(p) = piece.upgrade() {
2932            p.read().deselect();
2933        }
2934    }
2935    selected_pieces.write().clear();
2936}
2937
2938/// 向前或向后选择数据片段。
2939///
2940/// # Arguments
2941///
2942/// * `rd`:
2943/// * `piece_index`:
2944/// * `pos`:
2945/// * `selected_pieces`:
2946/// * `from`:
2947///
2948/// returns: ()
2949///
2950/// # Examples
2951///
2952/// ```
2953///
2954/// ```
2955fn select_piece_from_or_to(rd: &RichData, piece_index: usize, pos: usize, selected_pieces: Arc<RwLock<Vec<Weak<RwLock<LinePiece>>>>>, from: bool) {
2956    if let Some(last_piece_rc) = rd.line_pieces.get(piece_index) {
2957        let piece = &*last_piece_rc.read();
2958        if from {
2959            piece.select_from(pos);
2960        } else {
2961            piece.select_to(pos);
2962        }
2963        selected_pieces.write().push(Arc::downgrade(last_piece_rc));
2964    }
2965}
2966
2967/// 计算选中文本所在数据片段的选中属性。
2968///
2969/// # Arguments
2970///
2971/// * `from_point`: 起始位置。
2972/// * `to_point`: 结束位置。
2973/// * `data_buffer`: 数据缓存。
2974/// * `rd_range`: 选中的数据段索引范围。
2975/// * `selected_pieces`: 选中数据片段临时记录容器。
2976///
2977/// returns: ()
2978///
2979/// # Examples
2980///
2981/// ```
2982///
2983/// ```
2984pub(crate) fn select_text(
2985    from_point: &ClickPoint,
2986    to_point: &ClickPoint,
2987    data_buffer: &[RichData],
2988    rd_range: RangeInclusive<usize>,
2989    selected_pieces: Arc<RwLock<Vec<Weak<RwLock<LinePiece>>>>>,
2990    select_from_row: usize) {
2991    /*
2992    选择片段的原则:应选择起点右下方的第一行片段,结束点左上方的第一行片段,以及两点之间的中间行片段。
2993     */
2994    // debug!("传入的fp: {:?}, tp: {:?}", from_point, to_point);
2995    let drag_rect = from_point.to_rect(to_point);
2996    // debug!("drag_rect: {:?}", drag_rect);
2997    let (lt, br) = drag_rect.corner_rect();
2998    let (mut lt_p, mut br_p) = (from_point, to_point);
2999    /*
3000    lt_p: 划选区左上角点击位置
3001    br_p: 划选区右下角点击位置
3002    lf_p_i: 左上角数据分片索引号
3003    br_p_i: 右下角数据分片索引号
3004     */
3005    let (mut lt_p_i, mut br_p_i) = (lt_p.p_i, br_p.p_i);
3006    // debug!("lt_p_i: {lt_p_i}, br_p_i: {br_p_i}, rd_range: {:?}, drag_rect: {:?}, lt_p: {:?}, br_p: {:?}", rd_range, drag_rect, lt_p, br_p);
3007    // 清理上一次选择的区域
3008    clear_selected_pieces(selected_pieces.clone());
3009
3010    let (r_start, r_end) = (*rd_range.start(), *rd_range.end());
3011    let across_rds = max(r_end as isize - r_start as isize, 0);
3012    if across_rds > 0 {
3013        // 超过一行
3014        // debug!("选区超过一个数据段");
3015        if let Some(rd) = data_buffer.get(r_start) {
3016            // 选择第一个数据段起点之后的所有分片内容。
3017            // debug!("第一个数据段 lt_p_i: {lt_p_i}, lt_p.c_i: {}, br_p_i: {br_p_i}, br_p.c_i: {}", lt_p.c_i, br_p.c_i);
3018            let v_bounds = &*rd.v_bounds.read();
3019            // 若起始点成为划选区的右下角或左下角的时候,对调起始点和结束点
3020            let mut should_exchange = (br.0 == from_point.x && br.1 == from_point.y) || (lt.0 == from_point.x && br.1 == from_point.y);
3021            // 若划选区在起点数据段边界内,则不应对调
3022            if should_exchange && r_start == select_from_row && drag_rect.1 >= v_bounds.0 && (drag_rect.1 + drag_rect.3) <= v_bounds.1 {
3023                should_exchange = false;
3024            }
3025
3026            if should_exchange {
3027                // debug!("对换坐标点");
3028                lt_p = to_point;
3029                br_p = from_point;
3030                lt_p_i = lt_p.p_i;
3031                br_p_i = br_p.p_i;
3032            }
3033
3034            select_piece_from_or_to(rd, lt_p_i, lt_p.c_i, selected_pieces.clone(), true);
3035            for p in rd.line_pieces.iter().skip(lt_p_i + 1) {
3036                let piece = &*p.read();
3037                piece.select_all();
3038                selected_pieces.write().push(Arc::downgrade(p));
3039            }
3040        }
3041
3042        // 如果中间有更多跨行数据段,则全选这些数据段。
3043        if r_end > r_start + 1 {
3044            let mut piece_rcs = Vec::new();
3045            for i in r_start + 1..r_end {
3046                if let Some(rd) = data_buffer.get(i) {
3047                    for p in rd.line_pieces.iter() {
3048                        let piece = &*p.read();
3049                        piece.select_all();
3050                        piece_rcs.push(Arc::downgrade(p));
3051                    }
3052                }
3053            }
3054            selected_pieces.write().append(&mut piece_rcs);
3055        }
3056
3057        if let Some(rd) = data_buffer.get(r_end) {
3058            // 选择最后一个数据段终点之前的所有内容。
3059            for p in rd.line_pieces.iter().take(br_p_i) {
3060                let piece = &*p.read();
3061                piece.select_all();
3062                selected_pieces.write().push(Arc::downgrade(p));
3063            }
3064            // debug!("br_p_i: {br_p_i}, br_p.c_i: {}", br_p.c_i);
3065            select_piece_from_or_to(rd, br_p_i, br_p.c_i + 1, selected_pieces.clone(), false);
3066        }
3067    } else {
3068        // 只有一行
3069        // debug!("选区只有一个数据段");
3070
3071        // 若起始点成为划选区的右下角或左下角的时候,对调起始点和结束点
3072        if (br.0 == from_point.x && br.1 == from_point.y) || (lt.0 == from_point.x && br.1 == from_point.y) {
3073            // debug!("对换坐标点");
3074            lt_p = to_point;
3075            br_p = from_point;
3076            lt_p_i = lt_p.p_i;
3077            br_p_i = br_p.p_i;
3078        }
3079
3080        if let Some(rd) = data_buffer.get(r_start) {
3081            // debug!("br_p_i: {br_p_i}, lt_p_i: {lt_p_i}");
3082            if br_p_i != lt_p_i {
3083                // 超过一个分片
3084                // debug!("选区超过一个分片");
3085                select_piece_from_or_to(rd, lt_p_i, lt_p.c_i, selected_pieces.clone(), true);
3086
3087                // 超过两个分片
3088                // debug!("选区超过两个分片");
3089                let mut piece_rcs = Vec::new();
3090                for i in lt_p_i + 1..br_p_i {
3091                    if let Some(piece_rc) = rd.line_pieces.get(i) {
3092                        let piece = &*piece_rc.read();
3093                        piece.select_all();
3094                        piece_rcs.push(Arc::downgrade(piece_rc));
3095                    }
3096                }
3097                selected_pieces.write().append(&mut piece_rcs);
3098
3099                select_piece_from_or_to(rd, br_p_i, br_p.c_i + 1, selected_pieces.clone(), false);
3100            } else {
3101                // 在同一个分片内
3102                if let Some(piece_rc) = rd.line_pieces.get(lt_p_i) {
3103                    // debug!("selected range from: {} to: {}", lt_p.c_i, br_p.c_i + 1);
3104                    let (mut fci, mut tci) = (lt_p.c_i, br_p.c_i + 1);
3105                    if fci >= tci {
3106                        fci = br_p.c_i;
3107                        tci = lt_p.c_i + 1;
3108                    }
3109                    piece_rc.read().select_range(fci, tci);
3110                    selected_pieces.write().push(Arc::downgrade(piece_rc));
3111                }
3112            }
3113        }
3114    }
3115
3116    /*
3117    拷贝至剪贴板
3118     */
3119    let mut selection = String::new();
3120    copy_pieces(selected_pieces.read().iter(), &mut selection);
3121    app::copy(selection.as_str());
3122}
3123
3124#[derive(Debug)]
3125pub(crate) struct TargetRow {
3126    pub(crate) row: usize,
3127    pub(crate) expanded: bool,
3128}
3129
3130impl TargetRow {
3131    pub fn new(row: usize, expanded: bool) -> Self {
3132        Self { row, expanded }
3133    }
3134}
3135
3136/// 检测拖选范围所涵盖的数据段。
3137///
3138/// # Arguments
3139///
3140/// * `point`: 起始点。
3141/// * `drag_rect`: 拖选矩形范围。
3142/// * `panel_width`: 容器面板宽度。
3143/// * `data_buffer`: 数据缓存。
3144/// * `index_vec`: 容器面板可见范围内数据的顺序位置索引。
3145///
3146/// returns: Option<usize> 返回拖选结束点的数据段索引。
3147///
3148/// # Examples
3149///
3150/// ```
3151///
3152/// ```
3153pub(crate) fn locate_target_rd(point: &mut ClickPoint, mut drag_rect: Rectangle, panel_width: i32, data_buffer: &[RichData], index_vec: Vec<usize>) -> Option<TargetRow> {
3154    let point_rect = point.as_rect();
3155    // debug!("index_vec: {:?}", index_vec);
3156    if let Ok(idx) = index_vec.binary_search_by({
3157        let point_rect_rc = point_rect.clone();
3158        let point_rc = point.clone();
3159        move |row| _search_row_idx(row, &data_buffer, panel_width, &drag_rect, point_rect_rc, point_rc)
3160    }) {
3161        // debug!("已定位当前数据段 {idx}");
3162        let ret = _record_start_char_pos(data_buffer, &index_vec, idx, &point_rect, point);
3163        if let Some(row) = ret {
3164            Some(TargetRow::new(row, false))
3165        } else {
3166            None
3167        }
3168    } else {
3169        // debug!("没找到目标数据段!向左上扩展");
3170        drag_rect.2 = max(drag_rect.0 - PADDING.left, 0);
3171        drag_rect.3 = max(drag_rect.1 - PADDING.top, 0);
3172        drag_rect.0 = PADDING.left;
3173        drag_rect.1 = PADDING.top;
3174        let point_rect = drag_rect.clone();
3175        let mut tmp_point = point.clone();
3176        tmp_point.x = PADDING.left;
3177
3178        // 先用二分法粗略定位到选区中的某个数据段,再从该数据段开始向后遍历找到最后一个位于选区内的数据段,将该数据段的末尾设定为新的选择起点。
3179        if let Ok(idx) = index_vec.binary_search_by({
3180            let point_rect_rc = point_rect.clone();
3181            let point_rc = tmp_point.clone();
3182            move |row| _search_row_idx(row, &data_buffer, panel_width, &drag_rect, point_rect_rc, point_rc)
3183        }) {
3184            // debug!("粗略定位到选区中的数据段 {idx}");
3185            let ret = _record_start_char_pos2(data_buffer, index_vec[idx], &point_rect, &mut tmp_point);
3186            if let Some(row) = ret {
3187                point.x = tmp_point.x;
3188                point.y = tmp_point.y;
3189                point.p_i = tmp_point.p_i;
3190                point.c_i = tmp_point.c_i;
3191                // debug!("point: {point:?}");
3192                Some(TargetRow::new(row, true))
3193            } else {
3194                None
3195            }
3196        } else {
3197            None
3198        }
3199    }
3200}
3201
3202fn _search_row_idx(row: &usize, data_buffer: &[RichData], panel_width: i32, drag_rect: &Rectangle, point_rect_rc: Rectangle, point_rc: ClickPoint) -> Ordering {
3203    /*
3204    先将不规则的数据段外形扩展为顶宽的矩形,再检测划选区是否与之重叠;
3205    如果有重叠,则进一步检测其中每个分片是否与划选区有重叠,如果任意分片有重叠,说明划选区包含了该数据段的某些分片,
3206    还须再进一步确定选区起点位置所在的分片,最终返回等于,否则返回大于或小于;
3207    如果没有重叠,则判断其相对位置,并返回大于或小于。
3208     */
3209    let mut rd_extend_rect = Rectangle::zero();
3210    let rd = &data_buffer[*row];
3211    // debug!("检测行 {row} : {}", rd.text);
3212    let (rd_top_y, rd_bottom_y, _, _) = *rd.v_bounds.read();
3213    // let max_h = if let Some(first_lp) = rd.line_pieces.first() {
3214    //     rd_top_y = first_lp.read().top_y;
3215    //     first_lp.read().through_line.read().max_h
3216    // } else {
3217    //     rd_bottom_y - rd_top_y
3218    // };
3219    rd_extend_rect.replace(0, rd_top_y, panel_width, rd_bottom_y - rd_top_y);
3220    // debug!("{point_rect:?}, rd_top_y: {}, rd_bottom_y: {}, drag_rect: {:?}", rd_top_y, rd_bottom_y, drag_rect);
3221
3222    // 粗略过滤到的数据段,还须进一步检测其中的分片是否包含划选区起点。
3223    if is_overlap(&rd_extend_rect, &drag_rect) {
3224        // debug!("行 {row} 与划选区有重叠");
3225        let mut ord = Ordering::Less;
3226        for piece_rc in rd.line_pieces.iter() {
3227            let piece = &*piece_rc.read();
3228            let piece_rect = piece.rect(0, 0);
3229            // debug!("piece_rect: {:?}, piece_top_y: {}, : {}", piece_rect, piece.top_y, piece.line);
3230            if is_overlap(&piece_rect, &point_rect_rc) {
3231                // 划选区起点位于分片内
3232                // debug!("划选区起点位于分片内:{:?}", piece.line);
3233                ord = Ordering::Equal;
3234                break;
3235            }
3236        }
3237        // 如果划选起点不在重叠数据段的任意分片内部,则还须判断当前数据段在起点的前面或后面,为查找算法提供判断依据。
3238        if ord != Ordering::Equal {
3239            if let Some(first_piece_rc) = rd.line_pieces.first() {
3240                let piece = &*first_piece_rc.read();
3241                // debug!("piece: {:?}", piece);
3242                if point_rc.x < piece.x && point_rc.y < piece.top_y + piece.through_line.read().max_h {
3243                    ord = Ordering::Greater;
3244                } else {
3245                    ord = Ordering::Less;
3246                }
3247            }
3248        }
3249        // debug!("行 {row}: ord: {:?}", ord);
3250        ord
3251    } else {
3252        if rd_extend_rect.is_below(&drag_rect) {
3253            // debug!("行 {row}: 大于");
3254            Ordering::Greater
3255        } else {
3256            // debug!("行 {row}: 小于");
3257            Ordering::Less
3258        }
3259    }
3260}
3261
3262
3263fn _record_start_char_pos2(data_buffer: &[RichData], from_row: usize, point_rect: &Rectangle, point: &mut ClickPoint) -> Option<usize> {
3264    // 从指定位置开始向后查找选区内的数据段
3265    let mut ret = from_row;
3266    let bottom_y = point_rect.1 + point_rect.3;
3267    'OUTER: for row in from_row..data_buffer.len() {
3268        let rd = &data_buffer[row];
3269        if rd.data_type != DataType::Image {
3270            if rd.v_bounds.read().0 > bottom_y {
3271                // 直到数据段上边界大于选区的下边界时,结束遍历。
3272                break 'OUTER;
3273            } else {
3274                for (p_i, piece_rc) in rd.line_pieces.iter().enumerate() {
3275                    let piece = &*piece_rc.read();
3276                    let piece_rect = piece.rect(0, 0);
3277                    // 将最后一个选区内的数据片段末尾位置,当作选区的起点
3278                    if is_overlap(&piece_rect, &point_rect) {
3279                        point.x = piece_rect.0 + piece_rect.2;
3280                        point.y = piece_rect.1 + piece_rect.3 / 2;
3281                        point.p_i = p_i;
3282                        point.c_i = piece.line.trim().len();
3283                        ret = row;
3284                    }
3285                }
3286            }
3287        }
3288    }
3289    Some(ret)
3290}
3291
3292fn _record_start_char_pos(data_buffer: &[RichData], index_vec: &Vec<usize>, idx: usize, point_rect: &Rectangle, point: &mut ClickPoint) -> Option<usize> {
3293    let rd = &data_buffer[index_vec[idx]];
3294    if rd.data_type != DataType::Image {
3295        // debug!("找到目标点所在数据段: {}", rd.text);
3296        for (p_i, piece_rc) in rd.line_pieces.iter().enumerate() {
3297            let piece = &*piece_rc.read();
3298            let piece_rect = piece.rect(0, 0);
3299            // debug!("point_rect: {:?}, piece_rect: {:?}, line: {}", point_rect, piece_rect, piece.line);
3300            if is_overlap(&piece_rect, &point_rect) {
3301                // 划选区起点位于分片内
3302                point.p_i = p_i;
3303                // debug!("目标点位于分片:{} 内: {}", p_i, piece.line);
3304                search_index_of_piece(piece, point);
3305                break;
3306            }
3307        }
3308        return Some(idx);
3309    } else {
3310        // 选择了图片,不予处理。
3311        // debug!("选择了图片")
3312        None
3313    }
3314}
3315
3316/// 更新拖选结束点所在数段的位置属性,用于拖选过程准实时检测结束点位置。
3317///
3318/// # Arguments
3319///
3320/// * `push_from_point`: 起始点。
3321/// * `select_from_row`: 起始点所在数据段的顺序索引号。
3322/// * `current_point`: 当前结束点。
3323/// * `data_buffer_slice`: 数据缓存。
3324/// * `selected_pieces`: 临时保存选中数据片段的容器。
3325/// * `panel`: 当前容器面板。
3326///
3327/// returns: bool
3328///
3329/// # Examples
3330///
3331/// ```
3332///
3333/// ```
3334pub(crate) fn update_selection_when_drag(
3335    push_from_point: ClickPoint,
3336    select_from_row: usize,
3337    current_point: &mut ClickPoint,
3338    data_buffer_slice: &[RichData],
3339    selected_pieces: Arc<RwLock<Vec<Weak<RwLock<LinePiece>>>>>,
3340    panel: &mut impl WidgetBase,) {
3341
3342    let mut down = true;
3343    let index_vec = if current_point.y >= push_from_point.y {
3344        // 向下选择
3345        (select_from_row..data_buffer_slice.len()).collect::<Vec<usize>>()
3346    } else {
3347        // 向上选择
3348        // debug!("向上划选");
3349        down = false;
3350        /*
3351        这里引入一个魔法数字100,表示查找范围从0到起点行再向后扩展100个数据段,
3352        这是为了临时解决划选区从起点数据段向右侧划选时,跨入同行内后面的数据段无法被查找到的问题,
3353        数字100表示允许向起点数据段之后延伸查找最多100个数据段。
3354         */
3355        let max_rows = min(select_from_row + 100, data_buffer_slice.len() - 1);
3356        (0..=max_rows).collect::<Vec<usize>>()
3357    };
3358    // debug!("开始查找结束点所在数据段: {:?}", index_vec);
3359    // debug!("push_from: {:?}, current_point: {:?}", push_from_point, current_point);
3360    if let Some(select_to_row) = locate_target_rd(current_point, current_point.as_rect(), panel.w(), data_buffer_slice, index_vec) {
3361        // debug!("select_from_row: {select_from_row}, select_to_row: {select_to_row:?}");
3362        /*
3363        向下选择时,select_to_row表示相对于select_from_row的偏移量或绝对位置;
3364        向上选择时,select_to_row表示相对于0的偏移量也就是绝对位置。
3365         */
3366        let rd_range = if down {
3367            if !select_to_row.expanded {
3368                select_from_row..=(select_from_row + select_to_row.row)
3369            } else {
3370                select_from_row..=select_to_row.row
3371            }
3372        } else {
3373            if select_from_row < select_to_row.row {
3374                select_from_row..=select_to_row.row
3375            } else {
3376                select_to_row.row..=select_from_row
3377            }
3378        };
3379        // let rd_range = select_from_row..=(select_from_row + select_to_row);
3380        // debug!("rd_range: {:?}", rd_range);
3381        // debug!("push_from: {:?}, current_point: {:?}", push_from_point, current_point);
3382        select_text(&push_from_point, current_point, data_buffer_slice, rd_range, selected_pieces, select_from_row);
3383        // debug!("push_from: {:?}, current_point: {:?}", push_from_point, current_point);
3384        panel.set_damage(true);
3385    }
3386}
3387
3388
3389/// 测量鼠标点击的片段内容字符索引位置。
3390///
3391/// # Arguments
3392///
3393/// * `piece`:
3394/// * `point`:
3395///
3396/// returns: ()
3397///
3398/// # Examples
3399///
3400/// ```
3401///
3402/// ```
3403pub(crate) fn search_index_of_piece(piece: &LinePiece, point: &mut ClickPoint) {
3404    let len = piece.line.chars().count();
3405    if let Ok(c_i) = (0..len).collect::<Vec<usize>>().binary_search_by({
3406        set_font(piece.font, piece.font_size);
3407        let text = piece.line.clone();
3408        let x = point.x;
3409        let start_x = piece.x;
3410        move |pos| {
3411            let (mut pw1, _) = measure(text.chars().take(*pos + 1).collect::<String>().as_str(), false);
3412            let (mut pw2, _) = measure(text.chars().take(*pos).collect::<String>().as_str(), false);
3413            pw1 += start_x;
3414            pw2 += start_x;
3415            if x > pw2 && x <= pw1 {
3416                Ordering::Equal
3417            } else if x <= pw2 {
3418                Ordering::Greater
3419            } else {
3420                Ordering::Less
3421            }
3422        }
3423    }) {
3424        point.c_i = c_i;
3425        // debug!("目标字符:{},位置:{}, point: {point:?}", piece.line.chars().nth(c_i).unwrap(), c_i);
3426    } else {
3427        // debug!("没找到目标字符!")
3428    }
3429}
3430
3431/// 选择段落。
3432/// 段落的定义:在目标点前后两个换行符之间的所有数据片段,可能跨越多个数据段。
3433///
3434/// # Arguments
3435///
3436/// * `anchor_row`: 目标点所在数据段索引。
3437/// * `data_buffer`: 数据缓存片段。
3438///
3439/// returns: ()
3440///
3441/// # Examples
3442///
3443/// ```
3444///
3445/// ```
3446pub(crate) fn select_paragraph(anchor_row: usize, push_from_point: &mut ClickPoint, data_buffer: &[RichData], selected_pieces: Arc<RwLock<Vec<Weak<RwLock<LinePiece>>>>>) {
3447    let (mut from_point, mut to_point) = (ClickPoint::new(0, 0), ClickPoint::new(0, 0));
3448    let (mut from_row, mut to_row) = (0, 0);
3449
3450    'BEFORE: for i in (0..=anchor_row).rev() {
3451        let mut piece_range = 0..data_buffer[i].line_pieces.len();
3452        if i == anchor_row {
3453            if push_from_point.p_i > 0 {
3454                piece_range = 0..push_from_point.p_i;
3455            } else {
3456                continue;
3457            }
3458        }
3459        for j in piece_range.rev() {
3460            if let Some(lp_arc) = data_buffer[i].line_pieces.get(j) {
3461                let lp = &*lp_arc.read();
3462                if lp.line.ends_with('\n') {
3463                    from_row = i;
3464                    from_point.p_i = j + 1;
3465                    from_point.c_i = 0;
3466                    from_point.x = lp.next_x;
3467                    from_point.y = lp.next_y;
3468                    break 'BEFORE;
3469                }
3470            }
3471        }
3472    }
3473
3474    'AFTER: for i in anchor_row..data_buffer.len() {
3475        let piece_range = if i == anchor_row {
3476            push_from_point.p_i..data_buffer[i].line_pieces.len()
3477        } else {
3478            0..data_buffer[i].line_pieces.len()
3479        };
3480        for j in piece_range {
3481            if let Some(lp_arc) = data_buffer[i].line_pieces.get(j) {
3482                let lp = &*lp_arc.read();
3483                if lp.line.ends_with('\n') {
3484                    to_row = i;
3485                    to_point.p_i = j;
3486                    to_point.c_i = lp.line.chars().count() - 1;
3487                    to_point.x = lp.next_x;
3488                    to_point.y = lp.next_y;
3489                    break 'AFTER;
3490                }
3491            }
3492        }
3493    }
3494
3495    let rd_range = from_row..=to_row;
3496    select_text(&from_point, &to_point, data_buffer, rd_range, selected_pieces, anchor_row);
3497}
3498
3499/// 获取指定颜色的对比色。若指定颜色为中等灰色(R/G/B值相等且在116-139之间),则返回白色。
3500///
3501/// # Arguments
3502///
3503/// * `color`: 指定颜色。
3504///
3505/// returns: Color 返回其对比色。
3506///
3507/// # Examples
3508///
3509/// ```
3510/// use fltk::enums::Color;
3511/// use fltkrs_richdisplay::get_contrast_color;
3512///
3513/// assert_eq!(get_contrast_color(Color::from_rgb(255, 255, 255)), Color::from_rgb(0, 0, 0));
3514/// assert_eq!(get_contrast_color(Color::from_rgb(0, 0, 0)), Color::from_rgb(255, 255, 255));
3515/// assert_eq!(get_contrast_color(Color::from_rgb(255, 0, 0)), Color::from_rgb(0, 255, 255));
3516/// assert_eq!(get_contrast_color(Color::from_rgb(120, 120, 120)), Color::from_rgb(255, 255, 255));
3517/// ```
3518pub fn get_contrast_color(color: Color) -> Color {
3519    let (r, g, b) = color.to_rgb();
3520    let (cr, cg, cb) = (255 - r, 255 - g, 255 - b);
3521    if (cr == cg && cg == cb) && ((cr as i16) - (r as i16)).abs() < 25 {
3522        WHITE
3523    } else {
3524        Color::from_rgb(cr, cg, cb)
3525    }
3526}
3527
3528/// 获取指定颜色的亮色或暗色,若指定颜色的R/G/B值其中最大的超过128,则获取暗色,否则获取亮色。
3529///
3530/// # Arguments
3531///
3532/// * `color`: 指定颜色。
3533///
3534/// returns: Color 返回对应的亮色或暗色。
3535///
3536/// # Examples
3537///
3538/// ```
3539///
3540/// ```
3541pub fn get_lighter_or_darker_color(color: Color) -> Color {
3542    let (r, g, b) = color.to_rgb();
3543
3544    let total = r as u16 + g as u16 + b as u16;
3545    let max_c = max(r, max(g, b));
3546    if total >= 383 || max_c as u16 + 127 > 255u16 {
3547        // 当三原色合计值超过最大合计值的一半时,或者某项原色值超过128,降低各原色数值。效果是变暗。
3548        let (cr, cg, cb) = (max(0i16, r as i16 - 127), max(0i16, g as i16 - 127), max(0i16, b as i16 - 127));
3549        Color::from_rgb(cr as u8, cg as u8, cb as u8)
3550    } else {
3551        // 当三原色合计值小于最大合计值的一半时,提高各原色数值。效果是变亮。
3552        let (cr, cg, cb) = (min(255i16, r as i16 + 127), min(255i16, g as i16 + 127), min(255i16, b as i16 + 127));
3553        Color::from_rgb(cr as u8, cg as u8, cb as u8)
3554    }
3555}
3556
3557/// 使符合过滤条件的目标数据段过期、禁用。
3558///
3559/// # Arguments
3560///
3561/// * `target`:
3562///
3563/// returns: ()
3564///
3565/// # Examples
3566///
3567/// ```
3568///
3569/// ```
3570pub(crate) fn expire_data(buffer: Arc<RwLock<Vec<RichData>>>, target: &String) {
3571    for rd in buffer.write().iter_mut() {
3572        let mut should_expire = false;
3573        if let Some(action) = &rd.action {
3574            if let Some(cat) = &action.category {
3575                if target.eq(cat) {
3576                    should_expire = true;
3577                }
3578            }
3579        }
3580        if should_expire {
3581            rd.action = None;
3582            rd.expired = true;
3583            rd.clickable = false;
3584            rd.disabled = true;
3585            rd.strike_through = true;
3586        }
3587    }
3588}
3589
3590/// 加载图片文件并生成面板更新信息。
3591///
3592/// # Arguments
3593///
3594/// * `data_id`: 数据段ID。
3595/// * `file_path`: 文件路径。
3596/// * `target_width`: 图片目标宽度,可能与图片原始宽度不同。
3597/// * `target_height`: 图片目标高度,可能与图片原始高度不同。
3598///
3599/// returns: RichDataOptions
3600///
3601/// # Examples
3602///
3603/// ```
3604///
3605/// ```
3606pub fn load_image_from_file(load_opt: LoadImageOption) -> RichDataOptions {
3607    let mut update_opt = RichDataOptions::new(load_opt.data_id);
3608    if let Some(file_path) = load_opt.file_path {
3609        if file_path.to_lowercase().ends_with(".svg") {
3610            // 对于SVG格式的文件要特殊处理一下: normalize(),否则会转换出错。
3611            match SvgImage::load(file_path.clone()) {
3612                Ok(mut si) => {
3613                    // debug!("开始转换到RGB格式,文件:{:?}", file_path);
3614                    si.normalize();
3615                    match si.to_rgb() {
3616                        Ok(new_img) => {
3617                            let mut new_action = Action::default();
3618                            new_action.items.push(ActionItem::new("刷新", MXP_IMAGE_CONTEXT_MENU_REFRESH));
3619                            new_action.items.push(ActionItem::new("复制地址", MXP_IMAGE_CONTEXT_MENU_COPY_URL));
3620                            new_action.items.push(ActionItem::new("另存为", MXP_IMAGE_CONTEXT_MENU_SAVE_AS));
3621                            update_opt = update_opt.image(Some(new_img), load_opt.target_width, load_opt.target_height)
3622                                .text(String::new())
3623                                .image_file_path(PathBuf::from(file_path))
3624                                .change_action(new_action);
3625                        }
3626                        Err(e) => {
3627                            error!("将SVG转换到RGB格式时失败:{:?}", e);
3628                        }
3629                    }
3630                }
3631                Err(e) => {
3632                    error!("加载或解码图片失败:{:?} {:?}", file_path, e);
3633                    update_opt = update_opt.text("decoding failed".to_string());
3634                }
3635            }
3636        } else {
3637            match SharedImage::load(file_path.clone()) {
3638                Ok(si) => {
3639                    // debug!("开始转换到RGB格式,文件:{:?}", file_path);
3640                    match si.to_rgb() {
3641                        Ok(new_img) => {
3642                            let mut new_action = Action::default();
3643                            new_action.items.push(ActionItem::new("刷新", MXP_IMAGE_CONTEXT_MENU_REFRESH));
3644                            new_action.items.push(ActionItem::new("复制地址", MXP_IMAGE_CONTEXT_MENU_COPY_URL));
3645                            new_action.items.push(ActionItem::new("另存为", MXP_IMAGE_CONTEXT_MENU_SAVE_AS));
3646                            update_opt = update_opt.image(Some(new_img), load_opt.target_width, load_opt.target_height)
3647                                .text(String::new())
3648                                .image_file_path(PathBuf::from(file_path))
3649                                .change_action(new_action);
3650                        }
3651                        Err(e) => {
3652                            error!("将通用格式转换到RGB格式时失败:{:?}", e);
3653                        }
3654                    }
3655                }
3656                Err(e) => {
3657                    error!("加载或解码图片失败:{:?} {:?}", file_path, e);
3658                    update_opt = update_opt.text("decoding failed".to_string());
3659                }
3660            }
3661        }
3662
3663    } else {
3664        update_opt = update_opt.text("save failed".to_string());
3665    }
3666    update_opt
3667}
3668
3669#[cfg(test)]
3670mod tests {
3671    use fltk::enums::Color;
3672    use crate::{get_contrast_color, get_lighter_or_darker_color, WHITE, Rectangle};
3673
3674    #[test]
3675    pub fn make_rectangle_test() {
3676        let rect = Rectangle::new(20, 30, 25, 25);
3677        assert_eq!(rect.tup(), (20, 30, 25, 25));
3678
3679        let rect = Rectangle::new(20, 30, -10, 25);
3680        assert_eq!(rect.tup(), (10, 30, 10, 25));
3681
3682        let rect = Rectangle::new(20, 30, 10, -25);
3683        assert_eq!(rect.tup(), (20, 5, 10, 25));
3684
3685        let rect = Rectangle::new(20, 30, -10, -25);
3686        assert_eq!(rect.tup(), (10, 5, 10, 25));
3687    }
3688
3689    #[test]
3690    pub fn str_index_test() {
3691        let str = String::from("我爱中国");
3692        assert_eq!(str.find("中国"), Some(6));
3693
3694        str.rmatch_indices("中国").for_each(|(i, _)| {
3695            let ni = str[0..i].chars().count();
3696            assert_eq!(ni, 2);
3697        })
3698    }
3699
3700    #[test]
3701    pub fn test_contrast_color_test() {
3702        assert_eq!(get_contrast_color(Color::from_rgb(255, 255, 255)), Color::from_rgb(0, 0, 0));
3703        assert_eq!(get_contrast_color(Color::from_rgb(0, 0, 0)), Color::from_rgb(255, 255, 255));
3704        for i in 1..116 {
3705            assert_ne!(get_contrast_color(Color::from_rgb(i, i, i)), WHITE);
3706        }
3707        for i in 116..=139 {
3708            assert_eq!(get_contrast_color(Color::from_rgb(i, i, i)), WHITE);
3709        }
3710        for i in 140..=255 {
3711            assert_ne!(get_contrast_color(Color::from_rgb(i, i, i)), WHITE);
3712        }
3713    }
3714
3715    #[test]
3716    pub fn get_lighter_color_test() {
3717        let lighter = get_lighter_or_darker_color(Color::DarkCyan);
3718        println!("{:?} -> {:?}", Color::DarkCyan.to_rgb(), lighter.to_rgb())
3719    }
3720
3721    #[test]
3722    pub fn emoji_test() {
3723        let emoji = "😀";
3724        println!("{:?}", emoji.chars().nth(0).unwrap());
3725        assert_eq!(emoji.len(), 1);
3726    }
3727
3728    #[test]
3729    pub fn fold_chars_test() {
3730        let hint = "这里是一个空旷的广场,地面上散落着一些碎纸片。";
3731        let mut count = 0;
3732        let new_hint = hint.chars().fold("".to_string(), |mut s, c| {
3733            s.push(c);
3734            count += 1;
3735            if count % 8 == 0 {
3736                s.push_str("\r\n");
3737            }
3738
3739            s
3740        });
3741        println!("{:?}", new_hint);
3742    }
3743
3744    #[test]
3745    pub fn c1_test() {
3746        let s = String::from_utf8_lossy(&[0xe2, 0x96, 0xbd]);
3747        println!("{}", s);
3748    }
3749}