Skip to content

Commit

Permalink
feat: highlight_mode attribute (#704)
Browse files Browse the repository at this point in the history
* feat: Built-in vertical alignment for text

* feat: `highlight_mode` attribute

* clean up

* text navigation fixes

* clean up

* clean up and bug fixes

* parse tests
  • Loading branch information
marc2332 authored Jun 15, 2024
1 parent 7e6d388 commit 4da5788
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 40 deletions.
4 changes: 2 additions & 2 deletions crates/core/src/dom/paragraph_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use torin::prelude::*;

use crate::{
dom::DioxusNode,
layout::relative_align_main_align_paragraph,
layout::align_main_align_paragraph,
};

pub fn measure_paragraph(
Expand All @@ -36,7 +36,7 @@ pub fn measure_paragraph(
return;
}

let y = relative_align_main_align_paragraph(node, &layout_node.area, paragraph);
let y = align_main_align_paragraph(node, &layout_node.area, paragraph);

if let Some(cursor_reference) = &cursor_settings.cursor_ref {
if let Some(cursor_position) = text_measurement.cursor_position {
Expand Down
45 changes: 28 additions & 17 deletions crates/core/src/layout/skia_measurer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use freya_native_core::{
NodeId,
};
use freya_node_state::{
CursorSettings,
FontStyleState,
HighlightMode,
LayoutState,
TextOverflow,
};
Expand Down Expand Up @@ -156,30 +158,39 @@ pub fn create_label(
paragraph
}

/// Align the main alignment of a paragraph
pub fn align_main_align_paragraph(
/// Align the Y axis of the highlights and cursor of a paragraph
pub fn align_highlights_and_cursor_paragraph(
node: &DioxusNode,
area: &Area,
paragraph: &Paragraph,
) -> Point2D {
let layout = node.get::<LayoutState>().unwrap();
cursor_rect: &TextBox,
width: Option<f32>,
) -> (Point2D, Point2D) {
let cursor_settings = node.get::<CursorSettings>().unwrap();

let x = area.min_x() + cursor_rect.rect.left;
let x2 = x + width.unwrap_or(cursor_rect.rect.right - cursor_rect.rect.left);

match cursor_settings.highlight_mode {
HighlightMode::Fit => {
let y = area.min_y()
+ align_main_align_paragraph(node, area, paragraph)
+ cursor_rect.rect.top;
let y2 = y + (cursor_rect.rect.bottom - cursor_rect.rect.top);

(Point2D::new(x, y), Point2D::new(x2, y2))
}
HighlightMode::Expanded => {
let y = area.min_y();
let y2 = area.max_y();

match layout.main_alignment {
Alignment::Start => area.origin,
Alignment::Center => Point2D::new(
area.min_x(),
area.min_y() + (area.height() / 2.0) - (paragraph.height() / 2.0),
),
Alignment::End => Point2D::new(area.min_x(), area.max_y() - paragraph.height()),
(Point2D::new(x, y), Point2D::new(x2, y2))
}
}
}

/// Align relatively the main alignment of a paragraph
pub fn relative_align_main_align_paragraph(
node: &DioxusNode,
area: &Area,
paragraph: &Paragraph,
) -> f32 {
/// Align the main alignment of a paragraph
pub fn align_main_align_paragraph(node: &DioxusNode, area: &Area, paragraph: &Paragraph) -> f32 {
let layout = node.get::<LayoutState>().unwrap();

match layout.main_alignment {
Expand Down
1 change: 1 addition & 0 deletions crates/elements/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ builder_constructors! {
focus_id: AccessibilityId,
highlights: String,
highlight_color: String,
highlight_mode: String,
};
/// `text` element is simply a text span used for the `paragraph` element.
text {
Expand Down
2 changes: 2 additions & 0 deletions crates/native-core/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub enum AttributeName {
CursorId,
Highlights,
HighlightColor,
HighlightMode,
ImageReference,
ImageData,
SvgData,
Expand Down Expand Up @@ -131,6 +132,7 @@ impl FromStr for AttributeName {
"cursor_id" => Ok(AttributeName::CursorId),
"highlights" => Ok(AttributeName::Highlights),
"highlight_color" => Ok(AttributeName::HighlightColor),
"highlight_mode" => Ok(AttributeName::HighlightMode),
"image_reference" => Ok(AttributeName::ImageReference),
"image_data" => Ok(AttributeName::ImageData),
"svg_data" => Ok(AttributeName::SvgData),
Expand Down
3 changes: 2 additions & 1 deletion crates/renderer/src/elements/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ pub fn render_label(
) {
let paragraph = &data.as_ref().unwrap().get::<CachedParagraph>().unwrap().0;

let (x, y) = align_main_align_paragraph(dioxus_node, area, paragraph).to_tuple();
let x = area.min_x();
let y = area.min_y() + align_main_align_paragraph(dioxus_node, area, paragraph);

paragraph.paint(canvas, (x, y));
}
33 changes: 17 additions & 16 deletions crates/renderer/src/elements/paragraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use freya_common::CachedParagraph;
use freya_core::{
dom::DioxusNode,
layout::create_paragraph,
prelude::align_main_align_paragraph,
prelude::{
align_highlights_and_cursor_paragraph,
align_main_align_paragraph,
},
};
use freya_engine::prelude::*;
use freya_native_core::{
Expand All @@ -27,7 +30,8 @@ pub fn render_paragraph(
let node_cursor_settings = &*dioxus_node.get::<CursorSettings>().unwrap();

let paint = |paragraph: &Paragraph| {
let (x, y) = align_main_align_paragraph(dioxus_node, area, paragraph).to_tuple();
let x = area.min_x();
let y = area.min_y() + align_main_align_paragraph(dioxus_node, area, paragraph);

// Draw the highlights if specified
draw_cursor_highlights(area, paragraph, canvas, dioxus_node);
Expand Down Expand Up @@ -60,7 +64,6 @@ fn draw_cursor_highlights(
canvas: &Canvas,
dioxus_node: &DioxusNode,
) -> Option<()> {
let (x, y) = align_main_align_paragraph(dioxus_node, area, paragraph).to_tuple();
let node_cursor_settings = &*dioxus_node.get::<CursorSettings>().unwrap();

let highlights = node_cursor_settings.highlights.as_ref()?;
Expand All @@ -80,18 +83,20 @@ fn draw_cursor_highlights(
RectWidthStyle::Tight,
);
for cursor_rect in cursor_rects {
let x = x + cursor_rect.rect.left;
let y = y + cursor_rect.rect.top;

let x2 = x + (cursor_rect.rect.right - cursor_rect.rect.left);
let y2 = y + (cursor_rect.rect.bottom - cursor_rect.rect.top);
let (start, end) = align_highlights_and_cursor_paragraph(
dioxus_node,
area,
paragraph,
&cursor_rect,
None,
);

let mut paint = Paint::default();
paint.set_anti_alias(true);
paint.set_style(PaintStyle::Fill);
paint.set_color(highlight_color);

canvas.draw_rect(Rect::new(x, y, x2, y2), &paint);
canvas.draw_rect(Rect::new(start.x, start.y, end.x, end.y), &paint);
}
}

Expand All @@ -104,7 +109,6 @@ fn draw_cursor(
canvas: &Canvas,
dioxus_node: &DioxusNode,
) -> Option<()> {
let (x, y) = align_main_align_paragraph(dioxus_node, area, paragraph).to_tuple();
let node_cursor_settings = &*dioxus_node.get::<CursorSettings>().unwrap();

let cursor = node_cursor_settings.position?;
Expand All @@ -118,18 +122,15 @@ fn draw_cursor(
);
let cursor_rect = cursor_rects.first()?;

let x = x + cursor_rect.rect.left;
let y = y + cursor_rect.rect.top;

let x2 = x + 1.0;
let y2 = y + (cursor_rect.rect.bottom - cursor_rect.rect.top);
let (start, end) =
align_highlights_and_cursor_paragraph(dioxus_node, area, paragraph, cursor_rect, Some(1.0));

let mut paint = Paint::default();
paint.set_anti_alias(true);
paint.set_style(PaintStyle::Fill);
paint.set_color(cursor_color);

canvas.draw_rect(Rect::new(x, y, x2, y2), &paint);
canvas.draw_rect(Rect::new(start.x, start.y, end.x, end.y), &paint);

Some(())
}
11 changes: 11 additions & 0 deletions crates/state/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
CursorMode,
CursorReference,
CustomAttributeValues,
HighlightMode,
Parse,
};

Expand All @@ -31,6 +32,7 @@ pub struct CursorSettings {
pub cursor_id: Option<usize>,
pub highlights: Option<Vec<(usize, usize)>>,
pub highlight_color: Color,
pub highlight_mode: HighlightMode,
pub cursor_ref: Option<CursorReference>,
}

Expand All @@ -43,6 +45,7 @@ impl Default for CursorSettings {
cursor_id: None,
highlights: None,
highlight_color: Color::from_rgb(87, 108, 188),
highlight_mode: HighlightMode::default(),
cursor_ref: None,
}
}
Expand All @@ -64,6 +67,7 @@ impl State<CustomAttributeValues> for CursorSettings {
AttributeName::CursorId,
AttributeName::Highlights,
AttributeName::HighlightColor,
AttributeName::HighlightMode,
AttributeName::CursorReference,
]))
.with_tag();
Expand Down Expand Up @@ -124,6 +128,13 @@ impl State<CustomAttributeValues> for CursorSettings {
}
}
}
AttributeName::HighlightMode => {
if let Some(value) = attr.value.as_text() {
if let Ok(highlight_mode) = HighlightMode::parse(value) {
cursor.highlight_mode = highlight_mode;
}
}
}
AttributeName::CursorReference => {
if let OwnedAttributeValue::Custom(
CustomAttributeValues::CursorReference(reference),
Expand Down
24 changes: 24 additions & 0 deletions crates/state/src/values/highlight.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::Parse;

#[derive(Default, Clone, Debug, PartialEq)]
pub enum HighlightMode {
#[default]
/// Highlight considering the actual measure text bounds.
Fit,
/// Highlight considering the `paragraph` element bounds.
Expanded,
}

#[derive(Debug, PartialEq, Eq)]
pub struct HighlightModeError;

impl Parse for HighlightMode {
type Err = HighlightModeError;

fn parse(value: &str) -> Result<Self, Self::Err> {
match value {
"expanded" => Ok(HighlightMode::Expanded),
_ => Ok(HighlightMode::Fit),
}
}
}
2 changes: 2 additions & 0 deletions crates/state/src/values/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod fill;
mod font;
mod gaps;
mod gradient;
mod highlight;
mod overflow;
mod position;
mod shadow;
Expand All @@ -26,6 +27,7 @@ pub use fill::*;
pub use font::*;
pub use gaps::*;
pub use gradient::*;
pub use highlight::*;
pub use overflow::*;
pub use position::*;
pub use shadow::*;
Expand Down
23 changes: 23 additions & 0 deletions crates/state/tests/parse_highlight.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use freya_engine::prelude::*;
use freya_node_state::{
HighlightMode,
Parse,
};

#[test]
fn parse_expanded_highlight_mode() {
let expanded = HighlightMode::parse("expanded");
assert_eq!(expanded, Ok(HighlightMode::Expanded));
}

#[test]
fn parse_fit_highlight_mode() {
let fit = HighlightMode::parse("fit");
assert_eq!(fit, Ok(HighlightMode::Fit));
}

#[test]
fn parse_fallback_highlight_mode() {
let fallback = HighlightMode::parse("Hello, World!");
assert_eq!(fallback, Ok(HighlightMode::Fit));
}
9 changes: 5 additions & 4 deletions examples/cloned_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn Body() -> Element {
cursor_id: "{line_index}",
onmousedown,
onmouseover,
highlights: highlights,
highlights,
text {
color: "rgb(240, 240, 240)",
font_size: "15",
Expand All @@ -138,7 +138,7 @@ fn Body() -> Element {
width: "50%".into(),
}),
length: editor.len_lines(),
item_size: 100.0,
item_size: 60.0,
scroll_with_arrows: false,
cache_elements: false,
builder: move |line_index, _: &Option<()>| {
Expand Down Expand Up @@ -175,7 +175,7 @@ fn Body() -> Element {
rect {
key: "{line_index}",
width: "100%",
height: "100",
height: "60",
direction: "horizontal",
background: "{line_background}",
label {
Expand All @@ -198,7 +198,8 @@ fn Body() -> Element {
cursor_id: "{line_index}",
onmousedown,
onmouseover,
highlights: highlights,
highlights,
highlight_mode: "expanded",
text {
color: "rgb(240, 240, 240)",
font_size: "15",
Expand Down

0 comments on commit 4da5788

Please sign in to comment.