Skip to content

Commit

Permalink
Merge pull request #583 from Sharktheone/layout/text
Browse files Browse the repository at this point in the history
Layout/text
  • Loading branch information
Sharktheone authored Sep 6, 2024
2 parents 7c4dc50 + 14fd64c commit ab4fd1c
Show file tree
Hide file tree
Showing 24 changed files with 1,497 additions and 688 deletions.
382 changes: 375 additions & 7 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/gosub_render_backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ image = "0.25.2"
raw-window-handle = "0.6.2"
gosub_shared = { path = "../gosub_shared" }
gosub_html5 = { path = "../gosub_html5" }
gosub_typeface = { path = "../gosub_typeface" }

33 changes: 29 additions & 4 deletions crates/gosub_render_backend/src/layout.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use gosub_shared::types::Result;
use gosub_typeface::font::{Font, Glyph};
use std::fmt::Debug;

use crate::geo::{Point, Rect, Size, SizeU32};

pub trait LayoutTree<L: Layouter>: Sized {
type NodeId: Copy + Clone + From<u64> + Into<u64>;
type Node: Node;
type Node: Node + HasTextLayout<L>;

fn children(&self, id: Self::NodeId) -> Option<Vec<Self::NodeId>>;
fn contains(&self, id: &Self::NodeId) -> bool;
Expand All @@ -28,6 +30,8 @@ pub trait Layouter: Sized + Clone {
type Cache: Default;
type Layout: Layout;

type TextLayout: TextLayout;

const COLLAPSE_INLINE: bool;

fn layout<LT: LayoutTree<Self>>(
Expand Down Expand Up @@ -109,15 +113,21 @@ pub trait Layout: Default {
pub trait Node {
type Property: CssProperty;

fn get_property(&mut self, name: &str) -> Option<&mut Self::Property>; //TODO: this needs to be more generic...
fn text_size(&mut self) -> Option<Size>;
fn get_property(&mut self, name: &str) -> Option<&mut Self::Property>;
fn text_data(&self) -> Option<&str>;

fn text_size(&self) -> Option<Size>;

/// This can only return true if the `Layout::COLLAPSE_INLINE` is set true for the layouter
///
fn is_anon_inline_parent(&self) -> bool;
}

pub trait CssProperty {
pub trait HasTextLayout<L: Layouter> {
fn set_text_layout(&mut self, layout: L::TextLayout);
}

pub trait CssProperty: Debug {
fn compute_value(&mut self);
fn unit_to_px(&self) -> f32;

Expand All @@ -128,3 +138,18 @@ pub trait CssProperty {
fn as_number(&self) -> Option<f32>;
fn is_none(&self) -> bool;
}

pub trait TextLayout {
type Font: Font;
fn dbg_layout(&self) -> String;

fn size(&self) -> Size;

fn glyphs(&self) -> &[Glyph];

fn font(&self) -> &Self::Font;

fn font_size(&self) -> f32;

fn coords(&self) -> &[i16];
}
22 changes: 7 additions & 15 deletions crates/gosub_render_backend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt::{Debug, Display, Write};
use std::ops::{Div, Mul, MulAssign};

use crate::layout::TextLayout;
use crate::svg::SvgRenderer;
pub use geo::*;
use gosub_shared::types::Result;
Expand All @@ -21,8 +22,7 @@ pub trait RenderBackend: Sized + Debug {
type BorderSide: BorderSide<Self>;
type BorderRadius: BorderRadius;
type Transform: Transform;
type PreRenderText: PreRenderText;
type Text: Text<Self>;
type Text: Text;
type Gradient: Gradient<Self>;
type Color: Color;
type Image: Image;
Expand Down Expand Up @@ -488,20 +488,12 @@ pub trait Transform: Sized + Mul<Self> + MulAssign + Clone {
}
}

pub trait PreRenderText {
fn new(text: String, font: Option<Vec<String>>, size: FP) -> Self;
pub trait Text {
type Font;

fn with_lh(text: String, font: Option<Vec<String>>, size: FP, line_height: FP) -> Self;

fn prerender(&mut self) -> Size;
fn value(&self) -> &str;
fn fs(&self) -> FP;

//TODO: Who should be responsible for line breaking if the text is too long?
}

pub trait Text<B: RenderBackend> {
fn new(pre: &mut B::PreRenderText) -> Self;
fn new<TL: TextLayout>(node: &TL) -> Self
where
TL::Font: Into<Self::Font>;
}

pub struct ColorStop<B: RenderBackend> {
Expand Down
6 changes: 3 additions & 3 deletions crates/gosub_render_utils/src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct PositionTree {
}

impl PositionTree {
pub fn from_tree<B: RenderBackend, L: Layouter>(from_tree: &RenderTree<B, L>) -> Self {
pub fn from_tree<B: RenderBackend, L: Layouter>(from_tree: &RenderTree<L>) -> Self {
let mut tree = RTree::new();

//TODO: we somehow need to get the border radius and a potential stacking context of the element here
Expand All @@ -43,8 +43,8 @@ impl PositionTree {
Self { tree }
}

fn add_node_to_tree<B: RenderBackend, L: Layouter>(
from_tree: &RenderTree<B, L>,
fn add_node_to_tree<L: Layouter>(
from_tree: &RenderTree<L>,
id: NodeId,
z_index: i32,
tree: &mut RTree<Element>,
Expand Down
1 change: 1 addition & 0 deletions crates/gosub_renderer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ gosub_render_backend = { path = "../gosub_render_backend" }
anyhow = "1.0.86"
image = "0.25.2"
url = "2.5.2"
log = "0.4.22"



Expand Down
108 changes: 73 additions & 35 deletions crates/gosub_renderer/src/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ use std::sync::mpsc::Sender;
use anyhow::anyhow;
use url::Url;

use crate::draw::img::request_img;
use crate::render_tree::{load_html_rendertree, TreeDrawer};
use gosub_css3::colors::RgbColor;
use gosub_css3::stylesheet::CssValue;
use gosub_html5::node::NodeId;
use gosub_net::http::fetcher::Fetcher;
use gosub_render_backend::geo::{Size, SizeU32, FP};
use gosub_render_backend::layout::{Layout, LayoutTree, Layouter};
use gosub_render_backend::layout::{Layout, LayoutTree, Layouter, TextLayout};
use gosub_render_backend::svg::SvgRenderer;
use gosub_render_backend::{
Border, BorderSide, BorderStyle, Brush, Color, ImageBuffer, NodeDesc, Rect, RenderBackend,
Expand All @@ -17,9 +19,7 @@ use gosub_render_backend::{
use gosub_rendering::position::PositionTree;
use gosub_shared::types::Result;
use gosub_styling::render_tree::{RenderNodeData, RenderTree, RenderTreeNode};

use crate::draw::img::request_img;
use crate::render_tree::{load_html_rendertree, TreeDrawer};
use log::warn;

mod img;

Expand Down Expand Up @@ -48,7 +48,11 @@ const DEBUG_BORDER_COLOR: (u8, u8, u8) = (255, 72, 72); //rgb(255, 72, 72)

type Point = gosub_shared::types::Point<FP>;

impl<B: RenderBackend, L: Layouter> SceneDrawer<B, L, RenderTree<B, L>> for TreeDrawer<B, L> {
impl<B: RenderBackend, L: Layouter> SceneDrawer<B, L, RenderTree<L>> for TreeDrawer<B, L>
where
<<B as RenderBackend>::Text as Text>::Font:
From<<<L as Layouter>::TextLayout as TextLayout>::Font>,
{
fn draw(&mut self, backend: &mut B, data: &mut B::WindowData<'_>, size: SizeU32) {
if !self.dirty && self.size == Some(size) {
return;
Expand Down Expand Up @@ -206,7 +210,11 @@ struct Drawer<'s, 't, B: RenderBackend, L: Layouter> {
svg: B::SVGRenderer,
}

impl<B: RenderBackend, L: Layouter> Drawer<'_, '_, B, L> {
impl<B: RenderBackend, L: Layouter> Drawer<'_, '_, B, L>
where
<<B as RenderBackend>::Text as Text>::Font:
From<<<L as Layouter>::TextLayout as TextLayout>::Font>,
{
pub(crate) fn render(&mut self, size: SizeU32) {
let root = self.drawer.tree.root;
if let Err(e) = self
Expand All @@ -220,7 +228,7 @@ impl<B: RenderBackend, L: Layouter> Drawer<'_, '_, B, L> {

// print_tree(&self.taffy, self.root, &self.style);

self.drawer.position = PositionTree::from_tree(&self.drawer.tree);
self.drawer.position = PositionTree::from_tree::<B, L>(&self.drawer.tree);

self.render_node_with_children(self.drawer.tree.root, Point::ZERO);
}
Expand All @@ -242,6 +250,33 @@ impl<B: RenderBackend, L: Layouter> Drawer<'_, '_, B, L> {
}

fn render_node(&mut self, id: NodeId, pos: &mut Point) -> anyhow::Result<()> {
let parent = self.drawer.tree.parent_id(id).unwrap_or(id);

let parent_color = self
.drawer
.tree
.get_node_mut(parent)
.ok_or(anyhow!("Node {id} not found"))?
.properties
.get("color")
.and_then(|prop| {
prop.compute_value();

match &prop.actual {
CssValue::Color(color) => Some(*color),
CssValue::String(color) => Some(RgbColor::from(color.as_str())),
_ => None,
}
})
.unwrap_or(RgbColor::default()); //HACK: this needs to be removed

let parent_color = B::Color::rgba(
parent_color.r as u8,
parent_color.g as u8,
parent_color.b as u8,
parent_color.a as u8,
);

let node = self
.drawer
.tree
Expand All @@ -252,7 +287,8 @@ impl<B: RenderBackend, L: Layouter> Drawer<'_, '_, B, L> {
pos.x += p.x as FP;
pos.y += p.y as FP;

let border_radius = render_bg(node, self.scene, pos, &mut self.svg, &self.drawer.fetcher);
let border_radius =
render_bg::<B, L>(node, self.scene, pos, &mut self.svg, &self.drawer.fetcher);

if let RenderNodeData::Element(element) = &node.data {
if element.name() == "img" {
Expand All @@ -277,33 +313,35 @@ impl<B: RenderBackend, L: Layouter> Drawer<'_, '_, B, L> {
}
}

render_text(node, self.scene, pos);
render_text::<B, L>(node, parent_color, self.scene, pos);
Ok(())
}
}

fn render_text<B: RenderBackend, L: Layouter>(
node: &mut RenderTreeNode<B, L>,
node: &RenderTreeNode<L>,
color: B::Color,
scene: &mut B::Scene,
pos: &Point,
) {
if let RenderNodeData::Text(text) = &mut node.data {
let color = node
.properties
.get("color")
.and_then(|prop| {
prop.compute_value();

match &prop.actual {
CssValue::Color(color) => Some(*color),
CssValue::String(color) => Some(RgbColor::from(color.as_str())),
_ => None,
}
})
.map(|color| Color::rgba(color.r as u8, color.g as u8, color.b as u8, color.a as u8))
.unwrap_or(Color::WHITE);
) where
<<B as RenderBackend>::Text as Text>::Font:
From<<<L as Layouter>::TextLayout as TextLayout>::Font>,
{
// if u64::from(node.id) < 204 && u64::from(node.id) > 202 {
// return;
// }

// if u64::from(node.id) == 203 {
// return;
// }

if let RenderNodeData::Text(ref text) = node.data {
let Some(layout) = text.layout.as_ref() else {
warn!("No layout for text node");
return;
};

let text = Text::new(&mut text.prerender);
let text: B::Text = Text::new::<L::TextLayout>(layout);

let size = node.layout.size();

Expand All @@ -327,7 +365,7 @@ fn render_text<B: RenderBackend, L: Layouter>(
}

fn render_bg<B: RenderBackend, L: Layouter>(
node: &mut RenderTreeNode<B, L>,
node: &mut RenderTreeNode<L>,
scene: &mut B::Scene,
pos: &Point,
svg: &mut B::SVGRenderer,
Expand Down Expand Up @@ -390,7 +428,7 @@ fn render_bg<B: RenderBackend, L: Layouter>(
border_radius_left as FP,
);

let border = get_border(node).map(|border| RenderBorder::new(border));
let border = get_border::<B, L>(node).map(|border| RenderBorder::new(border));

if let Some(bg_color) = bg_color {
let size = node.layout.size();
Expand Down Expand Up @@ -624,11 +662,11 @@ pub fn print_tree<B: RenderBackend, L: Layouter>(
}
*/

fn get_border<B: RenderBackend, L: Layouter>(node: &mut RenderTreeNode<B, L>) -> Option<B::Border> {
let left = get_border_side(node, Side::Left);
let right = get_border_side(node, Side::Right);
let top = get_border_side(node, Side::Top);
let bottom = get_border_side(node, Side::Bottom);
fn get_border<B: RenderBackend, L: Layouter>(node: &mut RenderTreeNode<L>) -> Option<B::Border> {
let left = get_border_side::<B, L>(node, Side::Left);
let right = get_border_side::<B, L>(node, Side::Right);
let top = get_border_side::<B, L>(node, Side::Top);
let bottom = get_border_side::<B, L>(node, Side::Bottom);

if left.is_none() && right.is_none() && top.is_none() && bottom.is_none() {
return None;
Expand Down Expand Up @@ -656,7 +694,7 @@ fn get_border<B: RenderBackend, L: Layouter>(node: &mut RenderTreeNode<B, L>) ->
}

fn get_border_side<B: RenderBackend, L: Layouter>(
node: &mut RenderTreeNode<B, L>,
node: &mut RenderTreeNode<L>,
side: Side,
) -> Option<B::BorderSide> {
let width = node
Expand Down
12 changes: 6 additions & 6 deletions crates/gosub_renderer/src/render_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use gosub_styling::styling::CssProperties;

pub struct TreeDrawer<B: RenderBackend, L: Layouter> {
pub(crate) fetcher: Fetcher,
pub(crate) tree: RenderTree<B, L>,
pub(crate) tree: RenderTree<L>,
pub(crate) layouter: L,
pub(crate) size: Option<SizeU32>,
pub(crate) position: PositionTree,
Expand All @@ -32,7 +32,7 @@ pub struct TreeDrawer<B: RenderBackend, L: Layouter> {
}

impl<B: RenderBackend, L: Layouter> TreeDrawer<B, L> {
pub fn new(tree: RenderTree<B, L>, layouter: L, url: Url, debug: bool) -> Self {
pub fn new(tree: RenderTree<L>, layouter: L, url: Url, debug: bool) -> Self {
Self {
tree,
layouter,
Expand All @@ -50,19 +50,19 @@ impl<B: RenderBackend, L: Layouter> TreeDrawer<B, L> {
}
}

pub struct RenderTreeNode<B: RenderBackend> {
pub struct RenderTreeNode<L: Layouter> {
pub parent: Option<NodeId>,
pub children: Vec<NodeId>,
pub layout: i32, //TODO
pub name: String,
pub properties: CssProperties,
pub namespace: Option<String>,
pub data: RenderNodeData<B>,
pub data: RenderNodeData<L>,
}

pub(crate) fn load_html_rendertree<B: RenderBackend, L: Layouter>(
pub(crate) fn load_html_rendertree<L: Layouter>(
url: Url,
) -> gosub_shared::types::Result<RenderTree<B, L>> {
) -> gosub_shared::types::Result<RenderTree<L>> {
let html = if url.scheme() == "http" || url.scheme() == "https" {
// Fetch the html from the url
let response = ureq::get(url.as_ref()).call()?;
Expand Down
1 change: 1 addition & 0 deletions crates/gosub_shared/src/timing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum Scale {
}

#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[allow(dead_code)]
struct Duration {
duration: u64,
suffix: String,
Expand Down
Loading

0 comments on commit ab4fd1c

Please sign in to comment.