@@ -20,11 +20,14 @@ use swash::text::cluster::CharInfo;
20
20
21
21
use core:: ops:: RangeBounds ;
22
22
23
+ use crate :: inline_box:: InlineBox ;
24
+
23
25
/// Context for building a text layout.
24
26
pub struct LayoutContext < B : Brush = [ u8 ; 4 ] > {
25
27
bidi : bidi:: BidiResolver ,
26
28
rcx : ResolveContext ,
27
29
styles : Vec < RangedStyle < B > > ,
30
+ inline_boxes : Vec < InlineBox > ,
28
31
rsb : RangedStyleBuilder < B > ,
29
32
info : Vec < ( CharInfo , u16 ) > ,
30
33
scx : ShapeContext ,
@@ -36,6 +39,7 @@ impl<B: Brush> LayoutContext<B> {
36
39
bidi : bidi:: BidiResolver :: new ( ) ,
37
40
rcx : ResolveContext :: default ( ) ,
38
41
styles : vec ! [ ] ,
42
+ inline_boxes : vec ! [ ] ,
39
43
rsb : RangedStyleBuilder :: default ( ) ,
40
44
info : vec ! [ ] ,
41
45
scx : ShapeContext :: default ( ) ,
@@ -112,6 +116,10 @@ impl<'a, B: Brush, T: TextSource> RangedBuilder<'a, B, T> {
112
116
self . lcx . rsb . push ( resolved, range) ;
113
117
}
114
118
119
+ pub fn push_inline_box ( & mut self , inline_box : InlineBox ) {
120
+ self . lcx . inline_boxes . push ( inline_box) ;
121
+ }
122
+
115
123
#[ cfg( feature = "std" ) ]
116
124
pub fn build_into ( & mut self , layout : & mut Layout < B > ) {
117
125
layout. data . clear ( ) ;
@@ -121,6 +129,7 @@ impl<'a, B: Brush, T: TextSource> RangedBuilder<'a, B, T> {
121
129
let is_empty = text. is_empty ( ) ;
122
130
if is_empty {
123
131
// Force a layout to have at least one line.
132
+ // TODO: support layouts with no text
124
133
text = " " ;
125
134
}
126
135
layout. data . has_bidi = !lcx. bidi . levels ( ) . is_empty ( ) ;
@@ -135,44 +144,42 @@ impl<'a, B: Brush, T: TextSource> RangedBuilder<'a, B, T> {
135
144
char_index += 1 ;
136
145
}
137
146
}
138
- use super :: layout:: { Decoration , Style } ;
139
- fn conv_deco < B : Brush > (
140
- deco : & ResolvedDecoration < B > ,
141
- default_brush : & B ,
142
- ) -> Option < Decoration < B > > {
143
- if deco. enabled {
144
- Some ( Decoration {
145
- brush : deco. brush . clone ( ) . unwrap_or_else ( || default_brush. clone ( ) ) ,
146
- offset : deco. offset ,
147
- size : deco. size ,
148
- } )
149
- } else {
150
- None
151
- }
152
- }
153
- layout. data . styles . extend ( lcx. styles . iter ( ) . map ( |s| {
154
- let s = & s. style ;
155
- Style {
156
- brush : s. brush . clone ( ) ,
157
- underline : conv_deco ( & s. underline , & s. brush ) ,
158
- strikethrough : conv_deco ( & s. strikethrough , & s. brush ) ,
159
- line_height : s. line_height ,
160
- }
161
- } ) ) ;
147
+
148
+ // Copy the visual styles into the layout
149
+ layout
150
+ . data
151
+ . styles
152
+ . extend ( lcx. styles . iter ( ) . map ( |s| s. style . as_layout_style ( ) ) ) ;
153
+
154
+ // Sort the inline boxes as subsequent code assumes that they are in text index order.
155
+ // Note: It's important that this is a stable sort to allow users to control the order of contiguous inline boxes
156
+ lcx. inline_boxes . sort_by_key ( |b| b. index ) ;
157
+
158
+ // dbg!(&lcx.inline_boxes);
159
+
162
160
{
163
161
let query = fcx. collection . query ( & mut fcx. source_cache ) ;
164
162
super :: shape:: shape_text (
165
163
& lcx. rcx ,
166
164
query,
167
165
& lcx. styles ,
166
+ & lcx. inline_boxes ,
168
167
& lcx. info ,
169
168
lcx. bidi . levels ( ) ,
170
169
& mut lcx. scx ,
171
170
text,
172
171
layout,
173
172
) ;
174
173
}
174
+
175
+ // Move inline boxes into the layout
176
+ layout. data . inline_boxes . clear ( ) ;
177
+ core:: mem:: swap ( & mut layout. data . inline_boxes , & mut lcx. inline_boxes ) ;
178
+
175
179
layout. data . finish ( ) ;
180
+
181
+ // Extra processing if the text is empty
182
+ // TODO: update this logic to work with inline boxes
176
183
if is_empty {
177
184
layout. data . text_len = 0 ;
178
185
let run = & mut layout. data . runs [ 0 ] ;
0 commit comments