mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-04 09:29:49 -06:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
875029b502 |
@@ -247,6 +247,11 @@ pub fn repair(p: &mut Presentation) {
|
||||
if !(p.notes_size.width.is_finite() && p.notes_size.width > 0.0 && p.notes_size.height.is_finite() && p.notes_size.height > 0.0) {
|
||||
p.notes_size = Presentation::default().notes_size;
|
||||
}
|
||||
// A notes page is bounded like a slide: canvas and handout drawing scale with it.
|
||||
p.notes_size = deckcraft_model::Size::new(
|
||||
p.notes_size.width.clamp(*SLIDE_SIDE.start(), *SLIDE_SIDE.end()),
|
||||
p.notes_size.height.clamp(*SLIDE_SIDE.start(), *SLIDE_SIDE.end()),
|
||||
);
|
||||
sane_numbers(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -451,6 +451,13 @@ fn degenerate_slide_sizes_are_repaired() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repair_bounds_the_notes_page_size() {
|
||||
let mut p = Presentation { notes_size: deckcraft_model::Size::new(1e12, 0.01), ..Default::default() };
|
||||
repair(&mut p);
|
||||
assert_eq!((p.notes_size.width, p.notes_size.height), (4032.0, 72.0));
|
||||
}
|
||||
|
||||
/// A file whose slide size is null, a string or missing a side (written by hand or by
|
||||
/// another tool; NaN is also saved as null) opens with the default size rather than failing
|
||||
/// as damaged.
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
|
||||
use deckcraft_model::edit;
|
||||
use deckcraft_model::text::{Bullet, Paragraph, RunProps, Strike, TextBody};
|
||||
use egui::text::{Galley, LayoutJob, TextFormat};
|
||||
use egui::text::{CCursor, Galley, LayoutJob, TextFormat};
|
||||
use egui::{Align2, Color32, Stroke, Ui, vec2};
|
||||
|
||||
use crate::theme;
|
||||
@@ -210,30 +210,16 @@ pub fn editor(
|
||||
out
|
||||
}
|
||||
|
||||
/// The (min y, max y) of the row holding char index `at`; `walk` (row index, chars through that
|
||||
/// row) carries over between calls, so successive non-decreasing `at`s cost one pass in all.
|
||||
fn row_span_at(galley: &Galley, walk: &mut (usize, usize), at: usize) -> (f32, f32) {
|
||||
while walk.1 <= at && walk.0 + 1 < galley.rows.len() {
|
||||
walk.0 += 1;
|
||||
walk.1 += galley.rows[walk.0].char_count_including_newline().0;
|
||||
}
|
||||
galley.rows.get(walk.0).map_or((0.0, 0.0), |r| (r.min_y(), r.max_y()))
|
||||
}
|
||||
|
||||
/// Bullets and numbers at the start of each paragraph's first line of `galley` (laid out from
|
||||
/// `body`), drawn at `origin`.
|
||||
fn paint_bullets(painter: &egui::Painter, body: &TextBody, galley: &Galley, origin: egui::Pos2, size: f32, color: Color32) {
|
||||
let k = size / 12.0;
|
||||
let mut at = 0usize;
|
||||
// Paragraph starts only grow, so one forward walk over the rows finds each start's row
|
||||
// (`pos_from_cursor` would rescan from the top for every bullet).
|
||||
let mut walk = (0usize, galley.rows.first().map_or(0, |r| r.char_count_including_newline().0));
|
||||
for (p, bullet) in body.paragraphs.iter().zip(bullets(body)) {
|
||||
if let Some(b) = bullet {
|
||||
let (min_y, max_y) = row_span_at(galley, &mut walk, at);
|
||||
let (center_y, height) = ((min_y + max_y) / 2.0, max_y - min_y);
|
||||
let pos = origin + vec2(px(indents(p).0, k), center_y);
|
||||
let font = theme::font(height.clamp(size * 0.75, size * 2.0) * 0.8);
|
||||
let row = galley.pos_from_cursor(CCursor::new(at));
|
||||
let pos = origin + vec2(px(indents(p).0, k), row.center().y);
|
||||
let font = theme::font(row.height().clamp(size * 0.75, size * 2.0) * 0.8);
|
||||
painter.text(pos, Align2::LEFT_CENTER, b, font, color);
|
||||
}
|
||||
at += p.char_len() + 1;
|
||||
@@ -255,7 +241,6 @@ mod tests {
|
||||
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
||||
|
||||
use deckcraft_model::text::{ParaProps, Run};
|
||||
use egui::text::CCursor;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -332,23 +317,6 @@ mod tests {
|
||||
assert!(wrapped.iter().all(|r| (r.pos.x - 22.5).abs() < 1.0), "wrapped lines align with the text");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn row_walk_matches_cursor_lookup_at_paragraph_starts() {
|
||||
let ctx = egui::Context::default();
|
||||
let body = notes();
|
||||
let mut laid = None;
|
||||
let mut out = ctx.run_ui(Default::default(), |ui| laid = Some(galley(ui, &body, 12.0, Color32::BLACK, 300.0)));
|
||||
out.textures_delta.clear();
|
||||
let g = laid.unwrap();
|
||||
let mut walk = (0usize, g.rows.first().map_or(0, |r| r.char_count_including_newline().0));
|
||||
let mut at = 0usize;
|
||||
for p in &body.paragraphs {
|
||||
let want = g.pos_from_cursor(CCursor::new(at));
|
||||
assert_eq!(row_span_at(&g, &mut walk, at), (want.min.y, want.max.y), "paragraph at char {at}");
|
||||
at += p.char_len() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typing_keeps_the_formatting_around_it() {
|
||||
let mut d = Draft::new(¬es());
|
||||
|
||||
Reference in New Issue
Block a user