use crate::drawing::{Drawable, Plan};
use crate::layouts::{HasPlanner, Layout};
use skia_safe::{Canvas, Font, Paint, Path, Point, RRect};
use std::cell::RefCell;
use std::rc::Rc;
pub struct RoundedRectangle {
w: RefCell<Option<f32>>,
h: RefCell<Option<f32>>,
x_rad: RefCell<f32>,
y_rad: RefCell<f32>,
p: Paint,
planner: RefCell<Option<Rc<dyn Layout>>>,
}
impl HasPlanner for RoundedRectangle {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Drawable for RoundedRectangle {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let planned_area = self.get_planned_drawing_area(canvas);
let rrect = RRect::new_rect_xy(planned_area, *self.x_rad.borrow(), *self.y_rad.borrow());
canvas.draw_rrect(rrect, &self.p);
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
match *self.w.borrow() {
Some(w) => Plan::Fit(w),
None => Plan::Fill,
}
}
fn get_vertical_plan(&self) -> Plan {
match *self.h.borrow() {
Some(h) => Plan::Fit(h),
None => Plan::Fill,
}
}
}
impl RoundedRectangle {
pub fn new(w: Option<f32>, h: Option<f32>, x_rad: f32, y_rad: f32, paint: Paint) -> Rc<Self> {
Rc::new(Self {
w: RefCell::new(w),
h: RefCell::new(h),
x_rad: RefCell::new(x_rad),
y_rad: RefCell::new(y_rad),
p: paint,
planner: RefCell::new(None),
})
}
pub fn set_w(&self, w: Option<f32>) {
*self.w.borrow_mut() = w;
}
pub fn set_h(&self, h: Option<f32>) {
*self.h.borrow_mut() = h;
}
pub fn set_x_rad(&self, x_rad: f32) {
*self.x_rad.borrow_mut() = x_rad;
}
pub fn set_y_rad(&self, y_rad: f32) {
*self.y_rad.borrow_mut() = y_rad;
}
}
pub struct PathShape {
path: RefCell<Path>,
w: RefCell<f32>,
h: RefCell<f32>,
p: Paint,
planner: RefCell<Option<Rc<dyn Layout>>>,
}
impl HasPlanner for PathShape {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Drawable for PathShape {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let planned_area = self.get_planned_drawing_area(canvas);
let dest_w = planned_area.width();
let dest_h = planned_area.height();
let w = *self.w.borrow();
let h = *self.h.borrow();
if w <= 0.0 || h <= 0.0 || dest_w <= 0.0 || dest_h <= 0.0 {
return Ok(());
}
let scale_x = dest_w / w;
let scale_y = dest_h / h;
canvas.save();
canvas.translate(Point::new(planned_area.left, planned_area.top));
canvas.scale((scale_x, scale_y));
canvas.draw_path(&self.path.borrow(), &self.p);
canvas.restore();
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
Plan::Fit(*self.w.borrow())
}
fn get_vertical_plan(&self) -> Plan {
Plan::Fit(*self.h.borrow())
}
}
impl PathShape {
pub fn new(path: Path, w: f32, h: f32, paint: Paint) -> Rc<Self> {
Rc::new(Self {
path: RefCell::new(path),
w: RefCell::new(w),
h: RefCell::new(h),
p: paint,
planner: RefCell::new(None),
})
}
pub fn set_path(&self, path: Path) {
*self.path.borrow_mut() = path;
}
pub fn set_w(&self, w: f32) {
*self.w.borrow_mut() = w;
}
pub fn set_h(&self, h: f32) {
*self.h.borrow_mut() = h;
}
}
pub struct ArcShape {
w: RefCell<Option<f32>>,
h: RefCell<Option<f32>>,
start_angle: RefCell<f32>,
sweep_angle: RefCell<f32>,
p: Paint,
planner: RefCell<Option<Rc<dyn Layout>>>,
}
impl HasPlanner for ArcShape {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Drawable for ArcShape {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let planned_area = self.get_planned_drawing_area(canvas);
canvas.draw_arc(
planned_area,
*self.start_angle.borrow(),
*self.sweep_angle.borrow(),
false,
&self.p,
);
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
match *self.w.borrow() {
Some(w) => Plan::Fit(w),
None => Plan::Fill,
}
}
fn get_vertical_plan(&self) -> Plan {
match *self.h.borrow() {
Some(h) => Plan::Fit(h),
None => Plan::Fill,
}
}
}
impl ArcShape {
pub fn new(
w: Option<f32>,
h: Option<f32>,
start_angle: f32,
sweep_angle: f32,
paint: Paint,
) -> Rc<Self> {
Rc::new(Self {
w: RefCell::new(w),
h: RefCell::new(h),
start_angle: RefCell::new(start_angle),
sweep_angle: RefCell::new(sweep_angle),
p: paint,
planner: RefCell::new(None),
})
}
pub fn set_w(&self, w: Option<f32>) {
*self.w.borrow_mut() = w;
}
pub fn set_h(&self, h: Option<f32>) {
*self.h.borrow_mut() = h;
}
pub fn set_start_angle(&self, start_angle: f32) {
*self.start_angle.borrow_mut() = start_angle;
}
pub fn set_sweep_angle(&self, sweep_angle: f32) {
*self.sweep_angle.borrow_mut() = sweep_angle;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum HorizontalAlign {
Left,
Center,
Right,
}
pub struct Text {
text: RefCell<String>,
font: Font,
p: Paint,
align: HorizontalAlign,
cached_w: RefCell<f32>,
cached_h: RefCell<f32>,
planner: RefCell<Option<Rc<dyn Layout>>>,
}
impl HasPlanner for Text {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Drawable for Text {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let area = self.get_planned_drawing_area(canvas);
let text_w = *self.cached_w.borrow();
let x = match self.align {
HorizontalAlign::Left => area.left,
HorizontalAlign::Center => area.left + (area.width() - text_w) / 2.0,
HorizontalAlign::Right => area.right - text_w,
};
let (_, metrics) = self.font.metrics();
let center_y = area.top + area.height() / 2.0;
let baseline_y = center_y - (metrics.ascent + metrics.descent) / 2.0;
canvas.draw_str(
&*self.text.borrow(),
Point::new(x, baseline_y),
&self.font,
&self.p,
);
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
Plan::Fit(*self.cached_w.borrow())
}
fn get_vertical_plan(&self) -> Plan {
Plan::Fit(*self.cached_h.borrow())
}
}
impl Text {
pub fn new(text: &str, font: Font, paint: Paint, align: HorizontalAlign) -> Rc<Self> {
let instance = Self {
text: RefCell::new(text.to_string()),
font,
p: paint,
align,
cached_w: RefCell::new(0.0),
cached_h: RefCell::new(0.0),
planner: RefCell::new(None),
};
instance.recalculate_metrics(text);
Rc::new(instance)
}
pub fn set_text(&self, new_text: &str) {
if *self.text.borrow() == new_text {
return; }
self.recalculate_metrics(new_text);
*self.text.borrow_mut() = new_text.to_string();
}
fn recalculate_metrics(&self, text: &str) {
let mut glyphs = vec![skia_safe::GlyphId::default(); text.len()];
let actual_glyph_count = self.font.text_to_glyphs(text, &mut glyphs);
glyphs.truncate(actual_glyph_count);
let mut widths = vec![0.0f32; glyphs.len()];
self.font.get_widths(&glyphs, &mut widths);
let total_text_w: f32 = widths.iter().sum();
*self.cached_w.borrow_mut() = total_text_w;
let (_, metrics) = self.font.metrics();
*self.cached_h.borrow_mut() = metrics.descent - metrics.ascent;
}
}