1
- use crate :: { Frame , Id , Image , ImageSource , Response , Sense , TextStyle , Ui , Widget , WidgetText } ;
1
+ use crate :: { Frame , Id , Image , Response , Sense , Style , TextStyle , Ui , Widget , WidgetText } ;
2
2
use ahash:: HashMap ;
3
- use emath:: { Align2 , Rect , Vec2 } ;
4
- use epaint:: { Color32 , Galley } ;
3
+ use emath:: { Align2 , NumExt , Rect , Vec2 } ;
4
+ use epaint:: { Color32 , Fonts , Galley } ;
5
5
use std:: sync:: Arc ;
6
6
7
7
pub enum SizedAtomicKind < ' a > {
@@ -29,6 +29,7 @@ pub struct WidgetLayout<'a> {
29
29
pub ( crate ) frame : Frame ,
30
30
pub ( crate ) sense : Sense ,
31
31
fallback_text_color : Option < Color32 > ,
32
+ min_size : Vec2 ,
32
33
}
33
34
34
35
impl < ' a > WidgetLayout < ' a > {
@@ -39,6 +40,7 @@ impl<'a> WidgetLayout<'a> {
39
40
frame : Frame :: default ( ) ,
40
41
sense : Sense :: hover ( ) ,
41
42
fallback_text_color : None ,
43
+ min_size : Vec2 :: ZERO ,
42
44
}
43
45
}
44
46
@@ -68,14 +70,28 @@ impl<'a> WidgetLayout<'a> {
68
70
self
69
71
}
70
72
73
+ pub fn min_size ( mut self , size : Vec2 ) -> Self {
74
+ self . min_size = size;
75
+ self
76
+ }
77
+
71
78
pub fn show ( self , ui : & mut Ui ) -> AtomicLayoutResponse {
79
+ let Self {
80
+ atomics,
81
+ gap,
82
+ frame,
83
+ sense,
84
+ fallback_text_color,
85
+ min_size,
86
+ } = self ;
87
+
72
88
let fallback_text_color = self
73
89
. fallback_text_color
74
90
. unwrap_or_else ( || ui. style ( ) . visuals . text_color ( ) ) ;
75
- let gap = self . gap . unwrap_or ( ui. spacing ( ) . icon_spacing ) ;
91
+ let gap = gap. unwrap_or ( ui. spacing ( ) . icon_spacing ) ;
76
92
77
93
// The size available for the content
78
- let available_inner_size = ui. available_size ( ) - self . frame . total_margin ( ) . sum ( ) ;
94
+ let available_inner_size = ui. available_size ( ) - frame. total_margin ( ) . sum ( ) ;
79
95
80
96
let mut desired_width = 0.0 ;
81
97
let mut preferred_width = 0.0 ;
@@ -88,13 +104,25 @@ impl<'a> WidgetLayout<'a> {
88
104
89
105
let mut shrink_item = None ;
90
106
91
- if self . atomics . 0 . len ( ) > 1 {
92
- let gap_space = gap * ( self . atomics . 0 . len ( ) as f32 - 1.0 ) ;
107
+ let align2 = Align2 ( [ ui. layout ( ) . horizontal_align ( ) , ui. layout ( ) . vertical_align ( ) ] ) ;
108
+
109
+ if atomics. 0 . len ( ) > 1 {
110
+ let gap_space = gap * ( atomics. 0 . len ( ) as f32 - 1.0 ) ;
93
111
desired_width += gap_space;
94
112
preferred_width += gap_space;
95
113
}
96
114
97
- for ( ( idx, item) ) in self . atomics . 0 . into_iter ( ) . enumerate ( ) {
115
+ let max_font_size = ui
116
+ . fonts ( |fonts| {
117
+ atomics
118
+ . 0
119
+ . iter ( )
120
+ . filter_map ( |a| a. get_min_height_for_image ( fonts, ui. style ( ) ) )
121
+ . max_by ( |a, b| a. partial_cmp ( b) . unwrap_or ( std:: cmp:: Ordering :: Equal ) )
122
+ } )
123
+ . unwrap_or_else ( || ui. text_style_height ( & TextStyle :: Body ) ) ;
124
+
125
+ for ( ( idx, item) ) in atomics. 0 . into_iter ( ) . enumerate ( ) {
98
126
if item. shrink {
99
127
debug_assert ! (
100
128
shrink_item. is_none( ) ,
@@ -108,7 +136,9 @@ impl<'a> WidgetLayout<'a> {
108
136
if item. grow {
109
137
grow_count += 1 ;
110
138
}
111
- let ( preferred_size, sized) = item. kind . into_sized ( ui, available_inner_size) ;
139
+ let ( preferred_size, sized) =
140
+ item. kind
141
+ . into_sized ( ui, available_inner_size, max_font_size) ;
112
142
let size = sized. size ( ) ;
113
143
114
144
desired_width += size. x ;
@@ -125,7 +155,7 @@ impl<'a> WidgetLayout<'a> {
125
155
available_inner_size. x - desired_width,
126
156
available_inner_size. y ,
127
157
) ;
128
- let ( preferred_size, sized) = item. kind . into_sized ( ui, shrunk_size) ;
158
+ let ( preferred_size, sized) = item. kind . into_sized ( ui, shrunk_size, max_font_size ) ;
129
159
let size = sized. size ( ) ;
130
160
131
161
desired_width += size. x ;
@@ -136,25 +166,31 @@ impl<'a> WidgetLayout<'a> {
136
166
sized_items. insert ( index, sized) ;
137
167
}
138
168
139
- let margin = self . frame . total_margin ( ) ;
169
+ let margin = frame. total_margin ( ) ;
140
170
let content_size = Vec2 :: new ( desired_width, height) ;
141
- let frame_size = content_size + margin. sum ( ) ;
171
+ let frame_size = ( content_size + margin. sum ( ) ) . at_least ( min_size ) ;
142
172
143
- let ( rect, response) = ui. allocate_at_least ( frame_size, self . sense ) ;
173
+ let ( rect, response) = ui. allocate_at_least ( frame_size, sense) ;
144
174
145
175
let mut response = AtomicLayoutResponse {
146
176
response,
147
177
custom_rects : HashMap :: default ( ) ,
148
178
} ;
149
179
150
- let content_rect = rect - margin;
151
- ui. painter ( ) . add ( self . frame . paint ( content_rect ) ) ;
180
+ let inner_rect = rect - margin;
181
+ ui. painter ( ) . add ( frame. paint ( inner_rect ) ) ;
152
182
153
- let width_to_fill = content_rect . width ( ) ;
183
+ let width_to_fill = inner_rect . width ( ) ;
154
184
let extra_space = f32:: max ( width_to_fill - desired_width, 0.0 ) ;
155
185
let grow_width = f32:: max ( extra_space / grow_count as f32 , 0.0 ) ;
156
186
157
- let mut cursor = content_rect. left ( ) ;
187
+ let aligned_rect = if grow_count > 0 {
188
+ align2. align_size_within_rect ( Vec2 :: new ( width_to_fill, content_size. y ) , inner_rect)
189
+ } else {
190
+ align2. align_size_within_rect ( content_size, inner_rect)
191
+ } ;
192
+
193
+ let mut cursor = aligned_rect. left ( ) ;
158
194
159
195
for sized in sized_items {
160
196
let size = sized. size ( ) ;
@@ -164,7 +200,7 @@ impl<'a> WidgetLayout<'a> {
164
200
_ => size. x ,
165
201
} ;
166
202
167
- let frame = content_rect . with_min_x ( cursor) . with_max_x ( cursor + width) ;
203
+ let frame = aligned_rect . with_min_x ( cursor) . with_max_x ( cursor + width) ;
168
204
cursor = frame. right ( ) + gap;
169
205
170
206
let align = Align2 :: CENTER_CENTER ;
@@ -262,7 +298,12 @@ pub enum AtomicKind<'a> {
262
298
263
299
impl < ' a > AtomicKind < ' a > {
264
300
/// First returned argument is the preferred size.
265
- pub fn into_sized ( self , ui : & Ui , available_size : Vec2 ) -> ( Vec2 , SizedAtomicKind < ' a > ) {
301
+ pub fn into_sized (
302
+ self ,
303
+ ui : & Ui ,
304
+ available_size : Vec2 ,
305
+ font_size : f32 ,
306
+ ) -> ( Vec2 , SizedAtomicKind < ' a > ) {
266
307
match self {
267
308
AtomicKind :: Text ( text) => {
268
309
let galley = text. into_galley ( ui, None , available_size. x , TextStyle :: Button ) ;
@@ -272,9 +313,9 @@ impl<'a> AtomicKind<'a> {
272
313
)
273
314
}
274
315
AtomicKind :: Image ( image) => {
275
- let size =
276
- image. load_and_calc_size ( ui, Vec2 :: min ( available_size, Vec2 :: splat ( 16.0 ) ) ) ;
277
- let size = size. unwrap_or_default ( ) ;
316
+ let max_size = Vec2 :: splat ( font_size ) ;
317
+ let size = image. load_and_calc_size ( ui, Vec2 :: min ( available_size, max_size ) ) ;
318
+ let size = size. unwrap_or ( max_size ) ;
278
319
( size, SizedAtomicKind :: Image ( image, size) )
279
320
}
280
321
AtomicKind :: Custom ( id, size) => ( size, SizedAtomicKind :: Custom ( id, size) ) ,
@@ -300,6 +341,19 @@ pub fn a<'a>(i: impl Into<AtomicKind<'a>>) -> Atomic<'a> {
300
341
}
301
342
302
343
impl Atomic < ' _ > {
344
+ fn get_min_height_for_image ( & self , fonts : & Fonts , style : & Style ) -> Option < f32 > {
345
+ self . size . map ( |s| s. y ) . or_else ( || {
346
+ match & self . kind {
347
+ AtomicKind :: Text ( text) => Some ( text. font_height ( fonts, style) ) ,
348
+ AtomicKind :: Custom ( _, size) => Some ( size. y ) ,
349
+ AtomicKind :: Grow => None ,
350
+ // Since this method is used to calculate the best height for an image, we always return
351
+ // None for images.
352
+ AtomicKind :: Image ( _) => None ,
353
+ }
354
+ } )
355
+ }
356
+
303
357
// pub fn size(mut self, size: Vec2) -> Self {
304
358
// self.size = Some(size);
305
359
// self
0 commit comments