@@ -13,6 +13,173 @@ use cosmic_text::{
13
13
14
14
use crate :: text:: Change ;
15
15
16
+ // Temporary hack until cosmic-text deploys a new version with this fix: https://github.com/pop-os/cosmic-text/pull/396
17
+ #[ derive( Debug ) ]
18
+ pub struct FixedRunIter < ' b > {
19
+ buffer : & ' b cosmic_text:: Buffer ,
20
+ line_i : usize ,
21
+ layout_i : usize ,
22
+ total_height : f32 ,
23
+ line_top : f32 ,
24
+ }
25
+
26
+ impl < ' b > FixedRunIter < ' b > {
27
+ pub fn new ( buffer : & ' b cosmic_text:: Buffer ) -> Self {
28
+ Self {
29
+ buffer,
30
+ line_i : buffer. scroll ( ) . line ,
31
+ layout_i : 0 ,
32
+ total_height : 0.0 ,
33
+ line_top : 0.0 ,
34
+ }
35
+ }
36
+ }
37
+
38
+ impl < ' b > Iterator for FixedRunIter < ' b > {
39
+ type Item = cosmic_text:: LayoutRun < ' b > ;
40
+
41
+ fn next ( & mut self ) -> Option < Self :: Item > {
42
+ while let Some ( line) = self . buffer . lines . get ( self . line_i ) {
43
+ let shape = line. shape_opt ( ) ?;
44
+ let layout = line. layout_opt ( ) ?;
45
+ while let Some ( layout_line) = layout. get ( self . layout_i ) {
46
+ self . layout_i += 1 ;
47
+
48
+ let line_height = layout_line
49
+ . line_height_opt
50
+ . unwrap_or ( self . buffer . metrics ( ) . line_height ) ;
51
+ self . total_height += line_height;
52
+
53
+ let line_top = self . line_top - self . buffer . scroll ( ) . vertical ;
54
+ let glyph_height = layout_line. max_ascent + layout_line. max_descent ;
55
+ let centering_offset = ( line_height - glyph_height) / 2.0 ;
56
+ let line_y = line_top + centering_offset + layout_line. max_ascent ;
57
+ if let Some ( height) = self . buffer . size ( ) . 1 {
58
+ if line_y - layout_line. max_ascent > height {
59
+ return None ;
60
+ }
61
+ }
62
+ self . line_top += line_height;
63
+ if line_y + layout_line. max_descent < 0.0 {
64
+ continue ;
65
+ }
66
+
67
+ return Some ( cosmic_text:: LayoutRun {
68
+ line_i : self . line_i ,
69
+ text : line. text ( ) ,
70
+ rtl : shape. rtl ,
71
+ glyphs : & layout_line. glyphs ,
72
+ line_y,
73
+ line_top,
74
+ line_height,
75
+ line_w : layout_line. w ,
76
+ } ) ;
77
+ }
78
+ self . line_i += 1 ;
79
+ self . layout_i = 0 ;
80
+ }
81
+
82
+ None
83
+ }
84
+ }
85
+
86
+ // Reimplementation of hit() using our FixedRunIter
87
+ fn hit_fixed ( buffer : & Buffer , x : f32 , y : f32 ) -> Option < Cursor > {
88
+ let mut new_cursor_opt = None ;
89
+
90
+ let mut runs = FixedRunIter :: new ( buffer) . peekable ( ) ;
91
+ let mut first_run = true ;
92
+ while let Some ( run) = runs. next ( ) {
93
+ let line_top = run. line_top ;
94
+ let line_height = run. line_height ;
95
+
96
+ if first_run && y < line_top {
97
+ first_run = false ;
98
+ let new_cursor = Cursor :: new ( run. line_i , 0 ) ;
99
+ new_cursor_opt = Some ( new_cursor) ;
100
+ } else if y >= line_top && y < line_top + line_height {
101
+ let mut new_cursor_glyph = run. glyphs . len ( ) ;
102
+ let mut new_cursor_char = 0 ;
103
+ let mut new_cursor_affinity = cosmic_text:: Affinity :: After ;
104
+
105
+ let mut first_glyph = true ;
106
+
107
+ ' hit: for ( glyph_i, glyph) in run. glyphs . iter ( ) . enumerate ( ) {
108
+ if first_glyph {
109
+ first_glyph = false ;
110
+ if ( run. rtl && x > glyph. x ) || ( !run. rtl && x < 0.0 ) {
111
+ new_cursor_glyph = 0 ;
112
+ new_cursor_char = 0 ;
113
+ }
114
+ }
115
+ if x >= glyph. x && x <= glyph. x + glyph. w {
116
+ new_cursor_glyph = glyph_i;
117
+
118
+ let cluster = & run. text [ glyph. start ..glyph. end ] ;
119
+ let total = cluster. grapheme_indices ( true ) . count ( ) ;
120
+ let mut egc_x = glyph. x ;
121
+ let egc_w = glyph. w / ( total as f32 ) ;
122
+ for ( egc_i, egc) in cluster. grapheme_indices ( true ) {
123
+ if x >= egc_x && x <= egc_x + egc_w {
124
+ new_cursor_char = egc_i;
125
+
126
+ let right_half = x >= egc_x + egc_w / 2.0 ;
127
+ if right_half != glyph. level . is_rtl ( ) {
128
+ // If clicking on last half of glyph, move cursor past glyph
129
+ new_cursor_char += egc. len ( ) ;
130
+ new_cursor_affinity = cosmic_text:: Affinity :: Before ;
131
+ }
132
+ break ' hit;
133
+ }
134
+ egc_x += egc_w;
135
+ }
136
+
137
+ let right_half = x >= glyph. x + glyph. w / 2.0 ;
138
+ if right_half != glyph. level . is_rtl ( ) {
139
+ // If clicking on last half of glyph, move cursor past glyph
140
+ new_cursor_char = cluster. len ( ) ;
141
+ new_cursor_affinity = cosmic_text:: Affinity :: Before ;
142
+ }
143
+ break ' hit;
144
+ }
145
+ }
146
+
147
+ let mut new_cursor = Cursor :: new ( run. line_i , 0 ) ;
148
+
149
+ match run. glyphs . get ( new_cursor_glyph) {
150
+ Some ( glyph) => {
151
+ // Position at glyph
152
+ new_cursor. index = glyph. start + new_cursor_char;
153
+ new_cursor. affinity = new_cursor_affinity;
154
+ }
155
+ None => {
156
+ if let Some ( glyph) = run. glyphs . last ( ) {
157
+ // Position at end of line
158
+ new_cursor. index = glyph. end ;
159
+ new_cursor. affinity = cosmic_text:: Affinity :: Before ;
160
+ }
161
+ }
162
+ }
163
+
164
+ new_cursor_opt = Some ( new_cursor) ;
165
+
166
+ break ;
167
+ } else if runs. peek ( ) . is_none ( ) && y > run. line_y {
168
+ let mut new_cursor = Cursor :: new ( run. line_i , 0 ) ;
169
+ if let Some ( glyph) = run. glyphs . last ( ) {
170
+ new_cursor = if run. rtl {
171
+ Cursor :: new_with_affinity ( run. line_i , glyph. start , cosmic_text:: Affinity :: After )
172
+ } else {
173
+ Cursor :: new_with_affinity ( run. line_i , glyph. end , cosmic_text:: Affinity :: Before )
174
+ }
175
+ }
176
+ new_cursor_opt = Some ( new_cursor) ;
177
+ }
178
+ }
179
+
180
+ new_cursor_opt
181
+ }
182
+
16
183
/// A wrapper of [`Buffer`] for easy editing. Modified to use an external Rc<RefCell<Buffer>> reference
17
184
/// to work better with our immutable framework
18
185
#[ derive( Debug , Clone ) ]
@@ -718,7 +885,7 @@ impl Editor {
718
885
Action :: Click { x, y } => {
719
886
self . set_selection ( buffer, Selection :: None ) ;
720
887
721
- if let Some ( new_cursor) = buffer . hit ( x as f32 , y as f32 ) {
888
+ if let Some ( new_cursor) = hit_fixed ( buffer , x as f32 , y as f32 ) {
722
889
if new_cursor != self . cursor {
723
890
self . set_cursor ( buffer, new_cursor) ;
724
891
buffer. set_redraw ( true ) ;
@@ -729,7 +896,7 @@ impl Editor {
729
896
Action :: DoubleClick { x, y } => {
730
897
self . set_selection ( buffer, Selection :: None ) ;
731
898
732
- if let Some ( new_cursor) = buffer . hit ( x as f32 , y as f32 ) {
899
+ if let Some ( new_cursor) = hit_fixed ( buffer , x as f32 , y as f32 ) {
733
900
if new_cursor != self . cursor {
734
901
self . set_cursor ( buffer, new_cursor) ;
735
902
buffer. set_redraw ( true ) ;
@@ -740,7 +907,7 @@ impl Editor {
740
907
SmallVec :: new ( )
741
908
}
742
909
Action :: Drag { x, y } => {
743
- if let Some ( new_cursor) = buffer . hit ( x as f32 , y as f32 ) {
910
+ if let Some ( new_cursor) = hit_fixed ( buffer , x as f32 , y as f32 ) {
744
911
// We do not trigger a selection if only the affinity changes, because it's not visible and ends up being confusing.
745
912
if new_cursor. index != self . cursor . index || new_cursor. line != self . cursor . line
746
913
{
@@ -757,7 +924,7 @@ impl Editor {
757
924
Action :: Scroll { lines } => {
758
925
let mut scroll = buffer. scroll ( ) ;
759
926
//TODO: align to layout lines
760
- scroll. vertical += lines as f32 * buffer. metrics ( ) . line_height ;
927
+ scroll. vertical += lines as f32 * buffer. metrics ( ) . line_height + 0.0001 ; // temporary hack until cosmic-text deploys a new version with this fix: https://github.com/pop-os/cosmic-text/pull/401
761
928
buffer. set_scroll ( scroll) ;
762
929
SmallVec :: new ( )
763
930
}
0 commit comments