From c109c605fa5a6cd3cda6f3f28782eb870b751495 Mon Sep 17 00:00:00 2001 From: Lucas Timmins Date: Thu, 25 May 2023 02:19:08 +0800 Subject: [PATCH 01/12] WIP --- Cargo.lock | 68 ++++++++++++++++++++---------- Cargo.toml | 4 ++ src/main.rs | 1 + src/positioner.rs | 44 ++++++++----------- src/renderer.rs | 85 ++++++++++++++++--------------------- src/table.rs | 105 +++++++++++++++++++++++++++++++++++++++++++++- src/text.rs | 89 +++++++++++++++++++++++++++++++++++++++ temp.md | 4 ++ 8 files changed, 300 insertions(+), 100 deletions(-) create mode 100644 temp.md diff --git a/Cargo.lock b/Cargo.lock index c4d3724a..676a0b96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "3f1e31e207a6b8fb791a38ea3105e6cb541f55e4d029902d3039a4ad07cc4105" [[package]] name = "bincode" @@ -371,9 +371,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytemuck" @@ -559,9 +559,9 @@ dependencies = [ [[package]] name = "console" -version = "0.15.6" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0525278dce688103060006713371cedbad27186c7d913f33d866b498da0f595" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", @@ -1122,7 +1122,7 @@ checksum = "af8d8cbea8f21307d7e84bca254772981296f058a1d36b461bf4d83a7499fc9e" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.6.1", + "memmap2 0.6.2", "slotmap", "tinyvec", "ttf-parser 0.19.0", @@ -1380,6 +1380,12 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "grid" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c" + [[package]] name = "h2" version = "0.3.19" @@ -1643,7 +1649,7 @@ dependencies = [ "log", "lyon", "lz4_flex", - "memmap2 0.6.1", + "memmap2 0.6.2", "notify", "open", "pollster", @@ -1652,6 +1658,7 @@ dependencies = [ "resvg", "serde", "serde_yaml", + "taffy", "tiny-skia 0.9.1", "toml", "twox-hash", @@ -1707,9 +1714,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", @@ -2038,9 +2045,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aa1b505aeecb0adb017db2b6a79a17a38e64f882a201f05e9de8a982cd6096" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" dependencies = [ "libc", ] @@ -2947,13 +2954,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.1", + "regex-syntax 0.7.2", ] [[package]] @@ -2964,9 +2971,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "renderdoc-sys" @@ -3537,6 +3544,17 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "taffy" +version = "0.3.11" +source = "git+https://github.com/DioxusLabs/taffy#5443996198f02cf5a6c6493499df3cf08394a70f" +dependencies = [ + "arrayvec", + "grid", + "num-traits", + "slotmap", +] + [[package]] name = "tempfile" version = "3.5.0" @@ -3757,9 +3775,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.9" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d964908cec0d030b812013af25a0e57fddfadb1e066ecc6681d86253129d4f" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "serde", @@ -3883,9 +3901,9 @@ checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-linebreak" @@ -4663,9 +4681,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.11" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1690519550bfa95525229b9ca2350c63043a4857b3b0013811b2ccf4a2420b01" +checksum = "2d8f380ae16a37b30e6a2cf67040608071384b1450c189e61bea3ff57cde922d" [[package]] name = "xmlparser" @@ -4823,3 +4841,7 @@ dependencies = [ "quote", "syn 1.0.109", ] + +[[patch.unused]] +name = "cosmic-text" +version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 80425852..b4707244 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ indexmap = { version = "1.9.3", features = ["serde"] } html-escape = "0.2.13" fxhash = "0.2.1" twox-hash = "1.6.3" +taffy = { git="https://github.com/DioxusLabs/taffy" } [dependencies.glyphon] version = "0.2" @@ -67,3 +68,6 @@ lto = true [dev-dependencies] insta = "1.29.0" pretty_assertions = "1.3.0" + +[patch.crates-io] +cosmic-text = { path = "../cosmic-text" } diff --git a/src/main.rs b/src/main.rs index 8117f41b..d540a805 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(default_free_fn)] pub mod color; pub mod fonts; pub mod image; diff --git a/src/positioner.rs b/src/positioner.rs index 5ed0f6f2..f863effa 100644 --- a/src/positioner.rs +++ b/src/positioner.rs @@ -3,7 +3,6 @@ use std::{cell::RefCell, collections::HashMap}; use anyhow::Context; use crate::{ - table::{TABLE_COL_GAP, TABLE_ROW_GAP}, text::{TextBox, TextSystem}, utils::{Align, Point, Rect, Size}, Element, @@ -107,33 +106,24 @@ impl Positioner { } Element::Table(table) => { let pos = (DEFAULT_MARGIN + centering, self.reserved_height); - let width = table - .column_widths( - text_system, - ( - self.screen_size.0 - pos.0 - DEFAULT_MARGIN - centering, - f32::INFINITY, - ), - zoom, - ) - .iter() - .fold(0., |acc, x| acc + x); - let height = table - .row_heights( - text_system, - ( - self.screen_size.0 - pos.0 - DEFAULT_MARGIN - centering, - f32::INFINITY, - ), - zoom, - ) - .iter() - .fold(0., |acc, x| acc + x); - Rect::new( - pos, + let layout = table.layout( + text_system, + ( + self.screen_size.0 - pos.0 - DEFAULT_MARGIN - centering, + f32::INFINITY, + ), + zoom, + )?; + let min = layout.first().unwrap().first().unwrap(); + let max = layout.last().unwrap().last().unwrap(); + Rect::from_min_max( + ( + DEFAULT_MARGIN + centering + min.location.x, + self.reserved_height + min.location.y, + ), ( - width * (TABLE_COL_GAP * table.headers.len() as f32), - height + (TABLE_ROW_GAP * (table.rows.len() + 1) as f32), + DEFAULT_MARGIN + centering + max.location.x + max.size.width, + self.reserved_height + max.location.y + max.size.height, ), ) } diff --git a/src/renderer.rs b/src/renderer.rs index 6f693041..fcbbbcd6 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -3,7 +3,7 @@ use crate::fonts::get_fonts; use crate::image::ImageRenderer; use crate::opts::FontOptions; use crate::positioner::{Positioned, Positioner, DEFAULT_MARGIN}; -use crate::table::{TABLE_COL_GAP, TABLE_ROW_GAP}; +use crate::table::TABLE_ROW_GAP; use crate::text::{CachedTextArea, TextCache, TextSystem}; use crate::utils::{Point, Rect, Selection, Size}; use crate::Element; @@ -358,45 +358,26 @@ impl Renderer { } } Element::Table(table) => { - let row_heights = table.row_heights( - &mut self.text_system, - ( - screen_size.0 - pos.0 - DEFAULT_MARGIN - centering, - f32::INFINITY, - ), - self.zoom, - ); - let column_widths = table.column_widths( - &mut self.text_system, - ( - screen_size.0 - pos.0 - DEFAULT_MARGIN - centering, - f32::INFINITY, - ), - self.zoom, + let bounds = ( + (screen_size.0 - pos.0 - DEFAULT_MARGIN - centering).max(0.), + f32::INFINITY, ); - let mut x = 0.; - let mut y = 0.; + let layout = table.layout(&mut self.text_system, bounds, self.zoom)?; - let header_height = row_heights.first().unwrap(); - for (col, width) in column_widths.iter().enumerate() { + for (col, node) in layout[0].iter().enumerate() { if let Some(text_box) = table.headers.get(col) { - let bounds = ( - (screen_size.0 - pos.0 - x - DEFAULT_MARGIN - centering) - .min(screen_size.0 - DEFAULT_MARGIN - centering), - f32::INFINITY, - ); text_areas.push(text_box.text_areas( &mut self.text_system, - (pos.0 + x, pos.1 + y), - bounds, + (pos.0 + node.location.x, pos.1 + node.location.y), + (node.size.width, f32::MAX), self.zoom, self.scroll_y, )); if let Some(selection) = self.selection { let (selection_rects, selection_text) = text_box.render_selection( &mut self.text_system, - (pos.0 + x, pos.1 + y), - bounds, + (pos.0 + node.location.x, pos.1 + node.location.y), + (node.size.width, node.size.height), self.zoom, selection, ); @@ -412,10 +393,17 @@ impl Renderer { )?; } } - x += width + TABLE_COL_GAP; } } - y += header_height + (TABLE_ROW_GAP / 2.); + let last_header_node = layout[0].last().unwrap(); + let y = last_header_node.location.y + + last_header_node.size.height + + TABLE_ROW_GAP / 2.; + let x = layout + .first() + .and_then(|f| f.last()) + .map(|f| f.location.x + f.size.width) + .unwrap_or(0.); { let min = ( scrolled_pos.0.max(DEFAULT_MARGIN + centering), @@ -423,7 +411,7 @@ impl Renderer { ); let max = ( (scrolled_pos.0 + x), - scrolled_pos.1 + y + 1. * self.hidpi_scale * self.zoom, + scrolled_pos.1 + y + 2. * self.hidpi_scale * self.zoom, ); self.draw_rectangle( Rect::from_min_max(min, max), @@ -431,20 +419,14 @@ impl Renderer { )?; } - y += TABLE_ROW_GAP / 2.; - for (row, height) in row_heights.iter().skip(1).enumerate() { - let mut x = 0.; - for (col, width) in column_widths.iter().enumerate() { + for (row, node_row) in layout.iter().skip(1).enumerate() { + for (col, node) in node_row.iter().enumerate() { if let Some(row) = table.rows.get(row) { if let Some(text_box) = row.get(col) { - let bounds = ( - screen_size.0 - pos.0 - x - DEFAULT_MARGIN - centering, - f32::INFINITY, - ); text_areas.push(text_box.text_areas( &mut self.text_system, - (pos.0 + x, pos.1 + y), - bounds, + (pos.0 + node.location.x, pos.1 + node.location.y), + (node.size.width, f32::MAX), self.zoom, self.scroll_y, )); @@ -453,8 +435,8 @@ impl Renderer { let (selection_rects, selection_text) = text_box .render_selection( &mut self.text_system, - (pos.0 + x, pos.1 + y), - bounds, + (pos.0 + node.location.x, pos.1 + node.location.y), + (node.size.width, node.size.height), self.zoom, selection, ); @@ -475,24 +457,29 @@ impl Renderer { } } } - x += width + TABLE_COL_GAP; } - y += height + (TABLE_COL_GAP / 2.); + let last_row_node = node_row.last().unwrap(); + let y = last_row_node.location.y + + last_row_node.size.height + + TABLE_ROW_GAP / 2.; + let x = node_row + .last() + .map(|f| f.location.x + f.size.width) + .unwrap_or(0.); { let min = ( scrolled_pos.0.max(DEFAULT_MARGIN + centering), scrolled_pos.1 + y, ); let max = ( - (scrolled_pos.0 + x), + scrolled_pos.0 + x, scrolled_pos.1 + y + 1. * self.hidpi_scale * self.zoom, ); self.draw_rectangle( Rect::from_min_max(min, max), - native_color(self.theme.code_block_color, &self.surface_format), + native_color(self.theme.text_color, &self.surface_format), )?; } - y += TABLE_ROW_GAP / 2.; } } Element::Image(_) => {} diff --git a/src/table.rs b/src/table.rs index 54a08ae4..58554591 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,8 +1,20 @@ use crate::{ - text::{Text, TextBox, TextSystem}, + text::{Text, TextBox, TextBoxMeasure, TextCache, TextSystem}, utils::{Point, Rect, Size}, }; +use std::{ + default::default, + sync::{Arc, Mutex}, +}; + +use glyphon::FontSystem; +use taffy::{ + prelude::{auto, length, Display, Layout, Size as TaffySize, Style, Taffy}, + style::AvailableSpace, + tree::MeasureFunc, +}; + pub const TABLE_ROW_GAP: f32 = 20.; pub const TABLE_COL_GAP: f32 = 20.; @@ -125,6 +137,97 @@ impl Table { heights } + pub fn layout( + &self, + _text_system: &mut TextSystem, + bounds: Size, + zoom: f32, + ) -> anyhow::Result>> { + let mut taffy = Taffy::new(); + let max_columns = self.rows.iter().fold(self.headers.len(), |max, row| { + if row.len() > max { + row.len() + } else { + max + } + }); + + // Setup the grid + let root_style = Style { + display: Display::Grid, + size: TaffySize { + width: auto(), + height: auto(), + }, + gap: TaffySize { + width: length(TABLE_COL_GAP), + height: length(TABLE_ROW_GAP), + }, + grid_template_columns: vec![auto(); max_columns], + ..default() + }; + + let font_system = Arc::new(Mutex::new(FontSystem::new())); + let text_cache = Arc::new(Mutex::new(TextCache::new())); + let mut nodes = Vec::new(); + let mut node_row = Vec::new(); + // Define the child nodes + for (_, header) in self.headers.iter().enumerate() { + node_row.push(taffy.new_leaf_with_measure( + Style { ..default() }, + MeasureFunc::Boxed(Box::new(TextBoxMeasure { + font_system: font_system.clone(), + text_cache: text_cache.clone(), + textbox: Arc::new(header.clone()), + zoom, + })), + )?); + } + nodes.push(node_row.clone()); + node_row.clear(); + + for (_, row) in self.rows.iter().enumerate() { + for (_, item) in row.iter().enumerate() { + let item = item.clone(); + node_row.push(taffy.new_leaf_with_measure( + Style { ..default() }, + MeasureFunc::Boxed(Box::new(TextBoxMeasure { + font_system: font_system.clone(), + text_cache: text_cache.clone(), + textbox: Arc::new(item.clone()), + zoom, + })), + )?); + } + nodes.push(node_row.clone()); + node_row.clear(); + } + + let mut flattened_nodes = Vec::new(); + for row in &nodes { + flattened_nodes.append(&mut row.clone()); + } + + let root = taffy.new_with_children(root_style, &flattened_nodes)?; + + taffy + .compute_layout( + root, + TaffySize:: { + width: AvailableSpace::Definite(bounds.0), + height: AvailableSpace::Definite(bounds.1), + }, + ) + .unwrap(); + let mut layouts = Vec::new(); + + for row in nodes { + layouts.push(row.iter().map(|n| *taffy.layout(*n).unwrap()).collect()) + } + + Ok(layouts) + } + pub fn push_header(&mut self, header: TextBox) { self.headers.push(header); } diff --git a/src/text.rs b/src/text.rs index 8cb88ac8..51d47640 100644 --- a/src/text.rs +++ b/src/text.rs @@ -2,6 +2,7 @@ use std::{ borrow::BorrowMut, collections::hash_map, hash::{BuildHasher, Hash, Hasher}, + sync::{Arc, Mutex}, }; use fxhash::{FxHashMap, FxHashSet}; @@ -9,12 +10,100 @@ use glyphon::{ cosmic_text::Align as TextAlign, Affinity, Attrs, AttrsList, BufferLine, Color, Cursor, FamilyOwned, FontSystem, Style, SwashCache, TextArea, TextBounds, Weight, }; +use taffy::prelude::Size as TaffySize; +use taffy::{style::AvailableSpace, tree::Measurable}; use crate::utils::{Align, Line, Point, Rect, Selection, Size}; type KeyHash = u64; type HashBuilder = twox_hash::RandomXxHashBuilder64; +pub struct TextBoxMeasure { + pub textbox: Arc, + pub text_cache: Arc>, + pub font_system: Arc>, + pub zoom: f32, +} + +impl Measurable for TextBoxMeasure { + fn measure( + &self, + known_dimensions: TaffySize>, + available_space: TaffySize, + ) -> TaffySize { + let size = move |bounds: (f32, f32)| { + if self.textbox.texts.is_empty() { + return ( + 0., + self.textbox.padding_height * self.textbox.hidpi_scale * self.zoom, + ); + } + + let mut cache = self.text_cache.lock().unwrap(); + + let line_height = self.textbox.line_height(self.zoom); + + let (_, paragraph) = cache.borrow_mut().allocate( + self.font_system.lock().unwrap().borrow_mut(), + self.textbox.key(bounds, self.zoom), + ); + + let (total_lines, max_width) = paragraph + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); + + ( + max_width, + total_lines as f32 * line_height + + self.textbox.padding_height * self.textbox.hidpi_scale * self.zoom, + ) + }; + let mut bounds = (0., f32::MAX); + match available_space.width { + AvailableSpace::Definite(space) => { + bounds.0 = space; + } + AvailableSpace::MinContent => { + bounds.0 = 0.; + } + AvailableSpace::MaxContent => { + bounds.0 = f32::MAX; + } + } + match known_dimensions { + TaffySize { + width: None, + height: Some(height), + } => { + let size = size((bounds.0, f32::MAX)); + return TaffySize:: { + width: size.0.min(bounds.0), + height, + }; + } + TaffySize { + width: Some(width), + height: None, + } => { + let size = size((bounds.0, f32::MAX)); + return TaffySize:: { + width, + height: size.1, + }; + } + _ => {} + } + let size = size(bounds); + TaffySize:: { + width: size.0.min(bounds.0), + height: size.1, + } + } +} + #[derive(Clone, Debug, Default)] pub struct TextBox { pub indent: f32, diff --git a/temp.md b/temp.md new file mode 100644 index 00000000..6802060a --- /dev/null +++ b/temp.md @@ -0,0 +1,4 @@ +| Syntax | Description | +| ----------- | ----------- | +| Header | Title | +| Paragraph | Text | \ No newline at end of file From bfbd349d2cc1cc90f50ef6ab240a4ed27a6742aa Mon Sep 17 00:00:00 2001 From: Lucas Timmins Date: Thu, 25 May 2023 02:20:20 +0800 Subject: [PATCH 02/12] Remove local repo --- Cargo.lock | 4 ---- Cargo.toml | 5 +---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 676a0b96..9fedc1bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4841,7 +4841,3 @@ dependencies = [ "quote", "syn 1.0.109", ] - -[[patch.unused]] -name = "cosmic-text" -version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index b4707244..1c02578d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,4 @@ lto = true [dev-dependencies] insta = "1.29.0" -pretty_assertions = "1.3.0" - -[patch.crates-io] -cosmic-text = { path = "../cosmic-text" } +pretty_assertions = "1.3.0" \ No newline at end of file From d6e4f1200b6496ec53364c671ac14e576a637e3e Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Wed, 24 May 2023 20:39:43 +0100 Subject: [PATCH 03/12] Refactor text measure function --- src/text.rs | 111 ++++++++++++++++++++-------------------------------- 1 file changed, 43 insertions(+), 68 deletions(-) diff --git a/src/text.rs b/src/text.rs index 51d47640..5106614c 100644 --- a/src/text.rs +++ b/src/text.rs @@ -25,81 +25,56 @@ pub struct TextBoxMeasure { pub zoom: f32, } +impl TextBoxMeasure { + fn internal_measure(&self, bounds: (f32, f32)) -> (f32, f32) { + if self.textbox.texts.is_empty() { + return ( + 0., + self.textbox.padding_height * self.textbox.hidpi_scale * self.zoom, + ); + } + + let mut cache = self.text_cache.lock().unwrap(); + + let line_height = self.textbox.line_height(self.zoom); + + let (_, paragraph) = cache.borrow_mut().allocate( + self.font_system.lock().unwrap().borrow_mut(), + self.textbox.key(bounds, self.zoom), + ); + + let (total_lines, max_width) = paragraph + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); + + ( + max_width, + total_lines as f32 * line_height + + self.textbox.padding_height * self.textbox.hidpi_scale * self.zoom, + ) + } +} + impl Measurable for TextBoxMeasure { fn measure( &self, known_dimensions: TaffySize>, available_space: TaffySize, ) -> TaffySize { - let size = move |bounds: (f32, f32)| { - if self.textbox.texts.is_empty() { - return ( - 0., - self.textbox.padding_height * self.textbox.hidpi_scale * self.zoom, - ); - } - - let mut cache = self.text_cache.lock().unwrap(); - - let line_height = self.textbox.line_height(self.zoom); - - let (_, paragraph) = cache.borrow_mut().allocate( - self.font_system.lock().unwrap().borrow_mut(), - self.textbox.key(bounds, self.zoom), - ); - - let (total_lines, max_width) = paragraph - .layout_runs() - .enumerate() - .fold((0, 0.0), |(_, max), (i, buffer)| { - (i + 1, buffer.line_w.max(max)) - }); - - ( - max_width, - total_lines as f32 * line_height - + self.textbox.padding_height * self.textbox.hidpi_scale * self.zoom, - ) + let available_width = match available_space.width { + AvailableSpace::Definite(space) => space, + AvailableSpace::MinContent => 0.0, + AvailableSpace::MaxContent => f32::MAX, }; - let mut bounds = (0., f32::MAX); - match available_space.width { - AvailableSpace::Definite(space) => { - bounds.0 = space; - } - AvailableSpace::MinContent => { - bounds.0 = 0.; - } - AvailableSpace::MaxContent => { - bounds.0 = f32::MAX; - } - } - match known_dimensions { - TaffySize { - width: None, - height: Some(height), - } => { - let size = size((bounds.0, f32::MAX)); - return TaffySize:: { - width: size.0.min(bounds.0), - height, - }; - } - TaffySize { - width: Some(width), - height: None, - } => { - let size = size((bounds.0, f32::MAX)); - return TaffySize:: { - width, - height: size.1, - }; - } - _ => {} - } - let size = size(bounds); - TaffySize:: { - width: size.0.min(bounds.0), - height: size.1, + let width_bound = known_dimensions.width.unwrap_or(available_width); + + let size = self.internal_measure((width_bound, f32::MAX)); + TaffySize { + width: known_dimensions.width.unwrap_or(size.0), + height: known_dimensions.height.unwrap_or(size.1), } } } From d3bf1a54175ef7e1ab7a39f55900b2be3d385c5d Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Wed, 24 May 2023 22:36:12 +0100 Subject: [PATCH 04/12] Make two example tables (wrapping and non-wrapping) --- temp.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/temp.md b/temp.md index 6802060a..bf019876 100644 --- a/temp.md +++ b/temp.md @@ -1,4 +1,14 @@ +## Small Table + | Syntax | Description | | ----------- | ----------- | | Header | Title | +| Paragraph | Text | + + +## Wide Table + +| Syntax | Description | +| ----------- | ----------- | +| Header Header Header Header Header Header Header Header Header Header Header Header Header Header Header Header Header Header Header Header Header | Title Title Title Title Title Title Title Title Title Title Title Title Title Title Title Title Title Title Title | | Paragraph | Text | \ No newline at end of file From c154dca1d42dc22b7c41074d59d31324d9df57be Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Wed, 24 May 2023 23:08:45 +0100 Subject: [PATCH 05/12] Replace unstable default feature with default util function --- src/main.rs | 1 - src/table.rs | 7 ++----- src/utils.rs | 4 ++++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index d540a805..8117f41b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![feature(default_free_fn)] pub mod color; pub mod fonts; pub mod image; diff --git a/src/table.rs b/src/table.rs index 58554591..2a8abbd4 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,12 +1,9 @@ use crate::{ text::{Text, TextBox, TextBoxMeasure, TextCache, TextSystem}, - utils::{Point, Rect, Size}, + utils::{default, Point, Rect, Size}, }; -use std::{ - default::default, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use glyphon::FontSystem; use taffy::{ diff --git a/src/utils.rs b/src/utils.rs index 04d9ac82..68a560ec 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -10,6 +10,10 @@ use winit::window::CursorIcon; use crate::{color::SyntaxTheme, image::ImageData}; +pub(crate) fn default() -> T { + Default::default() +} + pub fn usize_in_mib(num: usize) -> f32 { num as f32 / 1_024.0 / 1_024.0 } From c25d4068a5257f0c4cf73d2a195e924e5b27a390 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Thu, 25 May 2023 00:46:27 +0100 Subject: [PATCH 06/12] Don't constrain height --- src/table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/table.rs b/src/table.rs index 2a8abbd4..b137c031 100644 --- a/src/table.rs +++ b/src/table.rs @@ -212,7 +212,7 @@ impl Table { root, TaffySize:: { width: AvailableSpace::Definite(bounds.0), - height: AvailableSpace::Definite(bounds.1), + height: AvailableSpace::MaxContent, }, ) .unwrap(); From 07e0df0a269178a0f553bae131dae57a3546ca57 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Thu, 25 May 2023 00:46:34 +0100 Subject: [PATCH 07/12] Use fixed Taffy --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 676a0b96..4866381b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3547,7 +3547,7 @@ dependencies = [ [[package]] name = "taffy" version = "0.3.11" -source = "git+https://github.com/DioxusLabs/taffy#5443996198f02cf5a6c6493499df3cf08394a70f" +source = "git+https://github.com/nicoburns/taffy?rev=044780216f2400687bd86c0060d29b0c1105c494#044780216f2400687bd86c0060d29b0c1105c494" dependencies = [ "arrayvec", "grid", diff --git a/Cargo.toml b/Cargo.toml index b4707244..01a25a48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ indexmap = { version = "1.9.3", features = ["serde"] } html-escape = "0.2.13" fxhash = "0.2.1" twox-hash = "1.6.3" -taffy = { git="https://github.com/DioxusLabs/taffy" } +taffy = { git = "https://github.com/nicoburns/taffy", rev = "044780216f2400687bd86c0060d29b0c1105c494" } [dependencies.glyphon] version = "0.2" From f3975b76a880652cb40cf6399248ebe6446e4b2a Mon Sep 17 00:00:00 2001 From: Lucas Timmins Date: Sat, 27 May 2023 05:43:54 +0800 Subject: [PATCH 08/12] Touch up and simplify logic --- Cargo.lock | 56 ++++++------- src/positioner.rs | 14 +--- src/renderer.rs | 66 ++++++++-------- src/table.rs | 195 +++++++++++++++++++--------------------------- src/text.rs | 93 +++++++++++----------- temp.md | 14 ---- 6 files changed, 191 insertions(+), 247 deletions(-) delete mode 100644 temp.md diff --git a/Cargo.lock b/Cargo.lock index f5f16dc2..ddade461 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,7 +246,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -263,7 +263,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1e31e207a6b8fb791a38ea3105e6cb541f55e4d029902d3039a4ad07cc4105" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bincode" @@ -392,7 +392,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -484,7 +484,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -931,7 +931,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -1215,7 +1215,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -2460,7 +2460,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -2677,7 +2677,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -2807,9 +2807,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -2840,9 +2840,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -2954,9 +2954,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ "aho-corasick", "memchr", @@ -3217,7 +3217,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -3239,7 +3239,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -3501,9 +3501,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -3605,7 +3605,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -3812,7 +3812,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -4126,7 +4126,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", "wasm-bindgen-shared", ] @@ -4160,7 +4160,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4275,9 +4275,9 @@ checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "wgpu" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13edd72c7b08615b7179dd7e778ee3f0bdc870ef2de9019844ff2cceeee80b11" +checksum = "3059ea4ddec41ca14f356833e2af65e7e38c0a8f91273867ed526fb9bafcca95" dependencies = [ "arrayvec", "cfg-if", @@ -4299,9 +4299,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625bea30a0ba50d88025f95c80211d1a85c86901423647fb74f397f614abbd9a" +checksum = "8f478237b4bf0d5b70a39898a66fa67ca3a007d79f2520485b8b0c3dfc46f8c2" dependencies = [ "arrayvec", "bit-vec", diff --git a/src/positioner.rs b/src/positioner.rs index f863effa..2e052bd4 100644 --- a/src/positioner.rs +++ b/src/positioner.rs @@ -114,17 +114,9 @@ impl Positioner { ), zoom, )?; - let min = layout.first().unwrap().first().unwrap(); - let max = layout.last().unwrap().last().unwrap(); - Rect::from_min_max( - ( - DEFAULT_MARGIN + centering + min.location.x, - self.reserved_height + min.location.y, - ), - ( - DEFAULT_MARGIN + centering + max.location.x + max.size.width, - self.reserved_height + max.location.y + max.size.height, - ), + Rect::new( + (DEFAULT_MARGIN + centering, self.reserved_height), + layout.size, ) } Element::Row(row) => { diff --git a/src/renderer.rs b/src/renderer.rs index fcbbbcd6..aad564fe 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -15,7 +15,7 @@ use lyon::geom::Box2D; use lyon::path::Polygon; use lyon::tessellation::*; use std::borrow::Cow; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use wgpu::util::DeviceExt; use wgpu::{BindGroup, Buffer, IndexFormat, MultisampleState, TextureFormat}; use winit::window::Window; @@ -150,12 +150,12 @@ impl Renderer { surface.configure(&device, &config); let image_renderer = ImageRenderer::new(&device, &surface_format); - let font_system = get_fonts(&font_opts); + let font_system = Arc::new(Mutex::new(get_fonts(&font_opts))); let swash_cache = SwashCache::new(); let mut text_atlas = TextAtlas::new(&device, &queue, surface_format); let text_renderer = TextRenderer::new(&mut text_atlas, &device, MultisampleState::default(), None); - let text_cache = TextCache::new(); + let text_cache = Arc::new(Mutex::new(TextCache::new())); let text_system = TextSystem { font_system, swash_cache, @@ -364,7 +364,7 @@ impl Renderer { ); let layout = table.layout(&mut self.text_system, bounds, self.zoom)?; - for (col, node) in layout[0].iter().enumerate() { + for (col, node) in layout.headers.iter().enumerate() { if let Some(text_box) = table.headers.get(col) { text_areas.push(text_box.text_areas( &mut self.text_system, @@ -395,13 +395,13 @@ impl Renderer { } } } - let last_header_node = layout[0].last().unwrap(); + let last_header_node = layout.headers.last().unwrap(); let y = last_header_node.location.y + last_header_node.size.height + TABLE_ROW_GAP / 2.; let x = layout - .first() - .and_then(|f| f.last()) + .headers + .last() .map(|f| f.location.x + f.size.width) .unwrap_or(0.); { @@ -419,7 +419,7 @@ impl Renderer { )?; } - for (row, node_row) in layout.iter().skip(1).enumerate() { + for (row, node_row) in layout.rows.iter().enumerate() { for (col, node) in node_row.iter().enumerate() { if let Some(row) = table.rows.get(row) { if let Some(text_box) = row.get(col) { @@ -745,34 +745,36 @@ impl Renderer { // Prepare image bind groups for drawing let image_bindgroups = self.image_bindgroups(elements); - let text_areas: Vec