1use 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
186pub(crate) const PADDING: Padding = Padding { left: 5, top: 5, right: 5, bottom: 5 };
188
189pub const IMAGE_PADDING_H: i32 = 2;
191
192pub const IMAGE_PADDING_V: i32 = 2;
194
195pub const BLINK_INTERVAL: f64 = 0.5;
197
198pub const HIGHLIGHT_BACKGROUND_COLOR: Color = Color::from_rgb(0, 0, 255);
200
201pub const HIGHLIGHT_RECT_COLOR: Color = Color::from_rgb(255, 145, 0);
203
204pub const HIGHLIGHT_RECT_CONTRAST_COLOR: Color = Color::from_rgb(0, 110, 255);
206pub const HIGHLIGHT_ROUNDED_RECT_RADIUS: i32 = 3;
208
209pub const WHITE: Color = Color::from_rgb(255, 255, 255);
211
212pub const DEFAULT_FONT_SIZE: i32 = 16;
214
215pub const LINE_HEIGHT_FACTOR: f32 = 1.4;
217
218pub const BASIC_UNIT_CHAR: char = 'A';
220
221pub 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#[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 CursorHorizontalAbsolute(usize),
288 CursorAbsolute(usize, usize),
290 ToggleCursor(String, bool),
292 Expire(String),
294 RemoteFlowControl(u8),
296 CursorPosReport(CprCallback),
298 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#[derive(Debug)]
329pub enum CallbackData {
330 Data(UserData),
332 Shape(ShapeData),
334 Image(ImageEventData),
336}
337
338
339#[derive(Clone)]
343pub struct Callback {
344 notifier: Arc<RwLock<Box<dyn FnMut(CallbackData) + Send + Sync +'static>>>,
346}
347
348impl Callback {
349
350
351 pub fn new(notifier: Arc<RwLock<Box<dyn FnMut(CallbackData) + Send + Sync +'static>>>) -> Callback {
384 Callback { notifier }
385 }
386
387 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#[derive(Debug, Clone)]
414pub enum PageOptions {
415 NextPage(i64),
417 PrevPage(i64),
419}
420
421#[derive(Clone)]
425pub struct CallPage {
426 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#[derive(Debug, Clone, Copy)]
438pub struct ShapeData {
439 pub old_width: i32,
441 pub old_height: i32,
443 pub new_width: i32,
445 pub new_height: i32,
447 pub new_cols: i32,
449 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#[derive(Debug, Clone)]
469pub struct ImageEventData {
470 pub click_point: (i32, i32),
472 pub src: Option<String>,
474 pub data_id: i64,
476 pub act: String,
478 pub file: Option<PathBuf>,
479 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 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.write();
506 notify(opt);
507 }
508}
509
510#[derive(Debug, Clone,Copy, PartialEq, Eq)]
512pub(crate) enum BlinkDegree {
513 Normal,
515 Contrast,
517}
518
519#[derive(Debug, Clone, Copy, PartialEq, Eq)]
521pub(crate) struct BlinkState {
522 on: bool,
524 next: BlinkDegree,
526
527 focus_boarder_color: Color,
529
530 focus_boarder_contrast_color: Color,
532
533 focus_boarder_width: i32,
535
536 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 }
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 true
569 } else {
570 false
571 }
572 }
573
574}
575
576pub(crate) struct LocalEvent;
578impl LocalEvent {
579
580 pub const SCROLL_TO: i32 = 100;
582
583 pub const RESIZE: i32 = 101;
585
586 pub const DROP_REVIEWER_FROM_EXTERNAL: i32 = 102;
588
589 pub const OPEN_REVIEWER_FROM_EXTERNAL: i32 = 103;
591}
592
593#[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 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 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 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 pub fn stretch_to_left(&mut self, add_width: i32) {
740 self.0 -= add_width;
741 self.2 += add_width;
742 }
743
744 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 }
794
795#[derive(Debug, Clone, Copy)]
797pub(crate) struct ClickPoint {
798 pub x: i32,
799 pub y: i32,
800 pub p_i: usize,
802 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 pub fn as_rect(&self) -> Rectangle {
814 Rectangle::new(self.x, self.y, 0, 0)
815 }
816
817 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#[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 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 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#[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#[derive(Debug, Clone)]
941pub(crate) struct LinePiece {
942 pub line: String,
943 pub x: i32,
945 pub y: i32,
947 pub w: i32,
949 pub h: i32,
951 pub top_y: i32,
953 pub spacing: i32,
956 pub next_x: i32,
958 pub next_y: i32,
960
961 pub font_height: i32,
963 pub text_offset: i32,
965 pub bg_offset: i32,
967
968 pub through_line: Arc<RwLock<ThroughLine>>,
970
971 pub selected_range: Arc<RwLock<Option<(usize, usize)>>>,
973
974 pub font: Font,
975 pub font_size: i32,
976
977 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 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 pub fn get_cursor(&self) -> LinePiece {
1078 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 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 #[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 fn set_v_bounds(&mut self, top_y: i32, bottom_y: i32, start_x: i32, end_x: i32);
1174
1175 fn is_text_data(&self) -> bool;
1177
1178 fn clickable(&self) -> bool;
1180
1181 fn set_clickable(&mut self, clickable: bool);
1183
1184 fn is_expired(&self) -> bool;
1186
1187 fn set_expire(&mut self, expire: bool);
1201
1202 fn set_text_data(&mut self, text_data: &str);
1216
1217 fn set_binary_data(&mut self, binary_data: Vec<u8>);
1231
1232
1233 fn is_visible(&self, top_y: i32, bottom_y: i32) -> bool;
1248
1249
1250 fn draw(&self, offset_y: i32, blink_state: &BlinkState);
1265
1266 fn estimate(&mut self, last_piece: Arc<RwLock<LinePiece>>, max_width: i32, basic_char: char) -> Arc<RwLock<LinePiece>>;
1282
1283}
1284
1285#[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#[derive(Clone, Debug, Default, Serialize)]
1309pub struct Action {
1310 pub title: String,
1312 pub kind: u8,
1314 pub items: Vec<ActionItem>,
1317 pub active: Option<String>,
1319 pub category: Option<String>,
1321}
1322
1323#[derive(Clone, Debug)]
1325pub struct UserData {
1326 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 pub fg_color_index: u8,
1336 pub bg_color_index: u8,
1338 pub strong: bool,
1340 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 pub image_width: i32,
1351 pub image_height: i32,
1353 pub image_target_width: i32,
1355 pub image_target_height: i32,
1357 pub image_src_url: Option<String>,
1359 pub image_file_path: Option<PathBuf>,
1361 pub(crate) custom_font_text: bool,
1362 pub custom_font_color: bool,
1363 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 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 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 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 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 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
1710pub(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
1730pub(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
1753pub(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
1842pub(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
1874pub 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
1897pub 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::Rgb8 => {
1937 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 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 vec![128u8; pixels]
1962 }
1963 ColorDepth::La8 => {
1964 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#[derive(Debug, Clone)]
1977pub(crate) struct RichData {
1978 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 blink: bool,
1990 disabled: bool,
1991 pub strike_through: bool,
1992 pub line_height: i32,
1993 v_bounds: Arc<RwLock<(i32, i32, i32, i32)>>,
1995
1996 pub(crate) line_pieces: Vec<Arc<RwLock<LinePiece>>>,
1998 data_type: DataType,
1999 image: Option<Vec<u8>>,
2001 image_color_depth: ColorDepth,
2002 image_width: i32,
2004 image_height: i32,
2006 image_target_width: i32,
2008 image_target_height: i32,
2010 image_inactive: Option<Vec<u8>>,
2012 image_src_url: Option<String>,
2014 image_file_path: Option<PathBuf>,
2015 piece_spacing: i32,
2017
2018 pub(crate) search_result_positions: Option<Vec<(usize, usize)>>,
2019 pub(crate) search_highlight_pos: Option<usize>,
2020
2021 pub action: Option<Action>,
2023 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 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 let w = *tw.borrow();
2191 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 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 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 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 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 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_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 set_draw_color(rect_color);
2342 set_line_style(LineStyle::Solid, blink_state.focus_boarder_width);
2343 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_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_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_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_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 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(text, piece.x, y + self.font_size + piece.text_offset);
2400
2401 if self.strike_through {
2402 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 }
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 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 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 !self.text.is_empty() {
2476 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 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 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 let text = self.text.clone();
2534 if text.contains('\n') {
2535 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 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 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 if line.ends_with("\n") {
2564 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 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 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 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 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 last_line_piece.through_line.write().set_max_h(current_line_height);
2635 } else {
2636 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 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 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 self.line_pieces.iter_mut().for_each(|lp| lp.write().calc_offset());
2684
2685 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 v_b_top_y = if let Some(first_piece) = self.line_pieces.first() {
2709 let fp = &*first_piece.read();
2710 fp.top_y
2711 } 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 self.set_v_bounds(v_b_top_y, v_b_bottom_y, bound_start_x, bound_end_x);
2725 ret
2726 }
2727}
2728
2729#[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 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 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
2865pub(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 overlap
2886 }
2891
2892
2893fn 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
2916pub(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
2938fn 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
2967pub(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 let drag_rect = from_point.to_rect(to_point);
2996 let (lt, br) = drag_rect.corner_rect();
2998 let (mut lt_p, mut br_p) = (from_point, to_point);
2999 let (mut lt_p_i, mut br_p_i) = (lt_p.p_i, br_p.p_i);
3006 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 if let Some(rd) = data_buffer.get(r_start) {
3016 let v_bounds = &*rd.v_bounds.read();
3019 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 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 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 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 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 select_piece_from_or_to(rd, br_p_i, br_p.c_i + 1, selected_pieces.clone(), false);
3066 }
3067 } else {
3068 if (br.0 == from_point.x && br.1 == from_point.y) || (lt.0 == from_point.x && br.1 == from_point.y) {
3073 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 if br_p_i != lt_p_i {
3083 select_piece_from_or_to(rd, lt_p_i, lt_p.c_i, selected_pieces.clone(), true);
3086
3087 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 if let Some(piece_rc) = rd.line_pieces.get(lt_p_i) {
3103 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 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
3136pub(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 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 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 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 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 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 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 let mut rd_extend_rect = Rectangle::zero();
3210 let rd = &data_buffer[*row];
3211 let (rd_top_y, rd_bottom_y, _, _) = *rd.v_bounds.read();
3213 rd_extend_rect.replace(0, rd_top_y, panel_width, rd_bottom_y - rd_top_y);
3220 if is_overlap(&rd_extend_rect, &drag_rect) {
3224 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 if is_overlap(&piece_rect, &point_rect_rc) {
3231 ord = Ordering::Equal;
3234 break;
3235 }
3236 }
3237 if ord != Ordering::Equal {
3239 if let Some(first_piece_rc) = rd.line_pieces.first() {
3240 let piece = &*first_piece_rc.read();
3241 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 ord
3251 } else {
3252 if rd_extend_rect.is_below(&drag_rect) {
3253 Ordering::Greater
3255 } else {
3256 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 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 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 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 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 if is_overlap(&piece_rect, &point_rect) {
3301 point.p_i = p_i;
3303 search_index_of_piece(piece, point);
3305 break;
3306 }
3307 }
3308 return Some(idx);
3309 } else {
3310 None
3313 }
3314}
3315
3316pub(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 (select_from_row..data_buffer_slice.len()).collect::<Vec<usize>>()
3346 } else {
3347 down = false;
3350 let max_rows = min(select_from_row + 100, data_buffer_slice.len() - 1);
3356 (0..=max_rows).collect::<Vec<usize>>()
3357 };
3358 if let Some(select_to_row) = locate_target_rd(current_point, current_point.as_rect(), panel.w(), data_buffer_slice, index_vec) {
3361 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 select_text(&push_from_point, current_point, data_buffer_slice, rd_range, selected_pieces, select_from_row);
3383 panel.set_damage(true);
3385 }
3386}
3387
3388
3389pub(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 } else {
3427 }
3429}
3430
3431pub(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
3499pub 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
3528pub 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 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 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
3557pub(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
3590pub 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 match SvgImage::load(file_path.clone()) {
3612 Ok(mut si) => {
3613 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 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}