1414
1515//! Text editing movements.
1616
17- use crate :: text:: { EditableText , Selection } ;
17+ use crate :: kurbo:: Point ;
18+ use crate :: piet:: TextLayout as _;
19+ use crate :: text:: { EditableText , Selection , TextLayout , TextStorage } ;
1820
1921/// The specification of a movement.
2022#[ derive( Debug , PartialEq , Clone , Copy ) ]
@@ -23,6 +25,10 @@ pub enum Movement {
2325 Left ,
2426 /// Move to the right by one grapheme cluster.
2527 Right ,
28+ /// Move up one visible line.
29+ Up ,
30+ /// Move down one visible line.
31+ Down ,
2632 /// Move to the left by one word.
2733 LeftWord ,
2834 /// Move to the right by one word.
@@ -37,44 +43,98 @@ pub enum Movement {
3743 EndOfDocument ,
3844}
3945
40- /// Compute the result of movement on a selection .
41- pub fn movement ( m : Movement , s : Selection , text : & impl EditableText , modify : bool ) -> Selection {
42- let offset = match m {
46+ /// Compute the result of movement on a selection.
47+ ///
48+ /// returns a new selection representing the state after the movement.
49+ ///
50+ /// If `modify` is true, only the leading edge (the 'end') of the selection
51+ /// should be changed; this is the case when the user moves with the shift
52+ /// key pressed.
53+ pub fn movement < T : EditableText + TextStorage > (
54+ m : Movement ,
55+ s : Selection ,
56+ layout : & TextLayout < T > ,
57+ modify : bool ,
58+ ) -> Selection {
59+ let ( text, layout) = match ( layout. text ( ) , layout. layout ( ) ) {
60+ ( Some ( text) , Some ( layout) ) => ( text, layout) ,
61+ _ => {
62+ debug_assert ! ( false , "movement() called before layout rebuild" ) ;
63+ return s;
64+ }
65+ } ;
66+
67+ let ( offset, h_pos) = match m {
4368 Movement :: Left => {
4469 if s. is_caret ( ) || modify {
45- text. prev_grapheme_offset ( s. end ) . unwrap_or ( 0 )
70+ text. prev_grapheme_offset ( s. end )
71+ . map ( |off| ( off, None ) )
72+ . unwrap_or ( ( 0 , s. h_pos ) )
4673 } else {
47- s. min ( )
74+ ( s. min ( ) , None )
4875 }
4976 }
5077 Movement :: Right => {
5178 if s. is_caret ( ) || modify {
52- text. next_grapheme_offset ( s. end ) . unwrap_or ( s. end )
79+ text. next_grapheme_offset ( s. end )
80+ . map ( |off| ( off, None ) )
81+ . unwrap_or ( ( s. end , s. h_pos ) )
5382 } else {
54- s. max ( )
83+ ( s. max ( ) , None )
5584 }
5685 }
5786
58- Movement :: PrecedingLineBreak => text. preceding_line_break ( s. end ) ,
59- Movement :: NextLineBreak => text. next_line_break ( s. end ) ,
87+ Movement :: Up => {
88+ let cur_pos = layout. hit_test_text_position ( s. end ) ;
89+ if cur_pos. line == 0 {
90+ ( 0 , Some ( cur_pos. point . x ) )
91+ } else {
92+ let lm = layout. line_metric ( cur_pos. line ) . unwrap ( ) ;
93+ let h_pos = s. h_pos . unwrap_or ( cur_pos. point . x ) ;
94+ let point_above = Point :: new ( h_pos, cur_pos. point . y - lm. height ) ;
95+ let up_pos = layout. hit_test_point ( point_above) ;
96+ ( up_pos. idx , Some ( point_above. x ) )
97+ }
98+ }
99+ Movement :: Down => {
100+ let cur_pos = layout. hit_test_text_position ( s. end ) ;
101+ if cur_pos. line == layout. line_count ( ) - 1 {
102+ ( text. len ( ) , Some ( cur_pos. point . x ) )
103+ } else {
104+ let lm = layout. line_metric ( cur_pos. line ) . unwrap ( ) ;
105+ let h_pos = s. h_pos . unwrap_or ( cur_pos. point . x ) ;
106+ // may not work correctly for point sizes below 1.0
107+ let y_below = lm. y_offset + lm. height + 1.0 ;
108+ let point_below = Point :: new ( h_pos, y_below) ;
109+ let up_pos = layout. hit_test_point ( point_below) ;
110+ ( up_pos. idx , Some ( point_below. x ) )
111+ }
112+ }
60113
61- Movement :: StartOfDocument => 0 ,
62- Movement :: EndOfDocument => text. len ( ) ,
114+ Movement :: PrecedingLineBreak => ( text. preceding_line_break ( s. end ) , None ) ,
115+ Movement :: NextLineBreak => ( text. next_line_break ( s. end ) , None ) ,
116+
117+ Movement :: StartOfDocument => ( 0 , None ) ,
118+ Movement :: EndOfDocument => ( text. len ( ) , None ) ,
63119
64120 Movement :: LeftWord => {
65- if s. is_caret ( ) || modify {
121+ let offset = if s. is_caret ( ) || modify {
66122 text. prev_word_offset ( s. end ) . unwrap_or ( 0 )
67123 } else {
68124 s. min ( )
69- }
125+ } ;
126+ ( offset, None )
70127 }
71128 Movement :: RightWord => {
72- if s. is_caret ( ) || modify {
129+ let offset = if s. is_caret ( ) || modify {
73130 text. next_word_offset ( s. end ) . unwrap_or ( s. end )
74131 } else {
75132 s. max ( )
76- }
133+ } ;
134+ ( offset, None )
77135 }
78136 } ;
79- Selection :: new ( if modify { s. start } else { offset } , offset)
137+
138+ let start = if modify { s. start } else { offset } ;
139+ Selection :: new ( start, offset) . with_h_pos ( h_pos)
80140}
0 commit comments