pub mod linechart;
pub mod ringchart;
use crate::drawing::sprites::{HorizontalAlign, Text};
use crate::drawing::{Drawable, Plan, RoundedRectangle};
use crate::interacting::{Event, Interactive};
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::any::Any;
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>,
enabled: RefCell<bool>,
}
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)
}
fn as_layout(&self) -> &dyn Layout {
self
}
fn foreach_child(&self, f: &mut dyn FnMut(&dyn Drawable)) {
f(self.internal_layout.as_ref());
f(self.video_title.as_ref());
f(self.video_subtitle.as_ref());
f(self.cpu_chart.as_ref());
f(self.gpu_chart.as_ref());
f(self.vram_ring.as_ref());
f(self.ram_ring.as_ref());
}
}
impl Interactive for TopBar {
fn handle_and_route(&self, e: &Event, u: &dyn Any) {
self.foreach_child(&mut |x| {
if x.is_enabled()
&& let Some(i) = x.try_to_interactive()
{
i.handle_and_route(e, u);
}
});
}
}
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::FillPositive
}
fn get_vertical_plan(&self) -> Plan {
Plan::Fit(55.0)
}
fn replan(&self) {
self.foreach_child(&mut |x| x.replan());
}
fn try_to_interactive(&self) -> Option<&dyn Interactive> {
Some(self)
}
fn is_enabled(&self) -> bool {
*self.enabled.borrow()
}
fn set_enabled(&self, e: bool) {
*self.enabled.borrow_mut() = e;
}
fn as_drawable(&self) -> &dyn Drawable {
self
}
}
impl TopBar {
pub fn new(font: &Font, meta: &VideoMetadata, hw: &HwStats) -> Rc<Self> {
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(Plan::FillPositive, Plan::FillPositive, 12.0, 12.0, bg_paint);
let border = RoundedRectangle::new(
Plan::FillPositive,
Plan::FillPositive,
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 chart_y_margin = (55.0 - 32.0) / 2.0;
let internal_layout = EdgeLayout::new(true);
let internal_layout = internal_layout
.add_child(
"bg",
EdgeLayoutElement {
drawable: bg,
l_edge: Edges::Left,
l_margin: 0.0,
t_edge: Edges::Top,
t_margin: 0.0,
r_edge: Edges::Right,
r_margin: 0.0,
b_edge: Edges::Bottom,
b_margin: 0.0,
},
)
.add_child(
"border",
EdgeLayoutElement {
drawable: border,
l_edge: Edges::Left,
l_margin: 0.0,
t_edge: Edges::Top,
t_margin: 0.0,
r_edge: Edges::Right,
r_margin: 0.0,
b_edge: Edges::Bottom,
b_margin: 0.0,
},
)
.add_child(
"title",
EdgeLayoutElement {
drawable: video_title.clone(),
l_edge: Edges::Left,
l_margin: 24.0,
t_edge: Edges::Top,
t_margin: 14.0,
r_edge: Edges::Right,
r_margin: 300.0,
b_edge: Edges::Bottom,
b_margin: 20.0,
},
)
.add_child(
"subtitle",
EdgeLayoutElement {
drawable: video_subtitle.clone(),
l_edge: Edges::Left,
l_margin: 24.0,
t_edge: Edges::Top,
t_margin: 34.0,
r_edge: Edges::Right,
r_margin: 300.0,
b_edge: Edges::Bottom,
b_margin: 0.0,
},
)
.add_child(
"gpu_chart",
EdgeLayoutElement {
drawable: gpu_chart.clone(),
l_edge: Edges::Right,
l_margin: 114.0,
t_edge: Edges::Top,
t_margin: chart_y_margin,
r_edge: Edges::Right,
r_margin: 24.0,
b_edge: Edges::Bottom,
b_margin: chart_y_margin,
},
)
.add_child(
"gpu_label",
EdgeLayoutElement {
drawable: gpu_label,
l_edge: Edges::Right,
l_margin: 149.0,
t_edge: Edges::Top,
t_margin: chart_y_margin,
r_edge: Edges::Right,
r_margin: 119.0,
b_edge: Edges::Bottom,
b_margin: chart_y_margin,
},
)
.add_child(
"cpu_chart",
EdgeLayoutElement {
drawable: cpu_chart.clone(),
l_edge: Edges::Right,
l_margin: 254.0,
t_edge: Edges::Top,
t_margin: chart_y_margin,
r_edge: Edges::Right,
r_margin: 164.0,
b_edge: Edges::Bottom,
b_margin: chart_y_margin,
},
)
.add_child(
"cpu_label",
EdgeLayoutElement {
drawable: cpu_label,
l_edge: Edges::Right,
l_margin: 289.0,
t_edge: Edges::Top,
t_margin: chart_y_margin,
r_edge: Edges::Right,
r_margin: 259.0,
b_edge: Edges::Bottom,
b_margin: chart_y_margin,
},
)
.add_child(
"vram_ring",
EdgeLayoutElement {
drawable: vram_ring.clone(),
l_edge: Edges::Right,
l_margin: 326.0,
t_edge: Edges::Top,
t_margin: chart_y_margin,
r_edge: Edges::Right,
r_margin: 294.0,
b_edge: Edges::Bottom,
b_margin: chart_y_margin,
},
)
.add_child(
"ram_ring",
EdgeLayoutElement {
drawable: ram_ring.clone(),
l_edge: Edges::Right,
l_margin: 368.0,
t_edge: Edges::Top,
t_margin: chart_y_margin,
r_edge: Edges::Right,
r_margin: 336.0,
b_edge: Edges::Bottom,
b_margin: chart_y_margin,
},
);
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,
enabled: RefCell::new(true),
});
internal_layout.set_planner(Some(instance.clone() as Rc<dyn Layout>));
instance
}
pub fn set_state(&self, meta: &VideoMetadata, hw: &HwStats, fps: f32) {
self.video_title.set_text(&meta.video_filename);
let sub_info = format!(
"Codec: {} | FPS: {:.2} | Size: {}x{} | Rendering FPS: {:.2}",
meta.codec_name, meta.frame_rate, meta.sv_sz.0, meta.sv_sz.1, fps
);
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();
}
}