pub mod linechart;
pub mod ringchart;
use crate::drawing::sprites::{HorizontalAlign, Text};
use crate::drawing::{Drawable, Plan, RoundedRectangle};
use crate::layouts::{EdgeLayout, EdgeLayoutElement, Edges, HasPlanner, Layout};
use crate::story_board::{StoryBoard, easing};
use crate::sys_monitor::HwStats;
use linechart::LineChart;
use ringchart::RingProgressChart;
use skia_safe::{Canvas, Color, Color4f, Font, Paint, PaintStyle, Point, Rect};
use std::cell::RefCell;
use std::rc::Rc;
pub struct VideoMetadata {
pub video_filename: String,
pub codec_name: String,
pub frame_rate: f64,
pub sv_sz: (i32, i32),
}
pub struct TopBar {
planner: RefCell<Option<Rc<dyn Layout>>>,
sh_state: RefCell<StoryBoard>,
internal_layout: Rc<EdgeLayout>,
video_title: Rc<Text>,
video_subtitle: Rc<Text>,
cpu_chart: Rc<LineChart>,
gpu_chart: Rc<LineChart>,
vram_ring: Rc<RingProgressChart>,
ram_ring: Rc<RingProgressChart>,
}
impl HasPlanner for TopBar {
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 Layout for TopBar {
fn measure_child(&self, _c: &dyn Drawable, canvas: &Canvas) -> Rect {
self.get_planned_drawing_area(canvas)
}
}
impl Drawable for TopBar {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let anim_offset_y = -(f64::from(&*self.sh_state.borrow()) as f32);
canvas.save();
canvas.translate(Point::new(0.0, anim_offset_y));
self.internal_layout.draw(canvas)?;
canvas.restore();
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
Plan::Fill
}
fn get_vertical_plan(&self) -> Plan {
Plan::Fit(55.0)
}
}
impl TopBar {
pub fn new(font: &Font, meta: &VideoMetadata, hw: &HwStats) -> Rc<Self> {
let internal_layout = EdgeLayout::new();
let mut bg_paint = Paint::default();
bg_paint
.set_anti_alias(true)
.set_color4f(Color4f::new(0.08, 0.08, 0.1, 0.85), None);
let mut border_paint = Paint::default();
border_paint
.set_anti_alias(true)
.set_style(PaintStyle::Stroke)
.set_stroke_width(1.0)
.set_color4f(Color4f::new(1.0, 1.0, 1.0, 0.15), None);
let mut text_paint = Paint::default();
text_paint.set_anti_alias(true).set_color(Color::WHITE);
let mut subtext_paint = Paint::default();
subtext_paint
.set_anti_alias(true)
.set_color4f(Color4f::new(0.7, 0.7, 0.75, 1.0), None);
let mut sub_font = font.clone();
sub_font.set_size(14.0);
let bg = RoundedRectangle::new(None, None, 12.0, 12.0, bg_paint);
let border = RoundedRectangle::new(None, None, 12.0, 12.0, border_paint);
let video_title = Text::new(
&meta.video_filename,
font.clone(),
text_paint.clone(),
HorizontalAlign::Left,
);
let sub_info = format!(
"Codec: {} | FPS: {:.2} | Size: {}x{}",
meta.codec_name, meta.frame_rate, meta.sv_sz.0, meta.sv_sz.1
);
let video_subtitle = Text::new(
&sub_info,
sub_font.clone(),
subtext_paint,
HorizontalAlign::Left,
);
let gpu_colors = [
Color::from_argb(255, 255, 42, 133),
Color::from_argb(255, 138, 43, 226),
];
let gpu_chart = LineChart::new(hw.gpu_history.clone(), gpu_colors);
let cpu_colors = [
Color::from_argb(255, 0, 212, 255),
Color::from_argb(255, 41, 121, 255),
];
let cpu_chart = LineChart::new(hw.cpu_history.clone(), cpu_colors);
let vram_ring = RingProgressChart::new(
hw.vram_usage,
"VR",
Color::from_rgb(138, 43, 226),
font.clone(),
);
let ram_ring = RingProgressChart::new(
hw.mem_usage,
"RAM",
Color::from_rgb(0, 212, 255),
font.clone(),
);
let gpu_label = Text::new(
"GPU",
sub_font.clone(),
text_paint.clone(),
HorizontalAlign::Left,
);
let cpu_label = Text::new(
"CPU",
sub_font.clone(),
text_paint.clone(),
HorizontalAlign::Left,
);
let add = |id: &str, dr: Rc<dyn Drawable>, l: f32, t: f32, r: f32, b: f32| {
internal_layout.add_child(
id,
EdgeLayoutElement {
drawable: dr,
l_edge: Edges::Left,
l_margin: l,
t_edge: Edges::Top,
t_margin: t,
r_edge: Edges::Right,
r_margin: r,
b_edge: Edges::Bottom,
b_margin: b,
},
);
};
add("bg", bg, 0.0, 0.0, 0.0, 0.0);
add("border", border, 0.0, 0.0, 0.0, 0.0);
add("title", video_title.clone(), 24.0, 14.0, 300.0, 20.0);
add("subtitle", video_subtitle.clone(), 24.0, 34.0, 300.0, 0.0);
let chart_y_margin = (55.0 - 32.0) / 2.0;
add(
"gpu_chart",
gpu_chart.clone(),
0.0,
chart_y_margin,
24.0,
chart_y_margin,
);
internal_layout
.rewrite_child(
"gpu_chart",
Edges::Right,
114.0,
Edges::Top,
chart_y_margin,
Edges::Right,
24.0,
Edges::Bottom,
chart_y_margin,
)
.unwrap();
add(
"gpu_label",
gpu_label,
0.0,
chart_y_margin,
114.0 + 35.0,
chart_y_margin,
);
internal_layout
.rewrite_child(
"gpu_label",
Edges::Right,
149.0,
Edges::Top,
chart_y_margin,
Edges::Right,
119.0,
Edges::Bottom,
chart_y_margin,
)
.unwrap();
add(
"cpu_chart",
cpu_chart.clone(),
0.0,
chart_y_margin,
164.0,
chart_y_margin,
);
internal_layout
.rewrite_child(
"cpu_chart",
Edges::Right,
254.0,
Edges::Top,
chart_y_margin,
Edges::Right,
164.0,
Edges::Bottom,
chart_y_margin,
)
.unwrap();
add(
"cpu_label",
cpu_label,
0.0,
chart_y_margin,
254.0 + 35.0,
chart_y_margin,
);
internal_layout
.rewrite_child(
"cpu_label",
Edges::Right,
289.0,
Edges::Top,
chart_y_margin,
Edges::Right,
259.0,
Edges::Bottom,
chart_y_margin,
)
.unwrap();
add(
"vram_ring",
vram_ring.clone(),
0.0,
chart_y_margin,
294.0,
chart_y_margin,
);
internal_layout
.rewrite_child(
"vram_ring",
Edges::Right,
326.0,
Edges::Top,
chart_y_margin,
Edges::Right,
294.0,
Edges::Bottom,
chart_y_margin,
)
.unwrap();
add(
"ram_ring",
ram_ring.clone(),
0.0,
chart_y_margin,
336.0,
chart_y_margin,
);
internal_layout
.rewrite_child(
"ram_ring",
Edges::Right,
368.0,
Edges::Top,
chart_y_margin,
Edges::Right,
336.0,
Edges::Bottom,
chart_y_margin,
)
.unwrap();
let instance = Rc::new(Self {
planner: RefCell::new(None),
sh_state: RefCell::new(StoryBoard::init(
100f64,
-20f64,
500f64,
false,
easing::cubic_in_out,
)),
internal_layout: internal_layout.clone(),
video_title,
video_subtitle,
cpu_chart,
gpu_chart,
vram_ring,
ram_ring,
});
internal_layout.set_planner(Some(instance.clone() as Rc<dyn Layout>));
instance
}
pub fn set_state(&self, meta: &VideoMetadata, hw: &HwStats) {
self.video_title.set_text(&meta.video_filename);
let sub_info = format!(
"Codec: {} | FPS: {:.2} | Size: {}x{}",
meta.codec_name, meta.frame_rate, meta.sv_sz.0, meta.sv_sz.1
);
self.video_subtitle.set_text(&sub_info);
*self.cpu_chart.history.borrow_mut() = hw.cpu_history.clone();
*self.gpu_chart.history.borrow_mut() = hw.gpu_history.clone();
*self.vram_ring.pct.borrow_mut() = hw.vram_usage;
*self.ram_ring.pct.borrow_mut() = hw.mem_usage;
}
pub fn to_layout_element(self: &Rc<Self>) -> EdgeLayoutElement {
EdgeLayoutElement {
drawable: self.clone(),
l_edge: Edges::Left,
l_margin: 40.0,
r_edge: Edges::Right,
r_margin: 40.0,
t_edge: Edges::Top,
t_margin: 0.0,
b_edge: Edges::Top,
b_margin: 55.0,
}
}
pub fn toggle_sh(&self) {
let mut sh = self.sh_state.borrow_mut();
sh.reverse();
sh.restart();
}
}