From 66523af9afbec5e57b20e5decb349d5f0954d477 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sat, 12 Oct 2024 13:58:34 +0200 Subject: [PATCH] feat: `text_height` attribute to control the height behavior of text (#976) * feat: `text_height` attribute to control the height behavior of text * update mocked apis * clean up --- crates/components/src/button.rs | 1 + crates/components/src/progress_bar.rs | 1 + crates/core/src/render/utils/label.rs | 4 ++- crates/core/src/render/utils/paragraph.rs | 12 +++++-- .../src/_docs/attributes/text_height.md | 22 +++++++++++++ crates/elements/src/definitions.rs | 6 ++++ crates/engine/src/mocked.rs | 14 +++++++-- crates/native-core/src/attributes.rs | 2 ++ crates/state/src/font_style.rs | 24 +++++++++++++- crates/state/src/values/mod.rs | 2 ++ crates/state/src/values/text_height.rs | 31 +++++++++++++++++++ 11 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 crates/elements/src/_docs/attributes/text_height.md create mode 100644 crates/state/src/values/text_height.rs diff --git a/crates/components/src/button.rs b/crates/components/src/button.rs index 9f4e97a23..9ab309b82 100644 --- a/crates/components/src/button.rs +++ b/crates/components/src/button.rs @@ -274,6 +274,7 @@ pub fn ButtonBase( corner_radius: "{corner_radius}", background: "{background}", text_align: "center", + text_height: "disable-least-ascent", main_align: "center", cross_align: "center", {&children} diff --git a/crates/components/src/progress_bar.rs b/crates/components/src/progress_bar.rs index df797f144..c26adc492 100644 --- a/crates/components/src/progress_bar.rs +++ b/crates/components/src/progress_bar.rs @@ -76,6 +76,7 @@ pub fn ProgressBar( width: "100%", color: "{color}", max_lines: "1", + text_height: "disable-least-ascent", "{progress.floor()}%" } } diff --git a/crates/core/src/render/utils/label.rs b/crates/core/src/render/utils/label.rs index e1d1619b2..1ed471caa 100644 --- a/crates/core/src/render/utils/label.rs +++ b/crates/core/src/render/utils/label.rs @@ -21,12 +21,14 @@ pub fn create_label( paragraph_style.set_text_align(font_style.text_align); paragraph_style.set_max_lines(font_style.max_lines); paragraph_style.set_replace_tab_characters(true); + paragraph_style.set_text_height_behavior(font_style.text_height); if let Some(ellipsis) = font_style.text_overflow.get_ellipsis() { paragraph_style.set_ellipsis(ellipsis); } - let text_style = font_style.text_style(default_font_family, scale_factor); + let text_style = + font_style.text_style(default_font_family, scale_factor, font_style.text_height); paragraph_style.set_text_style(&text_style); let mut paragraph_builder = ParagraphBuilder::new(¶graph_style, font_collection); diff --git a/crates/core/src/render/utils/paragraph.rs b/crates/core/src/render/utils/paragraph.rs index 6e8754a28..c4b84dabf 100644 --- a/crates/core/src/render/utils/paragraph.rs +++ b/crates/core/src/render/utils/paragraph.rs @@ -34,6 +34,7 @@ pub fn create_paragraph( paragraph_style.set_text_align(font_style.text_align); paragraph_style.set_max_lines(font_style.max_lines); paragraph_style.set_replace_tab_characters(true); + paragraph_style.set_text_height_behavior(font_style.text_height); if let Some(ellipsis) = font_style.text_overflow.get_ellipsis() { paragraph_style.set_ellipsis(ellipsis); @@ -41,7 +42,8 @@ pub fn create_paragraph( let mut paragraph_builder = ParagraphBuilder::new(¶graph_style, font_collection); - let text_style = font_style.text_style(default_font_family, scale_factor); + let text_style = + font_style.text_style(default_font_family, scale_factor, font_style.text_height); paragraph_builder.push_style(&text_style); for text_span in node.children() { @@ -52,8 +54,12 @@ pub fn create_paragraph( let text_nodes = text_span.children(); let text_node = *text_nodes.first().unwrap(); let text_node_type = &*text_node.node_type(); - let font_style = text_span.get::().unwrap(); - let text_style = font_style.text_style(default_font_family, scale_factor); + let text_font_style = text_span.get::().unwrap(); + let text_style = text_font_style.text_style( + default_font_family, + scale_factor, + font_style.text_height, + ); paragraph_builder.push_style(&text_style); if let NodeType::Text(text) = text_node_type { diff --git a/crates/elements/src/_docs/attributes/text_height.md b/crates/elements/src/_docs/attributes/text_height.md new file mode 100644 index 000000000..270d7450a --- /dev/null +++ b/crates/elements/src/_docs/attributes/text_height.md @@ -0,0 +1,22 @@ +Specify the text height behavior. + +Accepted values: + +- `disable-all` (default) +- `all` +- `disable-first-ascent` +- `disable-least-ascent` + +### Example + +```rust, no_run +# use freya::prelude::*; +fn app() -> Element { + rsx!( + label { + text_height: "disable-all", + "Hello, World!" + } + ) +} +``` \ No newline at end of file diff --git a/crates/elements/src/definitions.rs b/crates/elements/src/definitions.rs index a0a92abb7..fffe3f5b3 100644 --- a/crates/elements/src/definitions.rs +++ b/crates/elements/src/definitions.rs @@ -253,6 +253,8 @@ builder_constructors! { letter_spacing: String, #[doc = include_str!("_docs/attributes/word_spacing.md")] word_spacing: String, + #[doc = include_str!("_docs/attributes/text_height.md")] + text_height: String, // Transform #[doc = include_str!("_docs/attributes/rotate.md")] @@ -407,6 +409,8 @@ builder_constructors! { letter_spacing: String, #[doc = include_str!("_docs/attributes/word_spacing.md")] word_spacing: String, + #[doc = include_str!("_docs/attributes/text_height.md")] + text_height: String, // Transform #[doc = include_str!("_docs/attributes/rotate.md")] @@ -563,6 +567,8 @@ builder_constructors! { letter_spacing: String, #[doc = include_str!("_docs/attributes/word_spacing.md")] word_spacing: String, + #[doc = include_str!("_docs/attributes/text_height.md")] + text_height: String, // Transform #[doc = include_str!("_docs/attributes/rotate.md")] diff --git a/crates/engine/src/mocked.rs b/crates/engine/src/mocked.rs index cfeb00df0..3ba169335 100644 --- a/crates/engine/src/mocked.rs +++ b/crates/engine/src/mocked.rs @@ -613,6 +613,18 @@ impl TextStyle { pub fn set_placeholder(&mut self) -> &mut Self { unimplemented!("This is mocked") } + + pub fn set_height_behavior(&mut self, behavior: TextHeightBehavior) { + unimplemented!("This is mocked") + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum TextHeightBehavior { + All = 0, + DisableFirstAscent = 1, + DisableLastDescent = 2, + DisableAll = 3, } pub struct Typeface; @@ -988,8 +1000,6 @@ impl From<&FontCollection> for FontCollection { pub struct StrutStyle; -pub struct TextHeightBehavior; - #[repr(i32)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum TextDirection { diff --git a/crates/native-core/src/attributes.rs b/crates/native-core/src/attributes.rs index 1e7f2713a..59526e20c 100644 --- a/crates/native-core/src/attributes.rs +++ b/crates/native-core/src/attributes.rs @@ -33,6 +33,7 @@ pub enum AttributeName { DecorationColor, DecorationStyle, TextOverflow, + TextHeight, Rotate, Overflow, Margin, @@ -253,6 +254,7 @@ impl FromStr for AttributeName { "decoration_color" => Ok(AttributeName::DecorationColor), "decoration_style" => Ok(AttributeName::DecorationStyle), "text_overflow" => Ok(AttributeName::TextOverflow), + "text_height" => Ok(AttributeName::TextHeight), "rotate" => Ok(AttributeName::Rotate), "overflow" => Ok(AttributeName::Overflow), "margin" => Ok(AttributeName::Margin), diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 81005d484..18c033488 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -26,6 +26,7 @@ use crate::{ ExtSplit, Parse, ParseAttribute, + TextHeight, TextOverflow, }; @@ -45,10 +46,16 @@ pub struct FontStyleState { pub text_align: TextAlign, pub max_lines: Option, pub text_overflow: TextOverflow, + pub text_height: TextHeightBehavior, } impl FontStyleState { - pub fn text_style(&self, default_font_family: &[String], scale_factor: f32) -> TextStyle { + pub fn text_style( + &self, + default_font_family: &[String], + scale_factor: f32, + paragraph_text_height: TextHeightBehavior, + ) -> TextStyle { let mut text_style = TextStyle::new(); let mut font_family = self.font_family.clone(); @@ -66,6 +73,11 @@ impl FontStyleState { .set_word_spacing(self.word_spacing) .set_letter_spacing(self.letter_spacing); + if paragraph_text_height.needs_custom_height() { + text_style.set_height_override(true); + text_style.set_half_leading(true); + } + if let Some(line_height) = self.line_height { text_style.set_height_override(true).set_height(line_height); } @@ -102,6 +114,7 @@ impl Default for FontStyleState { text_align: TextAlign::default(), max_lines: None, text_overflow: TextOverflow::default(), + text_height: TextHeightBehavior::DisableAll, } } } @@ -234,6 +247,14 @@ impl ParseAttribute for FontStyleState { } } } + AttributeName::TextHeight => { + let value = attr.value.as_text(); + if let Some(value) = value { + if let Ok(text_height) = TextHeightBehavior::parse(value) { + self.text_height = text_height; + } + } + } _ => {} } @@ -267,6 +288,7 @@ impl State for FontStyleState { AttributeName::DecorationColor, AttributeName::DecorationStyle, AttributeName::TextOverflow, + AttributeName::TextHeight, ])); fn update<'a>( diff --git a/crates/state/src/values/mod.rs b/crates/state/src/values/mod.rs index 7cabda902..83d564b48 100644 --- a/crates/state/src/values/mod.rs +++ b/crates/state/src/values/mod.rs @@ -16,6 +16,7 @@ mod overflow; mod position; mod shadow; mod size; +mod text_height; mod text_shadow; pub use border::*; @@ -30,3 +31,4 @@ pub use highlight::*; pub use overflow::*; pub use shadow::*; pub use size::*; +pub use text_height::*; diff --git a/crates/state/src/values/text_height.rs b/crates/state/src/values/text_height.rs new file mode 100644 index 000000000..4977b383d --- /dev/null +++ b/crates/state/src/values/text_height.rs @@ -0,0 +1,31 @@ +use freya_engine::prelude::*; + +use crate::{ + Parse, + ParseError, +}; + +impl Parse for TextHeightBehavior { + fn parse(value: &str) -> Result { + match value { + "all" => Ok(TextHeightBehavior::All), + "disable-first-ascent" => Ok(TextHeightBehavior::DisableFirstAscent), + "disable-least-ascent" => Ok(TextHeightBehavior::DisableLastDescent), + "disable-all" => Ok(TextHeightBehavior::DisableAll), + _ => Err(ParseError), + } + } +} + +pub trait TextHeight { + fn needs_custom_height(&self) -> bool; +} + +impl TextHeight for TextHeightBehavior { + fn needs_custom_height(&self) -> bool { + matches!( + self, + Self::All | Self::DisableFirstAscent | Self::DisableLastDescent + ) + } +}