use std::cell::Cell;
use std::rc::Rc;
use oxframe_2d::{BaseProps, Code, Muted, Node, NodeBuilder, NodeId, Normal, Txt};
use oxframe_core::{Seconds, Tween, all};
use crate::host;
pub struct CodeExplainer {
root: Node,
code: Code,
caption: Txt,
steps: Rc<Vec<(usize, usize, String)>>,
cursor: Rc<Cell<usize>>,
}
impl CodeExplainer {
#[allow(clippy::new_ret_no_self)]
pub fn new(code: impl Into<String>) -> CodeExplainerBuilder {
CodeExplainerBuilder {
base: BaseProps::default(),
code: code.into(),
steps: Vec::new(),
}
}
pub fn enter(&self) -> Tween<f64> {
self.root.fade_in()
}
pub fn next(&self) -> impl std::future::Future<Output = ()> + 'static {
let n = self.steps.len();
let i = self.cursor.get();
let (first, last, text) = if n == 0 {
(0, 0, String::new())
} else {
let step = &self.steps[i.min(n - 1)];
if i < n {
self.cursor.set(i + 1);
}
(step.0, step.1, step.2.clone())
};
self.caption.text().set(text);
let focus = self.code.focus_lines(first, last, Normal.seconds());
let fade = if i == 0 {
self.caption.fade_in()
} else {
self.caption.opacity().to(1.0, Normal)
};
async move {
all((focus, fade)).await;
}
}
pub fn exit(&self) -> Tween<f64> {
self.root.fade_out()
}
pub fn len(&self) -> usize {
self.steps.len()
}
pub fn is_empty(&self) -> bool {
self.steps.is_empty()
}
}
#[must_use = "a builder does nothing until it is passed to `add`"]
pub struct CodeExplainerBuilder {
base: BaseProps,
code: String,
steps: Vec<(usize, usize, String)>,
}
impl CodeExplainerBuilder {
pub fn step(mut self, first: usize, last: usize, caption: impl Into<String>) -> Self {
self.steps.push((first, last, caption.into()));
self
}
}
impl NodeBuilder for CodeExplainerBuilder {
type Handle = CodeExplainer;
fn base(&mut self) -> &mut BaseProps {
&mut self.base
}
fn build(self, _: &oxframe_2d::SceneGraph, parent: NodeId) -> CodeExplainer {
let root = host::root(parent, self.base);
let code = root.add(
Code::new()
.code(self.code)
.language("rust")
.font_size(26.0)
.y(-24.0)
.name("code"),
);
let (_, h) = code.size();
let caption = root.add(
Txt::new()
.text(" ")
.caption()
.fill(Muted)
.y(h / 2.0 + 36.0)
.opacity(0.0)
.name("caption"),
);
CodeExplainer {
root: root.node(),
code,
caption,
steps: Rc::new(self.steps),
cursor: Rc::new(Cell::new(0)),
}
}
}