diff --git a/examples/border/border_with_child.rs b/examples/border/border_with_child.rs index 68d501b..95bca36 100644 --- a/examples/border/border_with_child.rs +++ b/examples/border/border_with_child.rs @@ -20,7 +20,7 @@ impl ObjectSubclass for BorderWithChild { impl ObjectImpl for BorderWithChild { fn initialize(&mut self) { - self.set_background(Color::GREY_LIGHT); + self.set_background(Color::TRANSPARENT); self.set_halign(Align::Center); self.enable_bubble(EventBubble::MOUSE_MOVE); @@ -44,6 +44,6 @@ impl WidgetImpl for BorderWithChild { fn on_mouse_leave(&mut self, _: &MouseEvent) { info!("Mouse leaved."); - self.set_background(Color::GREY_LIGHT); + self.set_background(Color::TRANSPARENT); } } diff --git a/tmui/src/graphics/painter.rs b/tmui/src/graphics/painter.rs index cac0c0d..8d3ffa2 100644 --- a/tmui/src/graphics/painter.rs +++ b/tmui/src/graphics/painter.rs @@ -906,7 +906,7 @@ impl<'a> Painter<'a> { } #[inline] - pub fn draw_dom(&self, dom: &tlib::typedef::SkiaSvgDom) { - dom.render(self.canvas); + pub fn draw_dom(&self, dom: impl AsRef) { + dom.as_ref().render(self.canvas); } } diff --git a/tmui/src/icons/mod.rs b/tmui/src/icons/mod.rs index b4ab6ef..41a00ee 100644 --- a/tmui/src/icons/mod.rs +++ b/tmui/src/icons/mod.rs @@ -1,3 +1,4 @@ pub mod font_icon; +pub mod svg_dom; pub mod svg_icon; -pub mod svg_toggle_icon; \ No newline at end of file +pub mod svg_toggle_icon; diff --git a/tmui/src/icons/svg_dom.rs b/tmui/src/icons/svg_dom.rs new file mode 100644 index 0000000..277e561 --- /dev/null +++ b/tmui/src/icons/svg_dom.rs @@ -0,0 +1,52 @@ +use std::io::Read; + +use tlib::{figure::Size, skia_safe::FontMgr, typedef::SkiaSvgDom}; +use usvg::{fontdb::Database, Options, Tree}; + +#[derive(Debug, Clone)] +pub struct SvgDom { + dom: SkiaSvgDom, + size: Size, +} + +impl SvgDom { + pub fn from_file(path: &str) -> Self { + let mut file = std::fs::File::open(path).expect("Open file failed"); + let mut data = vec![]; + file.read_to_end(&mut data).expect("Read file failed"); + + Self::build_from_data(&data) + } + + #[inline] + pub fn from_bytes(data: &[u8]) -> Self { + Self::build_from_data(data) + } + + #[inline] + fn build_from_data(data: &[u8]) -> Self { + let svg_tree = Tree::from_data(data, &Options::default(), &Database::default()) + .expect("Create svg tree failed"); + let size = svg_tree.size(); + let (w, h) = (size.width().ceil() as i32, size.height().ceil() as i32); + + let dom = SkiaSvgDom::from_bytes(data, FontMgr::default()).expect("Create svg dom failed"); + + Self { + dom, + size: Size::new(w, h), + } + } + + #[inline] + pub fn get_size(&self) -> Size { + self.size + } +} + +impl AsRef for SvgDom { + #[inline] + fn as_ref(&self) -> &SkiaSvgDom { + &self.dom + } +} diff --git a/tmui/src/icons/svg_icon.rs b/tmui/src/icons/svg_icon.rs index 18df55f..62403b0 100644 --- a/tmui/src/icons/svg_icon.rs +++ b/tmui/src/icons/svg_icon.rs @@ -22,15 +22,15 @@ impl ObjectImpl for SvgIcon {} impl WidgetImpl for SvgIcon { fn paint(&mut self, painter: &mut Painter) { - let rect = self.rect_f(); - let (x1, y1, w1, h1) = (rect.x(), rect.y(), rect.width(), rect.height()); - let (w2, h2) = ( - self.view_size.width() as f32, - self.view_size.height() as f32, - ); - let origin = FPoint::new(x1 + (w1 - w2) / 2., y1 + (h1 - h2) / 2.); - if let Some(ref dom) = self.dom { + let rect = self.rect_f(); + let (x1, y1, w1, h1) = (rect.x(), rect.y(), rect.width(), rect.height()); + let (w2, h2) = ( + self.view_size.width() as f32, + self.view_size.height() as f32, + ); + let origin = FPoint::new(x1 + (w1 - w2) / 2., y1 + (h1 - h2) / 2.); + painter.save(); painter.translate(origin.x(), origin.y()); painter.draw_dom(dom); @@ -61,6 +61,20 @@ impl SvgIcon { icon } + + #[inline] + pub fn load_file(&mut self, path: &str) { + let mut file = std::fs::File::open(path).expect("Open file failed"); + let mut data = vec![]; + file.read_to_end(&mut data).expect("Read file failed"); + + self.build_from_data(&data); + } + + #[inline] + pub fn load_bytes(&mut self, data: &[u8]) { + self.build_from_data(data); + } } impl SvgIcon { diff --git a/tmui/src/views/cell/cell_render.rs b/tmui/src/views/cell/cell_render.rs index 86b4ab6..8e3693c 100644 --- a/tmui/src/views/cell/cell_render.rs +++ b/tmui/src/views/cell/cell_render.rs @@ -1,9 +1,10 @@ #![allow(dead_code)] -use crate::graphics::painter::Painter; +use crate::{graphics::painter::Painter, icons::svg_dom::SvgDom}; use derivative::Derivative; +use log::warn; use std::fmt::Debug; use tlib::{ - figure::{Color, FRect}, + figure::{Color, FPoint, FRect}, global::{shown_value_32, shown_value_64}, namespace::BorderStyle, skia_safe::ClipOp, @@ -14,10 +15,11 @@ use tlib::{ pub enum CellRenderType { Text, Image, + Svg, } pub trait CellRender: Debug + 'static + Send + Sync { - fn render(&self, painter: &mut Painter, geometry: FRect, val: &Value); + fn render(&self, painter: &mut Painter, geometry: FRect, val: Option<&Value>); fn border(&self) -> (f32, f32, f32, f32); @@ -48,7 +50,7 @@ pub trait CellRender: Debug + 'static + Send + Sync { macro_rules! cell_render_struct { ( $cell:ident, $builder:ident, $render_type:ident $(, $field:ident:$ty:tt)* ) => { - #[derive(Debug, Clone, Copy)] + #[derive(Debug, Clone)] pub struct $cell { border: (f32, f32, f32, f32), border_style: BorderStyle, @@ -209,11 +211,14 @@ macro_rules! impl_cell_render_common { }; } +type OptSvgDom = Option; + cell_render_struct!(TextCellRender, TextCellRenderBuilder, Text, color:Color, letter_spacing:f32); cell_render_struct!(ImageCellRender, ImageCellRenderBuilder, Image); +cell_render_struct!(SvgCellRender, SvgCellRenderBuilder, Svg, dom:OptSvgDom); impl CellRender for TextCellRender { - fn render(&self, painter: &mut Painter, geometry: FRect, val: &Value) { + fn render(&self, painter: &mut Painter, geometry: FRect, val: Option<&Value>) { painter.save(); painter.save_pen(); painter.clip_rect(geometry, ClipOp::Intersect); @@ -223,6 +228,7 @@ impl CellRender for TextCellRender { if let Some(background) = self.background { painter.fill_rect(geometry, background); } + let val = val.as_ref().unwrap(); let text = match val.ty() { Type::STRING => val.get::(), @@ -280,7 +286,26 @@ impl TextCellRender { } impl CellRender for ImageCellRender { - fn render(&self, _painter: &mut Painter, _geometry: FRect, _val: &Value) {} + fn render(&self, _painter: &mut Painter, _geometry: FRect, _val: Option<&Value>) {} + + impl_cell_render_common!(); +} + +impl CellRender for SvgCellRender { + fn render(&self, painter: &mut Painter, rect: FRect, _: Option<&Value>) { + if let Some(dom) = self.dom.as_ref() { + let view_size = dom.get_size(); + let (x1, y1, w1, h1) = (rect.x(), rect.y(), rect.width(), rect.height()); + let (w2, h2) = (view_size.width() as f32, view_size.height() as f32); + let origin = FPoint::new(x1 + (w1 - w2) / 2., y1 + (h1 - h2) / 2.); + painter.save(); + painter.translate(origin.x(), origin.y()); + painter.draw_dom(dom); + painter.restore(); + } else { + warn!("The `dom` of `SvgCellRender` is not assigned."); + } + } impl_cell_render_common!(); } diff --git a/tmui/src/views/cell/mod.rs b/tmui/src/views/cell/mod.rs index 7f18bbe..7cc5ce3 100644 --- a/tmui/src/views/cell/mod.rs +++ b/tmui/src/views/cell/mod.rs @@ -26,13 +26,14 @@ pub enum Cell { F32 { expand: bool, val: Value, render: Option> }, F64 { expand: bool, val: Value, render: Option> }, Image { expand: bool, image_address: Value, render: Option> }, + Svg { expand: bool, render: Option> }, Value { val: Value }, } macro_rules! common_cell_render_clause { ($render:ident, $painter:ident, $geometry:ident, $val:ident) => { if let Some(render) = $render { - render.render($painter, $geometry, $val); + render.render($painter, $geometry, Some($val)); } }; } @@ -88,7 +89,12 @@ impl Cell { .. } => { if let Some(render) = render { - render.render(painter, geometry, image_address); + render.render(painter, geometry, Some(image_address)); + } + } + Self::Svg { render, .. } => { + if let Some(render) = render { + render.render(painter, geometry, None); } } Self::Value { .. } => {} @@ -112,6 +118,7 @@ impl Cell { Self::F32 { .. } => f32::static_type(), Self::F64 { .. } => f64::static_type(), Self::Image { .. } => String::static_type(), + Self::Svg { .. } => Type::NONE, Self::Value { val } => val.ty(), } } @@ -133,6 +140,7 @@ impl Cell { Self::F32 { val, .. } => val, Self::F64 { val, .. } => val, Self::Image { image_address, .. } => image_address, + Self::Svg { .. } => panic!("Invalid access, Svg cell does not has the value."), Self::Value { val } => val, } } @@ -154,6 +162,7 @@ impl Cell { Self::F32 { val, .. } => *val = value, Self::F64 { val, .. } => *val = value, Self::Image { image_address, .. } => *image_address = value, + Self::Svg { .. } => {} Self::Value { val } => *val = value, } } @@ -175,6 +184,7 @@ impl Cell { Self::F32 { render, .. } => render.as_deref(), Self::F64 { render, .. } => render.as_deref(), Self::Image { render, .. } => render.as_deref(), + Self::Svg { render, .. } => render.as_deref(), Self::Value { .. } => None, } } @@ -196,6 +206,7 @@ impl Cell { Self::F32 { render, .. } => render.as_deref_mut(), Self::F64 { render, .. } => render.as_deref_mut(), Self::Image { render, .. } => render.as_deref_mut(), + Self::Svg { render, .. } => render.as_deref_mut(), Self::Value { .. } => None, } } @@ -217,6 +228,7 @@ impl Cell { Self::F32 { .. } => vec![Text], Self::F64 { .. } => vec![Text], Self::Image { .. } => vec![Image], + Self::Svg { .. } => vec![Svg], Self::Value { .. } => vec![], } } @@ -351,6 +363,40 @@ impl CellValueBuilder { } } +#[derive(Default, Debug)] +pub struct CellSvgBuilder { + expand: bool, + cell_render: Option>, +} +impl CellSvgBuilder { + #[inline] + pub fn expand(mut self, expand: bool) -> Self { + self.expand = expand; + self + } + + #[inline] + pub fn cell_render(mut self, render: Box) -> Self { + self.cell_render = Some(render); + self + } + + #[inline] + pub fn build(self) -> Cell { + let cell = Cell::Svg { + expand: self.expand, + render: self.cell_render, + }; + + if let Some(cell_render) = cell.get_render() { + if !cell.support_render_types().contains(&cell_render.ty()) { + panic!("Unsupported cell render type."); + } + } + cell + } +} + macro_rules! cell_builder_func { ( $name:ident, $builder:ident ) => { #[inline] @@ -376,5 +422,6 @@ impl Cell { cell_builder_func!(f32, CellF32Builder); cell_builder_func!(f64, CellF64Builder); cell_builder_func!(image, CellImageBuilder); + cell_builder_func!(svg, CellSvgBuilder); cell_builder_func!(value_cell, CellValueBuilder); } diff --git a/tmui/src/widget/widget_ext.rs b/tmui/src/widget/widget_ext.rs index 63c0e60..f0787b2 100644 --- a/tmui/src/widget/widget_ext.rs +++ b/tmui/src/widget/widget_ext.rs @@ -1235,6 +1235,14 @@ impl WidgetExt for T { self.set_whole_styles_render(true); self.notify_update(); + + if self.border_ref().should_draw_radius() { + if let Some(parent) = self.get_parent_mut() { + parent.set_whole_styles_render(true); + parent.set_render_styles(true); + parent.update(); + } + } } #[inline]