Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/text_selection_utils.rs
StratoSDK / crates / strato-ui-core / src / text_selection_utils.rs
1use pathfinder_geometry::{
2 rect::RectF,
3 vector::{vec2f, Vector2F},
4};
5 
6/// Parameters for creating a visual tick mark that indicates a newline in text selection.
7///
8/// Used to render small rectangular indicators at line endings when text selections
9/// extend beyond the end of a line.
10pub struct NewlineTickParams {
11 /// The top-left corner position where the tick should be drawn
12 pub tick_origin: Vector2F,
13 /// The width of the tick mark in pixels
14 pub tick_width: f32,
15 /// The height of the tick mark in pixels
16 pub tick_height: f32,
17}
18 
19/// Creates a rectangle for rendering a newline tick mark in text selection.
20///
21/// The tick mark provides a visual indicator when a text selection extends beyond
22/// the end of a line, helping users understand that the selection continues to the
23/// next line.
24pub fn create_newline_tick_rect(params: NewlineTickParams) -> RectF {
25 RectF::new(
26 params.tick_origin,
27 vec2f(params.tick_width, params.tick_height),
28 )
29}
30 
31/// Calculates the appropriate width for a newline tick mark based on font size.
32///
33/// Returns a width that scales proportionally with the font size to ensure the
34/// tick mark remains visible and appropriately sized across different text scales.
35pub fn calculate_tick_width(font_size: f32) -> f32 {
36 font_size / 2.0
37}
38 
39/// Determines if a text selection crosses a newline using row-based coordinates.
40///
41/// This function checks whether a selection that intersects with the current row
42/// extends beyond the end of that row, indicating that a newline tick should be
43/// displayed. Returns `false` for the last line since there's no newline after it.
44pub fn selection_crosses_newline_row_based(
45 current_row: usize,
46 is_last_line: bool,
47 selection_start_row: usize,
48 selection_end_row: usize,
49 selection_end_column: usize,
50 line_end_index: usize,
51) -> bool {
52 if is_last_line {
53 return false;
54 }
55 
56 let selection_starts_before_or_at_line = selection_start_row <= current_row;
57 
58 let selection_ends_beyond_line = current_row < selection_end_row
59 || (current_row == selection_end_row && selection_end_column >= line_end_index);
60 
61 selection_starts_before_or_at_line && selection_ends_beyond_line
62}
63 
64/// Determines if a text selection crosses a newline using character offset coordinates.
65///
66/// This function checks whether a selection intersects with a line and extends beyond
67/// the end of that line, indicating that a newline tick should be displayed. Uses
68/// absolute character positions rather than row/column coordinates. Returns `false`
69/// for the last line since there's no newline after it.
70pub fn selection_crosses_newline_offset_based(
71 is_last_line: bool,
72 selection_start: usize,
73 selection_end: usize,
74 line_start_offset: usize,
75 line_end_offset: usize,
76) -> bool {
77 if is_last_line {
78 return false;
79 }
80 
81 let selection_intersects_line =
82 selection_end > line_start_offset && selection_start <= line_end_offset;
83 
84 let selection_extends_beyond_line = selection_end > line_end_offset;
85 
86 selection_intersects_line && selection_extends_beyond_line
87}
88