Skip to content

Commit

Permalink
feat: finalize NG Text widget (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
shamilsan authored Oct 30, 2023
1 parent c508c34 commit 1438fec
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 21 deletions.
26 changes: 17 additions & 9 deletions examples/ng_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,40 @@ use lerni::ng::*;

#[component]
pub fn TextExample() -> impl IntoView {
let (words_read1, _) = create_signal(0);
let (words_read2, _) = create_signal(0);
let (words_read3, _) = create_signal(0);
let words_read1 = create_rw_signal(0);
let letters_read1 = create_rw_signal(0);
let letters_total1 = create_rw_signal(0);

let words_read2 = create_rw_signal(0);
let letters_read2 = create_rw_signal(0);
let letters_total2 = create_rw_signal(0);

let words_read3 = create_rw_signal(0);
let letters_read3 = create_rw_signal(0);
let letters_total3 = create_rw_signal(0);

view! {
<Slide>
<Row cols=2 padding=30 border_width=4>
<Column stretch={vec![5, 1]}>
<Text lattice=true>
<Text lattice=true words_read=words_read1 letters_read=letters_read1 letters_total=letters_total1>
{ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." }
{ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." }
</Text>
<Label>{words_read1}</Label>
<Label>{words_read1} "w (" {letters_read1} " / " {letters_total1} ")"</Label>
</Column>

<Column stretch={vec![5, 1, 5, 1]}>
<Text font_size=48 bold=true font="serif">
<Text font_size=48 bold=true font="serif" words_read=words_read2 letters_read=letters_read2 letters_total=letters_total2>
{ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur." }
{ "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }
</Text>
<Label>{words_read2}</Label>
<Text font_size=48 erase_top=0.3>
<Label>{words_read2} "w (" {letters_read2} " / " {letters_total2} ")"</Label>
<Text font_size=48 erase_top=0.3 words_read=words_read3 letters_read=letters_read3 letters_total=letters_total3>
{ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur." }
{ "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }
</Text>
<Label>{words_read3}</Label>
<Label>{words_read3} "w (" {letters_read3} " / " {letters_total3} ")"</Label>
</Column>
</Row>
</Slide>
Expand Down
13 changes: 13 additions & 0 deletions src/ng/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ pub struct Frame {
pub height: i32,
}

/// SVG frame area.
#[derive(Clone, Default, Debug)]
pub struct SvgFrame {
/// SVG frame width (in viewbox pixels).
pub width: i32,
/// SVG frame height (in viewbox pixels).
pub height: i32,
/// Client area width (in screen pixels).
pub client_width: i32,
/// Client area height (in screen pixels).
pub client_height: i32,
}

/// Frames stack.
#[derive(Clone, Default, Debug)]
pub struct Frames(VecDeque<Frame>);
Expand Down
8 changes: 7 additions & 1 deletion src/ng/slide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use leptos::{
};
use leptos_use::*;

use crate::ng::{provide_frame, Color, Frame, Metadata};
use crate::ng::{provide_frame, Color, Frame, Metadata, SvgFrame};

const WIDTH: i32 = 1920;
const HEIGHT: i32 = 1080;
Expand Down Expand Up @@ -49,6 +49,12 @@ pub fn Slide(
if let Some(svg) = svg_ref.get() {
px = px * WIDTH / svg.client_width();
py = py * HEIGHT / svg.client_height();
provide_context(SvgFrame {
width: WIDTH,
height: HEIGHT,
client_width: svg.client_width(),
client_height: svg.client_height(),
});
}
set_pointer_position.set((px, py));
};
Expand Down
50 changes: 39 additions & 11 deletions src/ng/text.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use leptos::*;
use std::rc::Rc;
use wasm_bindgen::JsValue;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, MouseEvent};

use crate::ng::{use_frame, Color, Frame};
use crate::ng::{use_frame, Color, Frame, SvgFrame};

struct TextProperties<'a> {
bold: bool,
Expand All @@ -12,6 +13,7 @@ struct TextProperties<'a> {
indent: f32,
}

#[derive(Clone)]
struct Rect {
pub x: i32,
pub y: i32,
Expand All @@ -21,7 +23,7 @@ struct Rect {

struct Output {
pub words: Vec<String>,
pub rects: Vec<Rect>,
pub rects: Rc<Vec<Rect>>,
pub letter_counters: Vec<usize>,
}

Expand All @@ -35,10 +37,12 @@ pub fn Text(
#[prop(default = 1.2)] line_height: f32,
#[prop(default = 1.4)] indent: f32,
#[prop(default = Color::PaleGreen)] marker_color: Color,
#[prop(optional)] words_read: usize,
#[prop(optional)] lattice: bool,
#[prop(optional)] erase_top: f32,
#[prop(optional)] erase_bottom: f32,
#[prop(optional, into)] words_read: RwSignal<usize>,
#[prop(optional, into)] letters_read: RwSignal<usize>,
#[prop(optional, into)] letters_total: RwSignal<usize>,
children: Children,
) -> impl IntoView {
let props = TextProperties {
Expand All @@ -57,11 +61,12 @@ pub fn Text(
letter_counters,
} = wrap(&children, &canvas, &props, &f);

let _total_letters: usize = letter_counters.iter().sum();
letters_total.set(letter_counters.iter().sum());

let word = |(i, r): (usize, &Rect)| {
let word = |i, r: &Rect, hidden| {
view! {
<text
visibility=move || { (hidden && i >= words_read.get()).then_some("hidden") }
x=r.x + r.width / 2
y=r.y + r.height / 2
class:has-text-weight-bold=bold
Expand Down Expand Up @@ -95,10 +100,26 @@ pub fn Text(
}
};

let r = Rc::clone(&rects);
let on_click = move |e: MouseEvent| {
let svg: Option<SvgFrame> = use_context();
if let Some(svg) = svg {
let x = e.offset_x() * svg.width / svg.client_width;
let y = e.offset_y() * svg.height / svg.client_height;
if x >= f.x && x <= f.x + f.width && y >= f.y && y <= f.y + f.height {
if let Some(index) = find_word_index(x, y, &r) {
words_read.set(index + 1);
letters_read.set(letter_counters[0..index + 1].iter().sum());
}
}
}
};

let expand = text_width(" ", &canvas) / 2 + 1;

view! {
{rects.iter().enumerate().map(word).collect_view()}
<rect x=f.x y=f.y width=f.width height=f.height fill="white" on:click=on_click></rect>
{rects.iter().enumerate().map(|(i, r)| word(i, r, false)).collect_view()}
{(erase_top > 0.0 || erase_bottom > 0.0)
.then(|| { rects.iter().map(erase).collect_view() })}

Expand Down Expand Up @@ -128,10 +149,11 @@ pub fn Text(

{rects
.iter()
.take(words_read)
.map(|r| {
.enumerate()
.map(|(i, r)| {
view! {
<rect
visibility=move || { (i >= words_read.get()).then_some("hidden") }
x=r.x - expand
y=r.y - expand
width=r.width + 2 * expand
Expand All @@ -145,7 +167,7 @@ pub fn Text(
})
.collect_view()}

{rects.iter().take(words_read).enumerate().map(word).collect_view()}
{rects.iter().enumerate().map(|(i, r)| word(i, r, true)).collect_view()}
}
}

Expand Down Expand Up @@ -236,8 +258,14 @@ fn wrap(
y += dy;
}
Output {
rects,
rects: Rc::new(rects),
words,
letter_counters,
}
}

fn find_word_index(x: i32, y: i32, rects: &[Rect]) -> Option<usize> {
rects
.iter()
.position(|r| x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
}

0 comments on commit 1438fec

Please sign in to comment.