@akiojin/gwt 9.0.4 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.ja.md +106 -146
  2. package/README.md +103 -143
  3. package/bin/gwt.cjs +1 -1
  4. package/package.json +5 -5
  5. package/rustfmt.toml +0 -2
  6. package/scripts/check-release-flow.sh +2 -8
  7. package/scripts/postinstall.js +17 -7
  8. package/scripts/run-local-backend-tests-on-commit.sh +6 -12
  9. package/scripts/test-all.sh +1 -5
  10. package/scripts/check-e2e-coverage-threshold.mjs +0 -238
  11. package/scripts/run-local-e2e-coverage-on-commit.sh +0 -69
  12. package/scripts/run-local-e2e-on-commit.sh +0 -60
  13. package/scripts/verify-ci-node-toolchain.sh +0 -76
  14. package/scripts/voice-eval.sh +0 -48
  15. package/vendor/ratatui-core/src/backend/test.rs +0 -1077
  16. package/vendor/ratatui-core/src/backend.rs +0 -405
  17. package/vendor/ratatui-core/src/buffer/assert.rs +0 -71
  18. package/vendor/ratatui-core/src/buffer/buffer.rs +0 -1388
  19. package/vendor/ratatui-core/src/buffer/cell.rs +0 -377
  20. package/vendor/ratatui-core/src/buffer.rs +0 -9
  21. package/vendor/ratatui-core/src/layout/alignment.rs +0 -89
  22. package/vendor/ratatui-core/src/layout/constraint.rs +0 -526
  23. package/vendor/ratatui-core/src/layout/direction.rs +0 -63
  24. package/vendor/ratatui-core/src/layout/flex.rs +0 -212
  25. package/vendor/ratatui-core/src/layout/layout.rs +0 -2838
  26. package/vendor/ratatui-core/src/layout/margin.rs +0 -79
  27. package/vendor/ratatui-core/src/layout/offset.rs +0 -66
  28. package/vendor/ratatui-core/src/layout/position.rs +0 -253
  29. package/vendor/ratatui-core/src/layout/rect/iter.rs +0 -356
  30. package/vendor/ratatui-core/src/layout/rect/ops.rs +0 -136
  31. package/vendor/ratatui-core/src/layout/rect.rs +0 -1114
  32. package/vendor/ratatui-core/src/layout/size.rs +0 -147
  33. package/vendor/ratatui-core/src/layout.rs +0 -333
  34. package/vendor/ratatui-core/src/lib.rs +0 -82
  35. package/vendor/ratatui-core/src/style/anstyle.rs +0 -348
  36. package/vendor/ratatui-core/src/style/color.rs +0 -788
  37. package/vendor/ratatui-core/src/style/palette/material.rs +0 -608
  38. package/vendor/ratatui-core/src/style/palette/tailwind.rs +0 -653
  39. package/vendor/ratatui-core/src/style/palette.rs +0 -6
  40. package/vendor/ratatui-core/src/style/palette_conversion.rs +0 -82
  41. package/vendor/ratatui-core/src/style/stylize.rs +0 -668
  42. package/vendor/ratatui-core/src/style.rs +0 -1069
  43. package/vendor/ratatui-core/src/symbols/bar.rs +0 -51
  44. package/vendor/ratatui-core/src/symbols/block.rs +0 -51
  45. package/vendor/ratatui-core/src/symbols/border.rs +0 -709
  46. package/vendor/ratatui-core/src/symbols/braille.rs +0 -21
  47. package/vendor/ratatui-core/src/symbols/half_block.rs +0 -3
  48. package/vendor/ratatui-core/src/symbols/line.rs +0 -259
  49. package/vendor/ratatui-core/src/symbols/marker.rs +0 -82
  50. package/vendor/ratatui-core/src/symbols/merge.rs +0 -748
  51. package/vendor/ratatui-core/src/symbols/pixel.rs +0 -30
  52. package/vendor/ratatui-core/src/symbols/scrollbar.rs +0 -46
  53. package/vendor/ratatui-core/src/symbols/shade.rs +0 -5
  54. package/vendor/ratatui-core/src/symbols.rs +0 -15
  55. package/vendor/ratatui-core/src/terminal/frame.rs +0 -192
  56. package/vendor/ratatui-core/src/terminal/terminal.rs +0 -926
  57. package/vendor/ratatui-core/src/terminal/viewport.rs +0 -58
  58. package/vendor/ratatui-core/src/terminal.rs +0 -40
  59. package/vendor/ratatui-core/src/text/grapheme.rs +0 -84
  60. package/vendor/ratatui-core/src/text/line.rs +0 -1678
  61. package/vendor/ratatui-core/src/text/masked.rs +0 -149
  62. package/vendor/ratatui-core/src/text/span.rs +0 -904
  63. package/vendor/ratatui-core/src/text/text.rs +0 -1434
  64. package/vendor/ratatui-core/src/text.rs +0 -64
  65. package/vendor/ratatui-core/src/widgets/stateful_widget.rs +0 -193
  66. package/vendor/ratatui-core/src/widgets/widget.rs +0 -174
  67. package/vendor/ratatui-core/src/widgets.rs +0 -9
@@ -1,2838 +0,0 @@
1
- use alloc::rc::Rc;
2
- use alloc::vec::Vec;
3
- use core::array::TryFromSliceError;
4
- use core::iter;
5
- #[cfg(feature = "layout-cache")]
6
- use core::num::NonZeroUsize;
7
-
8
- use hashbrown::HashMap;
9
- use itertools::Itertools;
10
- use kasuari::WeightedRelation::{EQ, GE, LE};
11
- use kasuari::{AddConstraintError, Expression, Solver, Strength, Variable};
12
- #[cfg(feature = "layout-cache")]
13
- use lru::LruCache;
14
-
15
- use self::strengths::{
16
- ALL_SEGMENT_GROW, FILL_GROW, GROW, LENGTH_SIZE_EQ, MAX_SIZE_EQ, MAX_SIZE_LE, MIN_SIZE_EQ,
17
- MIN_SIZE_GE, PERCENTAGE_SIZE_EQ, RATIO_SIZE_EQ, SPACE_GROW, SPACER_SIZE_EQ,
18
- };
19
- use crate::layout::{Constraint, Direction, Flex, Margin, Rect};
20
-
21
- type Rects = Rc<[Rect]>;
22
- type Segments = Rects;
23
- type Spacers = Rects;
24
- // The solution to a Layout solve contains two `Rects`, where `Rects` is effectively a `[Rect]`.
25
- //
26
- // 1. `[Rect]` that contains positions for the segments corresponding to user provided constraints
27
- // 2. `[Rect]` that contains spacers around the user provided constraints
28
- //
29
- // <------------------------------------80 px------------------------------------->
30
- // ┌ ┐┌──────────────────┐┌ ┐┌──────────────────┐┌ ┐┌──────────────────┐┌ ┐
31
- // 1 │ a │ 2 │ b │ 3 │ c │ 4
32
- // └ ┘└──────────────────┘└ ┘└──────────────────┘└ ┘└──────────────────┘└ ┘
33
- //
34
- // Number of spacers will always be one more than number of segments.
35
- #[cfg(feature = "layout-cache")]
36
- type Cache = LruCache<(Rect, Layout), (Segments, Spacers)>;
37
-
38
- // Multiplier that decides floating point precision when rounding.
39
- // The number of zeros in this number is the precision for the rounding of f64 to u16 in layout
40
- // calculations.
41
- const FLOAT_PRECISION_MULTIPLIER: f64 = 100.0;
42
-
43
- #[cfg(feature = "layout-cache")]
44
- std::thread_local! {
45
- static LAYOUT_CACHE: core::cell::RefCell<Cache> = core::cell::RefCell::new(Cache::new(
46
- NonZeroUsize::new(Layout::DEFAULT_CACHE_SIZE).unwrap(),
47
- ));
48
- }
49
-
50
- /// Represents the spacing between segments in a layout.
51
- ///
52
- /// The `Spacing` enum is used to define the spacing between segments in a layout. It can represent
53
- /// either positive spacing (space between segments) or negative spacing (overlap between segments).
54
- ///
55
- /// # Variants
56
- ///
57
- /// - `Space(u16)`: Represents positive spacing between segments. The value indicates the number of
58
- /// cells.
59
- /// - `Overlap(u16)`: Represents negative spacing, causing overlap between segments. The value
60
- /// indicates the number of overlapping cells.
61
- ///
62
- /// # Default
63
- ///
64
- /// The default value for `Spacing` is `Space(0)`, which means no spacing or no overlap between
65
- /// segments.
66
- ///
67
- /// # Conversions
68
- ///
69
- /// The `Spacing` enum can be created from different integer types:
70
- ///
71
- /// - From `u16`: Directly converts the value to `Spacing::Space`.
72
- /// - From `i16`: Converts negative values to `Spacing::Overlap` and non-negative values to
73
- /// `Spacing::Space`.
74
- /// - From `i32`: Clamps the value to the range of `i16` and converts negative values to
75
- /// `Spacing::Overlap` and non-negative values to `Spacing::Space`.
76
- ///
77
- /// See the [`Layout::spacing`] method for details on how to use this enum.
78
- #[derive(Debug, Clone, Eq, PartialEq, Hash)]
79
- #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80
- pub enum Spacing {
81
- Space(u16),
82
- Overlap(u16),
83
- }
84
-
85
- impl Default for Spacing {
86
- fn default() -> Self {
87
- Self::Space(0)
88
- }
89
- }
90
-
91
- impl From<i32> for Spacing {
92
- fn from(value: i32) -> Self {
93
- Self::from(value.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16)
94
- }
95
- }
96
-
97
- impl From<u16> for Spacing {
98
- fn from(value: u16) -> Self {
99
- Self::Space(value)
100
- }
101
- }
102
-
103
- impl From<i16> for Spacing {
104
- fn from(value: i16) -> Self {
105
- if value < 0 {
106
- Self::Overlap(value.unsigned_abs())
107
- } else {
108
- Self::Space(value.unsigned_abs())
109
- }
110
- }
111
- }
112
-
113
- /// The primary layout engine for dividing terminal space using constraints and direction.
114
- ///
115
- /// A layout is a set of constraints that can be applied to a given area to split it into smaller
116
- /// rectangular areas. This is the core building block for creating structured user interfaces in
117
- /// terminal applications.
118
- ///
119
- /// A layout is composed of:
120
- /// - a direction (horizontal or vertical)
121
- /// - a set of constraints (length, ratio, percentage, fill, min, max)
122
- /// - a margin (horizontal and vertical), the space between the edge of the main area and the split
123
- /// areas
124
- /// - a flex option that controls space distribution
125
- /// - a spacing option that controls gaps between segments
126
- ///
127
- /// The algorithm used to compute the layout is based on the [`kasuari`] solver, a linear constraint
128
- /// solver that computes positions and sizes to satisfy as many constraints as possible in order of
129
- /// their priorities.
130
- ///
131
- /// When the layout is computed, the result is cached in a thread-local cache, so that subsequent
132
- /// calls with the same parameters are faster. The cache is a `LruCache`, and the size of the cache
133
- /// can be configured using [`Layout::init_cache()`] when the `layout-cache` feature is enabled.
134
- ///
135
- /// # Construction
136
- ///
137
- /// - [`default`](Default::default) - Create a layout with default values (vertical direction, no
138
- /// constraints, no margin)
139
- /// - [`new`](Self::new) - Create a new layout with a given direction and constraints
140
- /// - [`vertical`](Self::vertical) - Create a new vertical layout with the given constraints
141
- /// - [`horizontal`](Self::horizontal) - Create a new horizontal layout with the given constraints
142
- ///
143
- /// # Configuration
144
- ///
145
- /// - [`direction`](Self::direction) - Set the direction of the layout
146
- /// - [`constraints`](Self::constraints) - Set the constraints of the layout
147
- /// - [`margin`](Self::margin) - Set uniform margin on all sides
148
- /// - [`horizontal_margin`](Self::horizontal_margin) - Set the horizontal margin of the layout
149
- /// - [`vertical_margin`](Self::vertical_margin) - Set the vertical margin of the layout
150
- /// - [`flex`](Self::flex) - Set the way space is distributed when constraints are satisfied
151
- /// - [`spacing`](Self::spacing) - Set the gap between the constraints of the layout
152
- ///
153
- /// # Layout Operations
154
- ///
155
- /// - [`areas`](Self::areas) - Split area into fixed number of rectangles (compile-time known)
156
- /// - [`spacers`](Self::spacers) - Get spacer rectangles between layout areas
157
- /// - [`split`](Self::split) - Split area into rectangles (runtime determined count)
158
- /// - [`split_with_spacers`](Self::split_with_spacers) - Split area and return both areas and
159
- /// spacers
160
- ///
161
- /// # Cache Management
162
- ///
163
- /// - [`init_cache`](Self::init_cache) - Initialize layout cache with custom size
164
- ///
165
- /// # Example
166
- ///
167
- /// ```rust
168
- /// use ratatui_core::buffer::Buffer;
169
- /// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
170
- /// use ratatui_core::text::Text;
171
- /// use ratatui_core::widgets::Widget;
172
- ///
173
- /// fn render(area: Rect, buf: &mut Buffer) {
174
- /// let layout = Layout::vertical([Constraint::Length(5), Constraint::Fill(1)]);
175
- /// let [top, bottom] = layout.areas(area);
176
- /// Text::from("foo").render(top, buf);
177
- /// Text::from("bar").render(bottom, buf);
178
- /// }
179
- /// ```
180
- ///
181
- /// See the `layout`, `flex`, and `constraints` examples in the [Examples] folder for more details
182
- /// about how to use layouts.
183
- ///
184
- /// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
185
- ///
186
- /// ![layout
187
- /// example](https://camo.githubusercontent.com/77d22f3313b782a81e5e033ef82814bb48d786d2598699c27f8e757ccee62021/68747470733a2f2f7668732e636861726d2e73682f7668732d315a4e6f4e4c4e6c4c746b4a58706767396e435635652e676966)
188
- ///
189
- /// [`kasuari`]: https://crates.io/crates/kasuari
190
- /// [Examples]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
191
- #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
192
- #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
193
- pub struct Layout {
194
- direction: Direction,
195
- constraints: Vec<Constraint>,
196
- margin: Margin,
197
- flex: Flex,
198
- spacing: Spacing,
199
- }
200
-
201
- impl Layout {
202
- /// This is a somewhat arbitrary size for the layout cache based on adding the columns and rows
203
- /// on my laptop's terminal (171+51 = 222) and doubling it for good measure and then adding a
204
- /// bit more to make it a round number. This gives enough entries to store a layout for every
205
- /// row and every column, twice over, which should be enough for most apps. For those that need
206
- /// more, the cache size can be set with `Layout::init_cache()` (requires the `layout-cache`
207
- /// feature).
208
- #[cfg(feature = "layout-cache")]
209
- pub const DEFAULT_CACHE_SIZE: usize = 500;
210
-
211
- /// Creates a new layout with default values.
212
- ///
213
- /// The `constraints` parameter accepts any type that implements `IntoIterator<Item =
214
- /// Into<Constraint>>`. This includes arrays, slices, vectors, iterators. `Into<Constraint>` is
215
- /// implemented on `u16`, so you can pass an array, `Vec`, etc. of `u16` to this function to
216
- /// create a layout with fixed size chunks.
217
- ///
218
- /// Default values for the other fields are:
219
- ///
220
- /// - `margin`: 0, 0
221
- /// - `flex`: [`Flex::Start`]
222
- /// - `spacing`: 0
223
- ///
224
- /// # Examples
225
- ///
226
- /// ```rust
227
- /// use ratatui_core::layout::{Constraint, Direction, Layout};
228
- ///
229
- /// Layout::new(
230
- /// Direction::Horizontal,
231
- /// [Constraint::Length(5), Constraint::Fill(1)],
232
- /// );
233
- ///
234
- /// Layout::new(
235
- /// Direction::Vertical,
236
- /// [1, 2, 3].iter().map(|&c| Constraint::Length(c)),
237
- /// );
238
- ///
239
- /// Layout::new(Direction::Horizontal, vec![1, 2]);
240
- /// ```
241
- pub fn new<I>(direction: Direction, constraints: I) -> Self
242
- where
243
- I: IntoIterator,
244
- I::Item: Into<Constraint>,
245
- {
246
- Self {
247
- direction,
248
- constraints: constraints.into_iter().map(Into::into).collect(),
249
- ..Self::default()
250
- }
251
- }
252
-
253
- /// Creates a new vertical layout with default values.
254
- ///
255
- /// The `constraints` parameter accepts any type that implements `IntoIterator<Item =
256
- /// Into<Constraint>>`. This includes arrays, slices, vectors, iterators, etc.
257
- ///
258
- /// # Examples
259
- ///
260
- /// ```rust
261
- /// use ratatui_core::layout::{Constraint, Layout};
262
- ///
263
- /// let layout = Layout::vertical([Constraint::Length(5), Constraint::Fill(1)]);
264
- /// ```
265
- pub fn vertical<I>(constraints: I) -> Self
266
- where
267
- I: IntoIterator,
268
- I::Item: Into<Constraint>,
269
- {
270
- Self::new(Direction::Vertical, constraints.into_iter().map(Into::into))
271
- }
272
-
273
- /// Creates a new horizontal layout with default values.
274
- ///
275
- /// The `constraints` parameter accepts any type that implements `IntoIterator<Item =
276
- /// Into<Constraint>>`. This includes arrays, slices, vectors, iterators, etc.
277
- ///
278
- /// # Examples
279
- ///
280
- /// ```rust
281
- /// use ratatui_core::layout::{Constraint, Layout};
282
- ///
283
- /// let layout = Layout::horizontal([Constraint::Length(5), Constraint::Fill(1)]);
284
- /// ```
285
- pub fn horizontal<I>(constraints: I) -> Self
286
- where
287
- I: IntoIterator,
288
- I::Item: Into<Constraint>,
289
- {
290
- Self::new(
291
- Direction::Horizontal,
292
- constraints.into_iter().map(Into::into),
293
- )
294
- }
295
-
296
- /// Initialize an empty cache with a custom size. The cache is keyed on the layout and area, so
297
- /// that subsequent calls with the same parameters are faster. The cache is a `LruCache`, and
298
- /// grows until `cache_size` is reached.
299
- ///
300
- /// By default, the cache size is [`Self::DEFAULT_CACHE_SIZE`].
301
- #[cfg(feature = "layout-cache")]
302
- pub fn init_cache(cache_size: NonZeroUsize) {
303
- LAYOUT_CACHE.with_borrow_mut(|cache| cache.resize(cache_size));
304
- }
305
-
306
- /// Set the direction of the layout.
307
- ///
308
- /// # Examples
309
- ///
310
- /// ```rust
311
- /// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
312
- ///
313
- /// let layout = Layout::default()
314
- /// .direction(Direction::Horizontal)
315
- /// .constraints([Constraint::Length(5), Constraint::Fill(1)])
316
- /// .split(Rect::new(0, 0, 10, 10));
317
- /// assert_eq!(layout[..], [Rect::new(0, 0, 5, 10), Rect::new(5, 0, 5, 10)]);
318
- ///
319
- /// let layout = Layout::default()
320
- /// .direction(Direction::Vertical)
321
- /// .constraints([Constraint::Length(5), Constraint::Fill(1)])
322
- /// .split(Rect::new(0, 0, 10, 10));
323
- /// assert_eq!(layout[..], [Rect::new(0, 0, 10, 5), Rect::new(0, 5, 10, 5)]);
324
- /// ```
325
- #[must_use = "method moves the value of self and returns the modified value"]
326
- pub const fn direction(mut self, direction: Direction) -> Self {
327
- self.direction = direction;
328
- self
329
- }
330
-
331
- /// Sets the constraints of the layout.
332
- ///
333
- /// The `constraints` parameter accepts any type that implements `IntoIterator<Item =
334
- /// Into<Constraint>>`. This includes arrays, slices, vectors, iterators. `Into<Constraint>` is
335
- /// implemented on u16, so you can pass an array or vec of u16 to this function to create a
336
- /// layout with fixed size chunks.
337
- ///
338
- /// Note that the constraints are applied to the whole area that is to be split, so using
339
- /// percentages and ratios with the other constraints may not have the desired effect of
340
- /// splitting the area up. (e.g. splitting 100 into [min 20, 50%, 50%], may not result in [20,
341
- /// 40, 40] but rather an indeterminate result between [20, 50, 30] and [20, 30, 50]).
342
- ///
343
- /// # Examples
344
- ///
345
- /// ```rust
346
- /// use ratatui_core::layout::{Constraint, Layout, Rect};
347
- ///
348
- /// let layout = Layout::default()
349
- /// .constraints([
350
- /// Constraint::Percentage(20),
351
- /// Constraint::Ratio(1, 5),
352
- /// Constraint::Length(2),
353
- /// Constraint::Min(2),
354
- /// Constraint::Max(2),
355
- /// ])
356
- /// .split(Rect::new(0, 0, 10, 10));
357
- /// assert_eq!(
358
- /// layout[..],
359
- /// [
360
- /// Rect::new(0, 0, 10, 2),
361
- /// Rect::new(0, 2, 10, 2),
362
- /// Rect::new(0, 4, 10, 2),
363
- /// Rect::new(0, 6, 10, 2),
364
- /// Rect::new(0, 8, 10, 2),
365
- /// ]
366
- /// );
367
- ///
368
- /// Layout::default().constraints([Constraint::Fill(1)]);
369
- /// Layout::default().constraints(&[Constraint::Fill(1)]);
370
- /// Layout::default().constraints(vec![Constraint::Fill(1)]);
371
- /// Layout::default().constraints([Constraint::Fill(1)].iter().filter(|_| true));
372
- /// Layout::default().constraints([1, 2, 3].iter().map(|&c| Constraint::Length(c)));
373
- /// Layout::default().constraints([1, 2, 3]);
374
- /// Layout::default().constraints(vec![1, 2, 3]);
375
- /// ```
376
- #[must_use = "method moves the value of self and returns the modified value"]
377
- pub fn constraints<I>(mut self, constraints: I) -> Self
378
- where
379
- I: IntoIterator,
380
- I::Item: Into<Constraint>,
381
- {
382
- self.constraints = constraints.into_iter().map(Into::into).collect();
383
- self
384
- }
385
-
386
- /// Set the margin of the layout.
387
- ///
388
- /// # Examples
389
- ///
390
- /// ```rust
391
- /// use ratatui_core::layout::{Constraint, Layout, Rect};
392
- ///
393
- /// let layout = Layout::default()
394
- /// .constraints([Constraint::Fill(1)])
395
- /// .margin(2)
396
- /// .split(Rect::new(0, 0, 10, 10));
397
- /// assert_eq!(layout[..], [Rect::new(2, 2, 6, 6)]);
398
- /// ```
399
- #[must_use = "method moves the value of self and returns the modified value"]
400
- pub const fn margin(mut self, margin: u16) -> Self {
401
- self.margin = Margin {
402
- horizontal: margin,
403
- vertical: margin,
404
- };
405
- self
406
- }
407
-
408
- /// Set the horizontal margin of the layout.
409
- ///
410
- /// # Examples
411
- ///
412
- /// ```rust
413
- /// use ratatui_core::layout::{Constraint, Layout, Rect};
414
- ///
415
- /// let layout = Layout::default()
416
- /// .constraints([Constraint::Fill(1)])
417
- /// .horizontal_margin(2)
418
- /// .split(Rect::new(0, 0, 10, 10));
419
- /// assert_eq!(layout[..], [Rect::new(2, 0, 6, 10)]);
420
- /// ```
421
- #[must_use = "method moves the value of self and returns the modified value"]
422
- pub const fn horizontal_margin(mut self, horizontal: u16) -> Self {
423
- self.margin.horizontal = horizontal;
424
- self
425
- }
426
-
427
- /// Set the vertical margin of the layout.
428
- ///
429
- /// # Examples
430
- ///
431
- /// ```rust
432
- /// use ratatui_core::layout::{Constraint, Layout, Rect};
433
- ///
434
- /// let layout = Layout::default()
435
- /// .constraints([Constraint::Fill(1)])
436
- /// .vertical_margin(2)
437
- /// .split(Rect::new(0, 0, 10, 10));
438
- /// assert_eq!(layout[..], [Rect::new(0, 2, 10, 6)]);
439
- /// ```
440
- #[must_use = "method moves the value of self and returns the modified value"]
441
- pub const fn vertical_margin(mut self, vertical: u16) -> Self {
442
- self.margin.vertical = vertical;
443
- self
444
- }
445
-
446
- /// The `flex` method allows you to specify the flex behavior of the layout.
447
- ///
448
- /// # Arguments
449
- ///
450
- /// * `flex`: A [`Flex`] enum value that represents the flex behavior of the layout. It can be
451
- /// one of the following:
452
- /// - [`Flex::Legacy`]: The last item is stretched to fill the excess space.
453
- /// - [`Flex::Start`]: The items are aligned to the start of the layout.
454
- /// - [`Flex::Center`]: The items are aligned to the center of the layout.
455
- /// - [`Flex::End`]: The items are aligned to the end of the layout.
456
- /// - [`Flex::SpaceBetween`]: The items are evenly distributed with equal space between them.
457
- /// - [`Flex::SpaceAround`]: The items are evenly distributed with equal space around them,
458
- /// except the first and last items, which have half the space on their sides.
459
- /// - [`Flex::SpaceEvenly`]: The items are evenly distributed with equal space around them.
460
- ///
461
- /// # Examples
462
- ///
463
- /// In this example, the items in the layout will be aligned to the start.
464
- ///
465
- /// ```rust
466
- /// use ratatui_core::layout::Constraint::*;
467
- /// use ratatui_core::layout::{Flex, Layout};
468
- ///
469
- /// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).flex(Flex::Start);
470
- /// ```
471
- ///
472
- /// In this example, the items in the layout will be stretched equally to fill the available
473
- /// space.
474
- ///
475
- /// ```rust
476
- /// use ratatui_core::layout::Constraint::*;
477
- /// use ratatui_core::layout::{Flex, Layout};
478
- ///
479
- /// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).flex(Flex::Legacy);
480
- /// ```
481
- #[must_use = "method moves the value of self and returns the modified value"]
482
- pub const fn flex(mut self, flex: Flex) -> Self {
483
- self.flex = flex;
484
- self
485
- }
486
-
487
- /// Sets the spacing between items in the layout.
488
- ///
489
- /// The `spacing` method sets the spacing between items in the layout. The spacing is applied
490
- /// evenly between all segments. The spacing value represents the number of cells between each
491
- /// item.
492
- ///
493
- /// Spacing can be positive integers, representing gaps between segments; or negative integers
494
- /// representing overlaps. Additionally, one of the variants of the [`Spacing`] enum can be
495
- /// passed to this function. See the documentation of the [`Spacing`] enum for more information.
496
- ///
497
- /// Note that if the layout has only one segment, the spacing will not be applied.
498
- /// Also, spacing will not be applied for [`Flex::SpaceAround`], [`Flex::SpaceEvenly`] and
499
- /// [`Flex::SpaceBetween`]
500
- ///
501
- /// # Examples
502
- ///
503
- /// In this example, the spacing between each item in the layout is set to 2 cells.
504
- ///
505
- /// ```rust
506
- /// use ratatui_core::layout::Constraint::*;
507
- /// use ratatui_core::layout::Layout;
508
- ///
509
- /// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).spacing(2);
510
- /// ```
511
- ///
512
- /// In this example, the spacing between each item in the layout is set to -1 cells, i.e. the
513
- /// three segments will have an overlapping border.
514
- ///
515
- /// ```rust
516
- /// use ratatui_core::layout::Constraint::*;
517
- /// use ratatui_core::layout::Layout;
518
- /// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).spacing(-1);
519
- /// ```
520
- #[must_use = "method moves the value of self and returns the modified value"]
521
- pub fn spacing<T>(mut self, spacing: T) -> Self
522
- where
523
- T: Into<Spacing>,
524
- {
525
- self.spacing = spacing.into();
526
- self
527
- }
528
-
529
- /// Split the rect into a number of sub-rects according to the given [`Layout`].
530
- ///
531
- /// An ergonomic wrapper around [`Layout::split`] that returns an array of `Rect`s instead of
532
- /// `Rc<[Rect]>`.
533
- ///
534
- /// This method requires the number of constraints to be known at compile time. If you don't
535
- /// know the number of constraints at compile time, use [`Layout::split`] instead.
536
- ///
537
- /// # Panics
538
- ///
539
- /// Panics if the number of constraints is not equal to the length of the returned array.
540
- ///
541
- /// # Examples
542
- ///
543
- /// ```rust
544
- /// use ratatui_core::layout::{Constraint, Layout, Rect};
545
- ///
546
- /// let area = Rect::new(0, 0, 10, 10);
547
- /// let layout = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]);
548
- /// let [top, main] = layout.areas(area);
549
- ///
550
- /// // or explicitly specify the number of constraints:
551
- /// let areas = layout.areas::<2>(area);
552
- /// ```
553
- pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
554
- let areas = self.split(area);
555
- areas.as_ref().try_into().unwrap_or_else(|_| {
556
- panic!(
557
- "invalid number of rects: expected {N}, found {}",
558
- areas.len()
559
- )
560
- })
561
- }
562
-
563
- /// Split the rect into a number of sub-rects according to the given [`Layout`].
564
- ///
565
- /// An ergonomic wrapper around [`Layout::split`] that returns an array of `Rect`s instead of
566
- /// `Rc<[Rect]>`.
567
- ///
568
- /// This method requires the number of constraints to be known at compile time. If you don't
569
- /// know the number of constraints at compile time, use [`Layout::split`] instead.
570
- ///
571
- /// # Errors
572
- ///
573
- /// Returns an error if the number of constraints is not equal to the length of the returned
574
- /// array.
575
- ///
576
- /// # Examples
577
- ///
578
- /// ```rust
579
- /// use ratatui_core::layout::{Constraint, Layout, Rect};
580
- ///
581
- /// let area = Rect::new(0, 0, 10, 10);
582
- /// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
583
- /// let [top, main] = layout.try_areas(area)?;
584
- ///
585
- /// // or explicitly specify the number of constraints:
586
- /// let areas = layout.try_areas::<2>(area)?;
587
- /// # Ok::<(), core::array::TryFromSliceError>(())
588
- /// ```
589
- pub fn try_areas<const N: usize>(&self, area: Rect) -> Result<[Rect; N], TryFromSliceError> {
590
- self.split(area).as_ref().try_into()
591
- }
592
-
593
- /// Split the rect into a number of sub-rects according to the given [`Layout`] and return just
594
- /// the spacers between the areas.
595
- ///
596
- /// This method requires the number of constraints to be known at compile time. If you don't
597
- /// know the number of constraints at compile time, use [`Layout::split_with_spacers`] instead.
598
- ///
599
- /// This method is similar to [`Layout::areas`], and can be called with the same parameters, but
600
- /// it returns just the spacers between the areas. The result of calling the `areas` method is
601
- /// cached, so this will generally not re-run the solver, but will just return the cached
602
- /// result.
603
- ///
604
- /// # Panics
605
- ///
606
- /// Panics if the number of constraints + 1 is not equal to the length of the returned array.
607
- ///
608
- /// # Examples
609
- ///
610
- /// ```rust
611
- /// use ratatui_core::layout::{Constraint, Layout, Rect};
612
- ///
613
- /// let area = Rect::new(0, 0, 10, 10);
614
- /// let layout = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]);
615
- /// let [top, main] = layout.areas(area);
616
- /// let [before, inbetween, after] = layout.spacers(area);
617
- ///
618
- /// // or explicitly specify the number of constraints:
619
- /// let spacers = layout.spacers::<3>(area);
620
- /// ```
621
- pub fn spacers<const N: usize>(&self, area: Rect) -> [Rect; N] {
622
- let (_, spacers) = self.split_with_spacers(area);
623
- spacers
624
- .as_ref()
625
- .try_into()
626
- .expect("invalid number of rects")
627
- }
628
-
629
- /// Wrapper function around the [`kasuari`] solver to be able to split a given area into
630
- /// smaller ones based on the preferred widths or heights and the direction.
631
- ///
632
- /// Note that the constraints are applied to the whole area that is to be split, so using
633
- /// percentages and ratios with the other constraints may not have the desired effect of
634
- /// splitting the area up. (e.g. splitting 100 into [min 20, 50%, 50%], may not result in [20,
635
- /// 40, 40] but rather an indeterminate result between [20, 50, 30] and [20, 30, 50]).
636
- ///
637
- /// This method stores the result of the computation in a thread-local cache keyed on the layout
638
- /// and area, so that subsequent calls with the same parameters are faster. The cache is a
639
- /// `LruCache`, and grows until [`Self::DEFAULT_CACHE_SIZE`] is reached by default. If the cache
640
- /// is initialized with [`Layout::init_cache()`], it grows until the initialized cache size.
641
- ///
642
- /// There is a helper method that can be used to split the whole area into smaller ones based on
643
- /// the layout: [`Layout::areas()`]. That method is a shortcut for calling this method. It
644
- /// allows you to destructure the result directly into variables, which is useful when you know
645
- /// at compile time the number of areas that will be created.
646
- ///
647
- /// # Examples
648
- ///
649
- /// ```
650
- /// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
651
- /// let layout = Layout::default()
652
- /// .direction(Direction::Vertical)
653
- /// .constraints([Constraint::Length(5), Constraint::Fill(1)])
654
- /// .split(Rect::new(2, 2, 10, 10));
655
- /// assert_eq!(layout[..], [Rect::new(2, 2, 10, 5), Rect::new(2, 7, 10, 5)]);
656
- ///
657
- /// let layout = Layout::default()
658
- /// .direction(Direction::Horizontal)
659
- /// .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
660
- /// .split(Rect::new(0, 0, 9, 2));
661
- /// assert_eq!(layout[..], [Rect::new(0, 0, 3, 2), Rect::new(3, 0, 6, 2)]);
662
- /// ```
663
- pub fn split(&self, area: Rect) -> Rects {
664
- self.split_with_spacers(area).0
665
- }
666
-
667
- /// Wrapper function around the [`kasuari`] solver that splits the given area into smaller ones
668
- /// based on the preferred widths or heights and the direction, with the ability to include
669
- /// spacers between the areas.
670
- ///
671
- /// This method is similar to `split`, but it returns two sets of rectangles: one for the areas
672
- /// and one for the spacers.
673
- ///
674
- /// This method stores the result of the computation in a thread-local cache keyed on the layout
675
- /// and area, so that subsequent calls with the same parameters are faster. The cache is a
676
- /// `LruCache`, and grows until [`Self::DEFAULT_CACHE_SIZE`] is reached by default. If the cache
677
- /// is initialized with [`Layout::init_cache()`], it grows until the initialized cache size.
678
- ///
679
- /// # Examples
680
- ///
681
- /// ```
682
- /// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
683
- ///
684
- /// let (areas, spacers) = Layout::default()
685
- /// .direction(Direction::Vertical)
686
- /// .constraints([Constraint::Length(5), Constraint::Fill(1)])
687
- /// .split_with_spacers(Rect::new(2, 2, 10, 10));
688
- /// assert_eq!(areas[..], [Rect::new(2, 2, 10, 5), Rect::new(2, 7, 10, 5)]);
689
- /// assert_eq!(
690
- /// spacers[..],
691
- /// [
692
- /// Rect::new(2, 2, 10, 0),
693
- /// Rect::new(2, 7, 10, 0),
694
- /// Rect::new(2, 12, 10, 0)
695
- /// ]
696
- /// );
697
- ///
698
- /// let (areas, spacers) = Layout::default()
699
- /// .direction(Direction::Horizontal)
700
- /// .spacing(1)
701
- /// .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
702
- /// .split_with_spacers(Rect::new(0, 0, 10, 2));
703
- /// assert_eq!(areas[..], [Rect::new(0, 0, 3, 2), Rect::new(4, 0, 6, 2)]);
704
- /// assert_eq!(
705
- /// spacers[..],
706
- /// [
707
- /// Rect::new(0, 0, 0, 2),
708
- /// Rect::new(3, 0, 1, 2),
709
- /// Rect::new(10, 0, 0, 2)
710
- /// ]
711
- /// );
712
- /// ```
713
- pub fn split_with_spacers(&self, area: Rect) -> (Segments, Spacers) {
714
- let split = || self.try_split(area).expect("failed to split");
715
-
716
- #[cfg(feature = "layout-cache")]
717
- {
718
- LAYOUT_CACHE.with_borrow_mut(|cache| {
719
- let key = (area, self.clone());
720
- cache.get_or_insert(key, split).clone()
721
- })
722
- }
723
-
724
- #[cfg(not(feature = "layout-cache"))]
725
- split()
726
- }
727
-
728
- fn try_split(&self, area: Rect) -> Result<(Segments, Spacers), AddConstraintError> {
729
- // To take advantage of all of [`kasuari`] features, we would want to store the `Solver` in
730
- // one of the fields of the Layout struct. And we would want to set it up such that we could
731
- // add or remove constraints as and when needed.
732
- // The advantage of doing it as described above is that it would allow users to
733
- // incrementally add and remove constraints efficiently.
734
- // Solves will just one constraint different would not need to resolve the entire layout.
735
- //
736
- // The disadvantage of this approach is that it requires tracking which constraints were
737
- // added, and which variables they correspond to.
738
- // This will also require introducing and maintaining the API for users to do so.
739
- //
740
- // Currently we don't support that use case and do not intend to support it in the future,
741
- // and instead we require that the user re-solve the layout every time they call `split`.
742
- // To minimize the time it takes to solve the same problem over and over again, we
743
- // cache the `Layout` struct along with the results.
744
- //
745
- // `try_split` is the inner method in `split` that is called only when the LRU cache doesn't
746
- // match the key. So inside `try_split`, we create a new instance of the solver.
747
- //
748
- // This is equivalent to storing the solver in `Layout` and calling `solver.reset()` here.
749
- let mut solver = Solver::new();
750
-
751
- let inner_area = area.inner(self.margin);
752
- let (area_start, area_end) = match self.direction {
753
- Direction::Horizontal => (
754
- f64::from(inner_area.x) * FLOAT_PRECISION_MULTIPLIER,
755
- f64::from(inner_area.right()) * FLOAT_PRECISION_MULTIPLIER,
756
- ),
757
- Direction::Vertical => (
758
- f64::from(inner_area.y) * FLOAT_PRECISION_MULTIPLIER,
759
- f64::from(inner_area.bottom()) * FLOAT_PRECISION_MULTIPLIER,
760
- ),
761
- };
762
-
763
- // ```plain
764
- // <───────────────────────────────────area_size──────────────────────────────────>
765
- // ┌─area_start area_end─┐
766
- // V V
767
- // ┌────┬───────────────────┬────┬─────variables─────┬────┬───────────────────┬────┐
768
- // │ │ │ │ │ │ │ │
769
- // V V V V V V V V
770
- // ┌ ┐┌──────────────────┐┌ ┐┌──────────────────┐┌ ┐┌──────────────────┐┌ ┐
771
- // │ Max(20) │ │ Max(20) │ │ Max(20) │
772
- // └ ┘└──────────────────┘└ ┘└──────────────────┘└ ┘└──────────────────┘└ ┘
773
- // ^ ^ ^ ^ ^ ^ ^ ^
774
- // │ │ │ │ │ │ │ │
775
- // └─┬──┶━━━━━━━━━┳━━━━━━━━━┵─┬──┶━━━━━━━━━┳━━━━━━━━━┵─┬──┶━━━━━━━━━┳━━━━━━━━━┵─┬──┘
776
- // │ ┃ │ ┃ │ ┃ │
777
- // └────────────╂───────────┴────────────╂───────────┴────────────╂──Spacers──┘
778
- // ┃ ┃ ┃
779
- // ┗━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━Segments━━━━━━━━┛
780
- // ```
781
-
782
- let variable_count = self.constraints.len() * 2 + 2;
783
- let variables = iter::repeat_with(Variable::new)
784
- .take(variable_count)
785
- .collect_vec();
786
- let spacers = variables
787
- .iter()
788
- .tuples()
789
- .map(|(a, b)| Element::from((*a, *b)))
790
- .collect_vec();
791
- let segments = variables
792
- .iter()
793
- .skip(1)
794
- .tuples()
795
- .map(|(a, b)| Element::from((*a, *b)))
796
- .collect_vec();
797
-
798
- let flex = self.flex;
799
-
800
- let spacing = match self.spacing {
801
- Spacing::Space(x) => x as i16,
802
- Spacing::Overlap(x) => -(x as i16),
803
- };
804
-
805
- let constraints = &self.constraints;
806
-
807
- let area_size = Element::from((*variables.first().unwrap(), *variables.last().unwrap()));
808
- configure_area(&mut solver, area_size, area_start, area_end)?;
809
- configure_variable_in_area_constraints(&mut solver, &variables, area_size)?;
810
- configure_variable_constraints(&mut solver, &variables)?;
811
- configure_flex_constraints(&mut solver, area_size, &spacers, flex, spacing)?;
812
- configure_constraints(&mut solver, area_size, &segments, constraints, flex)?;
813
- configure_fill_constraints(&mut solver, &segments, constraints, flex)?;
814
-
815
- if !flex.is_legacy() {
816
- for (left, right) in segments.iter().tuple_windows() {
817
- solver.add_constraint(left.has_size(right, ALL_SEGMENT_GROW))?;
818
- }
819
- }
820
-
821
- // `solver.fetch_changes()` can only be called once per solve
822
- let changes: HashMap<Variable, f64> = solver.fetch_changes().iter().copied().collect();
823
- // debug_elements(&segments, &changes);
824
- // debug_elements(&spacers, &changes);
825
-
826
- let segment_rects = changes_to_rects(&changes, &segments, inner_area, self.direction);
827
- let spacer_rects = changes_to_rects(&changes, &spacers, inner_area, self.direction);
828
-
829
- Ok((segment_rects, spacer_rects))
830
- }
831
- }
832
-
833
- fn configure_area(
834
- solver: &mut Solver,
835
- area: Element,
836
- area_start: f64,
837
- area_end: f64,
838
- ) -> Result<(), AddConstraintError> {
839
- solver.add_constraint(area.start | EQ(Strength::REQUIRED) | area_start)?;
840
- solver.add_constraint(area.end | EQ(Strength::REQUIRED) | area_end)?;
841
- Ok(())
842
- }
843
-
844
- fn configure_variable_in_area_constraints(
845
- solver: &mut Solver,
846
- variables: &[Variable],
847
- area: Element,
848
- ) -> Result<(), AddConstraintError> {
849
- // all variables are in the range [area.start, area.end]
850
- for &variable in variables {
851
- solver.add_constraint(variable | GE(Strength::REQUIRED) | area.start)?;
852
- solver.add_constraint(variable | LE(Strength::REQUIRED) | area.end)?;
853
- }
854
-
855
- Ok(())
856
- }
857
-
858
- fn configure_variable_constraints(
859
- solver: &mut Solver,
860
- variables: &[Variable],
861
- ) -> Result<(), AddConstraintError> {
862
- // ┌────┬───────────────────┬────┬─────variables─────┬────┬───────────────────┬────┐
863
- // │ │ │ │ │ │ │ │
864
- // v v v v v v v v
865
- // ┌ ┐┌──────────────────┐┌ ┐┌──────────────────┐┌ ┐┌──────────────────┐┌ ┐
866
- // │ Max(20) │ │ Max(20) │ │ Max(20) │
867
- // └ ┘└──────────────────┘└ ┘└──────────────────┘└ ┘└──────────────────┘└ ┘
868
- // ^ ^ ^ ^ ^ ^ ^ ^
869
- // └v0 └v1 └v2 └v3 └v4 └v5 └v6 └v7
870
-
871
- for (&left, &right) in variables.iter().skip(1).tuples() {
872
- solver.add_constraint(left | LE(Strength::REQUIRED) | right)?;
873
- }
874
- Ok(())
875
- }
876
-
877
- fn configure_constraints(
878
- solver: &mut Solver,
879
- area: Element,
880
- segments: &[Element],
881
- constraints: &[Constraint],
882
- flex: Flex,
883
- ) -> Result<(), AddConstraintError> {
884
- for (&constraint, &segment) in constraints.iter().zip(segments.iter()) {
885
- match constraint {
886
- Constraint::Max(max) => {
887
- solver.add_constraint(segment.has_max_size(max, MAX_SIZE_LE))?;
888
- solver.add_constraint(segment.has_int_size(max, MAX_SIZE_EQ))?;
889
- }
890
- Constraint::Min(min) => {
891
- solver.add_constraint(segment.has_min_size(min as i16, MIN_SIZE_GE))?;
892
- if flex.is_legacy() {
893
- solver.add_constraint(segment.has_int_size(min, MIN_SIZE_EQ))?;
894
- } else {
895
- solver.add_constraint(segment.has_size(area, FILL_GROW))?;
896
- }
897
- }
898
- Constraint::Length(length) => {
899
- solver.add_constraint(segment.has_int_size(length, LENGTH_SIZE_EQ))?;
900
- }
901
- Constraint::Percentage(p) => {
902
- let size = area.size() * f64::from(p) / 100.00;
903
- solver.add_constraint(segment.has_size(size, PERCENTAGE_SIZE_EQ))?;
904
- }
905
- Constraint::Ratio(num, den) => {
906
- // avoid division by zero by using 1 when denominator is 0
907
- let size = area.size() * f64::from(num) / f64::from(den.max(1));
908
- solver.add_constraint(segment.has_size(size, RATIO_SIZE_EQ))?;
909
- }
910
- Constraint::Fill(_) => {
911
- // given no other constraints, this segment will grow as much as possible.
912
- solver.add_constraint(segment.has_size(area, FILL_GROW))?;
913
- }
914
- }
915
- }
916
- Ok(())
917
- }
918
-
919
- fn configure_flex_constraints(
920
- solver: &mut Solver,
921
- area: Element,
922
- spacers: &[Element],
923
- flex: Flex,
924
- spacing: i16,
925
- ) -> Result<(), AddConstraintError> {
926
- let spacers_except_first_and_last = spacers.get(1..spacers.len() - 1).unwrap_or(&[]);
927
- let spacing_f64 = f64::from(spacing) * FLOAT_PRECISION_MULTIPLIER;
928
- match flex {
929
- Flex::Legacy => {
930
- for spacer in spacers_except_first_and_last {
931
- solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
932
- }
933
- if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
934
- solver.add_constraint(first.is_empty())?;
935
- solver.add_constraint(last.is_empty())?;
936
- }
937
- }
938
-
939
- // All spacers excluding first and last are the same size and will grow to fill
940
- // any remaining space after the constraints are satisfied.
941
- // All spacers excluding first and last are also twice the size of the first and last
942
- // spacers
943
- Flex::SpaceAround => {
944
- if spacers.len() <= 2 {
945
- // If there are two or less spacers, fallback to Flex::SpaceEvenly
946
- for (left, right) in spacers.iter().tuple_combinations() {
947
- solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
948
- }
949
- for spacer in spacers {
950
- solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
951
- solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
952
- }
953
- } else {
954
- // Separate the first and last spacer from the middle ones
955
- let (first, rest) = spacers.split_first().unwrap();
956
- let (last, middle) = rest.split_last().unwrap();
957
-
958
- // All middle spacers should be equal in size
959
- for (left, right) in middle.iter().tuple_combinations() {
960
- solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
961
- }
962
-
963
- // First and last spacers should be half the size of any middle spacer
964
- if let Some(first_middle) = middle.first() {
965
- solver.add_constraint(first_middle.has_double_size(first, SPACER_SIZE_EQ))?;
966
- solver.add_constraint(first_middle.has_double_size(last, SPACER_SIZE_EQ))?;
967
- }
968
-
969
- // Apply minimum size and growth constraints
970
- for spacer in spacers {
971
- solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
972
- solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
973
- }
974
- }
975
- }
976
-
977
- // All spacers are the same size and will grow to fill any remaining space after the
978
- // constraints are satisfied
979
- Flex::SpaceEvenly => {
980
- for (left, right) in spacers.iter().tuple_combinations() {
981
- solver.add_constraint(left.has_size(right, SPACER_SIZE_EQ))?;
982
- }
983
- for spacer in spacers {
984
- solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
985
- solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
986
- }
987
- }
988
-
989
- // All spacers excluding first and last are the same size and will grow to fill
990
- // any remaining space after the constraints are satisfied.
991
- // The first and last spacers are zero size.
992
- Flex::SpaceBetween => {
993
- for (left, right) in spacers_except_first_and_last.iter().tuple_combinations() {
994
- solver.add_constraint(left.has_size(right.size(), SPACER_SIZE_EQ))?;
995
- }
996
- for spacer in spacers_except_first_and_last {
997
- solver.add_constraint(spacer.has_min_size(spacing, SPACER_SIZE_EQ))?;
998
- solver.add_constraint(spacer.has_size(area, SPACE_GROW))?;
999
- }
1000
- if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1001
- solver.add_constraint(first.is_empty())?;
1002
- solver.add_constraint(last.is_empty())?;
1003
- }
1004
- }
1005
-
1006
- Flex::Start => {
1007
- for spacer in spacers_except_first_and_last {
1008
- solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1009
- }
1010
- if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1011
- solver.add_constraint(first.is_empty())?;
1012
- solver.add_constraint(last.has_size(area, GROW))?;
1013
- }
1014
- }
1015
- Flex::Center => {
1016
- for spacer in spacers_except_first_and_last {
1017
- solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1018
- }
1019
- if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1020
- solver.add_constraint(first.has_size(area, GROW))?;
1021
- solver.add_constraint(last.has_size(area, GROW))?;
1022
- solver.add_constraint(first.has_size(last, SPACER_SIZE_EQ))?;
1023
- }
1024
- }
1025
- Flex::End => {
1026
- for spacer in spacers_except_first_and_last {
1027
- solver.add_constraint(spacer.has_size(spacing_f64, SPACER_SIZE_EQ))?;
1028
- }
1029
- if let (Some(first), Some(last)) = (spacers.first(), spacers.last()) {
1030
- solver.add_constraint(last.is_empty())?;
1031
- solver.add_constraint(first.has_size(area, GROW))?;
1032
- }
1033
- }
1034
- }
1035
- Ok(())
1036
- }
1037
-
1038
- /// Make every `Fill` constraint proportionally equal to each other
1039
- /// This will make it fill up empty spaces equally
1040
- ///
1041
- /// [Fill(1), Fill(1)]
1042
- /// ┌──────┐┌──────┐
1043
- /// │abcdef││abcdef│
1044
- /// └──────┘└──────┘
1045
- ///
1046
- /// [Fill(1), Fill(2)]
1047
- /// ┌──────┐┌────────────┐
1048
- /// │abcdef││abcdefabcdef│
1049
- /// └──────┘└────────────┘
1050
- ///
1051
- /// `size == base_element * scaling_factor`
1052
- fn configure_fill_constraints(
1053
- solver: &mut Solver,
1054
- segments: &[Element],
1055
- constraints: &[Constraint],
1056
- flex: Flex,
1057
- ) -> Result<(), AddConstraintError> {
1058
- for ((&left_constraint, &left_segment), (&right_constraint, &right_segment)) in constraints
1059
- .iter()
1060
- .zip(segments.iter())
1061
- .filter(|(c, _)| c.is_fill() || (!flex.is_legacy() && c.is_min()))
1062
- .tuple_combinations()
1063
- {
1064
- let left_scaling_factor = match left_constraint {
1065
- Constraint::Fill(scale) => f64::from(scale).max(1e-6),
1066
- Constraint::Min(_) => 1.0,
1067
- _ => unreachable!(),
1068
- };
1069
- let right_scaling_factor = match right_constraint {
1070
- Constraint::Fill(scale) => f64::from(scale).max(1e-6),
1071
- Constraint::Min(_) => 1.0,
1072
- _ => unreachable!(),
1073
- };
1074
- solver.add_constraint(
1075
- (right_scaling_factor * left_segment.size())
1076
- | EQ(GROW)
1077
- | (left_scaling_factor * right_segment.size()),
1078
- )?;
1079
- }
1080
- Ok(())
1081
- }
1082
-
1083
- // Used instead of `f64::round` directly, to provide fallback for `no_std`.
1084
- #[cfg(feature = "std")]
1085
- #[inline]
1086
- fn round(value: f64) -> f64 {
1087
- value.round()
1088
- }
1089
-
1090
- // A rounding fallback for `no_std` in pure rust.
1091
- #[cfg(not(feature = "std"))]
1092
- #[inline]
1093
- fn round(value: f64) -> f64 {
1094
- (value + 0.5f64.copysign(value)) as i64 as f64
1095
- }
1096
-
1097
- fn changes_to_rects(
1098
- changes: &HashMap<Variable, f64>,
1099
- elements: &[Element],
1100
- area: Rect,
1101
- direction: Direction,
1102
- ) -> Rects {
1103
- // convert to Rects
1104
- elements
1105
- .iter()
1106
- .map(|element| {
1107
- let start = changes.get(&element.start).unwrap_or(&0.0);
1108
- let end = changes.get(&element.end).unwrap_or(&0.0);
1109
- let start = round(round(*start) / FLOAT_PRECISION_MULTIPLIER) as u16;
1110
- let end = round(round(*end) / FLOAT_PRECISION_MULTIPLIER) as u16;
1111
- let size = end.saturating_sub(start);
1112
- match direction {
1113
- Direction::Horizontal => Rect {
1114
- x: start,
1115
- y: area.y,
1116
- width: size,
1117
- height: area.height,
1118
- },
1119
- Direction::Vertical => Rect {
1120
- x: area.x,
1121
- y: start,
1122
- width: area.width,
1123
- height: size,
1124
- },
1125
- }
1126
- })
1127
- .collect::<Rects>()
1128
- }
1129
-
1130
- /// please leave this here as it's useful for debugging unit tests when we make any changes to
1131
- /// layout code - we should replace this with tracing in the future.
1132
- #[expect(dead_code)]
1133
- #[cfg(feature = "std")]
1134
- fn debug_elements(elements: &[Element], changes: &HashMap<Variable, f64>) {
1135
- let variables = alloc::format!(
1136
- "{:?}",
1137
- elements
1138
- .iter()
1139
- .map(|e| (
1140
- changes.get(&e.start).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1141
- changes.get(&e.end).unwrap_or(&0.0) / FLOAT_PRECISION_MULTIPLIER,
1142
- ))
1143
- .collect::<Vec<(f64, f64)>>()
1144
- );
1145
- std::dbg!(variables);
1146
- }
1147
-
1148
- /// A container used by the solver inside split
1149
- #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1150
- struct Element {
1151
- start: Variable,
1152
- end: Variable,
1153
- }
1154
-
1155
- impl From<(Variable, Variable)> for Element {
1156
- fn from((start, end): (Variable, Variable)) -> Self {
1157
- Self { start, end }
1158
- }
1159
- }
1160
-
1161
- impl Element {
1162
- #[expect(dead_code)]
1163
- fn new() -> Self {
1164
- Self {
1165
- start: Variable::new(),
1166
- end: Variable::new(),
1167
- }
1168
- }
1169
-
1170
- fn size(&self) -> Expression {
1171
- self.end - self.start
1172
- }
1173
-
1174
- fn has_max_size(&self, size: u16, strength: Strength) -> kasuari::Constraint {
1175
- self.size() | LE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1176
- }
1177
-
1178
- fn has_min_size(&self, size: i16, strength: Strength) -> kasuari::Constraint {
1179
- self.size() | GE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1180
- }
1181
-
1182
- fn has_int_size(&self, size: u16, strength: Strength) -> kasuari::Constraint {
1183
- self.size() | EQ(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER)
1184
- }
1185
-
1186
- fn has_size<E: Into<Expression>>(&self, size: E, strength: Strength) -> kasuari::Constraint {
1187
- self.size() | EQ(strength) | size.into()
1188
- }
1189
-
1190
- fn has_double_size<E: Into<Expression>>(
1191
- &self,
1192
- size: E,
1193
- strength: Strength,
1194
- ) -> kasuari::Constraint {
1195
- self.size() | EQ(strength) | (size.into() * 2.0)
1196
- }
1197
-
1198
- fn is_empty(&self) -> kasuari::Constraint {
1199
- self.size() | EQ(Strength::REQUIRED - Strength::WEAK) | 0.0
1200
- }
1201
- }
1202
-
1203
- /// allow the element to represent its own size in expressions
1204
- impl From<Element> for Expression {
1205
- fn from(element: Element) -> Self {
1206
- element.size()
1207
- }
1208
- }
1209
-
1210
- /// allow the element to represent its own size in expressions
1211
- impl From<&Element> for Expression {
1212
- fn from(element: &Element) -> Self {
1213
- element.size()
1214
- }
1215
- }
1216
-
1217
- mod strengths {
1218
- use kasuari::Strength;
1219
-
1220
- /// The strength to apply to Spacers to ensure that their sizes are equal.
1221
- ///
1222
- /// ┌ ┐┌───┐┌ ┐┌───┐┌ ┐
1223
- /// ==x │ │ ==x │ │ ==x
1224
- /// └ ┘└───┘└ ┘└───┘└ ┘
1225
- pub const SPACER_SIZE_EQ: Strength = Strength::REQUIRED.div_f64(10.0);
1226
-
1227
- /// The strength to apply to Min inequality constraints.
1228
- ///
1229
- /// ┌────────┐
1230
- /// │Min(>=x)│
1231
- /// └────────┘
1232
- pub const MIN_SIZE_GE: Strength = Strength::STRONG.mul_f64(100.0);
1233
-
1234
- /// The strength to apply to Max inequality constraints.
1235
- ///
1236
- /// ┌────────┐
1237
- /// │Max(<=x)│
1238
- /// └────────┘
1239
- pub const MAX_SIZE_LE: Strength = Strength::STRONG.mul_f64(100.0);
1240
-
1241
- /// The strength to apply to Length constraints.
1242
- ///
1243
- /// ┌───────────┐
1244
- /// │Length(==x)│
1245
- /// └───────────┘
1246
- pub const LENGTH_SIZE_EQ: Strength = Strength::STRONG.mul_f64(10.0);
1247
-
1248
- /// The strength to apply to Percentage constraints.
1249
- ///
1250
- /// ┌───────────────┐
1251
- /// │Percentage(==x)│
1252
- /// └───────────────┘
1253
- pub const PERCENTAGE_SIZE_EQ: Strength = Strength::STRONG;
1254
-
1255
- /// The strength to apply to Ratio constraints.
1256
- ///
1257
- /// ┌────────────┐
1258
- /// │Ratio(==x,y)│
1259
- /// └────────────┘
1260
- pub const RATIO_SIZE_EQ: Strength = Strength::STRONG.div_f64(10.0);
1261
-
1262
- /// The strength to apply to Min equality constraints.
1263
- ///
1264
- /// ┌────────┐
1265
- /// │Min(==x)│
1266
- /// └────────┘
1267
- pub const MIN_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0);
1268
-
1269
- /// The strength to apply to Max equality constraints.
1270
- ///
1271
- /// ┌────────┐
1272
- /// │Max(==x)│
1273
- /// └────────┘
1274
- pub const MAX_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0);
1275
-
1276
- /// The strength to apply to Fill growing constraints.
1277
- ///
1278
- /// ┌─────────────────────┐
1279
- /// │<= Fill(x) =>│
1280
- /// └─────────────────────┘
1281
- pub const FILL_GROW: Strength = Strength::MEDIUM;
1282
-
1283
- /// The strength to apply to growing constraints.
1284
- ///
1285
- /// ┌────────────┐
1286
- /// │<= Min(x) =>│
1287
- /// └────────────┘
1288
- pub const GROW: Strength = Strength::MEDIUM.div_f64(10.0);
1289
-
1290
- /// The strength to apply to Spacer growing constraints.
1291
- ///
1292
- /// ┌ ┐
1293
- /// <= x =>
1294
- /// └ ┘
1295
- pub const SPACE_GROW: Strength = Strength::WEAK.mul_f64(10.0);
1296
-
1297
- /// The strength to apply to growing the size of all segments equally.
1298
- ///
1299
- /// ┌───────┐
1300
- /// │<= x =>│
1301
- /// └───────┘
1302
- pub const ALL_SEGMENT_GROW: Strength = Strength::WEAK;
1303
- }
1304
-
1305
- #[cfg(test)]
1306
- mod tests {
1307
- use alloc::borrow::ToOwned;
1308
- use alloc::vec;
1309
- use alloc::vec::Vec;
1310
-
1311
- use super::*;
1312
-
1313
- #[test]
1314
- // The compiler will optimize out the comparisons, but this ensures that the constants are
1315
- // defined in the correct order of priority.
1316
- pub fn strength_is_valid() {
1317
- use strengths::*;
1318
- assert!(SPACER_SIZE_EQ > MAX_SIZE_LE);
1319
- assert!(MAX_SIZE_LE > MAX_SIZE_EQ);
1320
- assert!(MIN_SIZE_GE == MAX_SIZE_LE);
1321
- assert!(MAX_SIZE_LE > LENGTH_SIZE_EQ);
1322
- assert!(LENGTH_SIZE_EQ > PERCENTAGE_SIZE_EQ);
1323
- assert!(PERCENTAGE_SIZE_EQ > RATIO_SIZE_EQ);
1324
- assert!(RATIO_SIZE_EQ > MAX_SIZE_EQ);
1325
- assert!(MIN_SIZE_GE > FILL_GROW);
1326
- assert!(FILL_GROW > GROW);
1327
- assert!(GROW > SPACE_GROW);
1328
- assert!(SPACE_GROW > ALL_SEGMENT_GROW);
1329
- }
1330
-
1331
- #[test]
1332
- #[cfg(feature = "layout-cache")]
1333
- fn cache_size() {
1334
- LAYOUT_CACHE.with_borrow(|cache| {
1335
- assert_eq!(cache.cap().get(), Layout::DEFAULT_CACHE_SIZE);
1336
- });
1337
-
1338
- Layout::init_cache(NonZeroUsize::new(10).unwrap());
1339
- LAYOUT_CACHE.with_borrow(|cache| {
1340
- assert_eq!(cache.cap().get(), 10);
1341
- });
1342
- }
1343
-
1344
- #[test]
1345
- fn default() {
1346
- assert_eq!(
1347
- Layout::default(),
1348
- Layout {
1349
- direction: Direction::Vertical,
1350
- margin: Margin::new(0, 0),
1351
- constraints: vec![],
1352
- flex: Flex::default(),
1353
- spacing: Spacing::default(),
1354
- }
1355
- );
1356
- }
1357
-
1358
- #[test]
1359
- fn new() {
1360
- // array
1361
- let fixed_size_array = [Constraint::Min(0)];
1362
- let layout = Layout::new(Direction::Horizontal, fixed_size_array);
1363
- assert_eq!(layout.direction, Direction::Horizontal);
1364
- assert_eq!(layout.constraints, [Constraint::Min(0)]);
1365
-
1366
- // array_ref
1367
- #[expect(clippy::needless_borrows_for_generic_args)] // backwards compatibility test
1368
- let layout = Layout::new(Direction::Horizontal, &[Constraint::Min(0)]);
1369
- assert_eq!(layout.direction, Direction::Horizontal);
1370
- assert_eq!(layout.constraints, [Constraint::Min(0)]);
1371
-
1372
- // vec
1373
- let layout = Layout::new(Direction::Horizontal, vec![Constraint::Min(0)]);
1374
- assert_eq!(layout.direction, Direction::Horizontal);
1375
- assert_eq!(layout.constraints, [Constraint::Min(0)]);
1376
-
1377
- // vec_ref
1378
- #[expect(clippy::needless_borrows_for_generic_args)] // backwards compatibility test
1379
- let layout = Layout::new(Direction::Horizontal, &(vec![Constraint::Min(0)]));
1380
- assert_eq!(layout.direction, Direction::Horizontal);
1381
- assert_eq!(layout.constraints, [Constraint::Min(0)]);
1382
-
1383
- // iterator
1384
- let layout = Layout::new(Direction::Horizontal, iter::once(Constraint::Min(0)));
1385
- assert_eq!(layout.direction, Direction::Horizontal);
1386
- assert_eq!(layout.constraints, [Constraint::Min(0)]);
1387
- }
1388
-
1389
- #[test]
1390
- fn vertical() {
1391
- assert_eq!(
1392
- Layout::vertical([Constraint::Min(0)]),
1393
- Layout {
1394
- direction: Direction::Vertical,
1395
- margin: Margin::new(0, 0),
1396
- constraints: vec![Constraint::Min(0)],
1397
- flex: Flex::default(),
1398
- spacing: Spacing::default(),
1399
- }
1400
- );
1401
- }
1402
-
1403
- #[test]
1404
- fn horizontal() {
1405
- assert_eq!(
1406
- Layout::horizontal([Constraint::Min(0)]),
1407
- Layout {
1408
- direction: Direction::Horizontal,
1409
- margin: Margin::new(0, 0),
1410
- constraints: vec![Constraint::Min(0)],
1411
- flex: Flex::default(),
1412
- spacing: Spacing::default(),
1413
- }
1414
- );
1415
- }
1416
-
1417
- /// The purpose of this test is to ensure that layout can be constructed with any type that
1418
- /// implements `IntoIterator<Item = AsRef<Constraint>>`.
1419
- #[test]
1420
- fn constraints() {
1421
- const CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Max(10)];
1422
- let fixed_size_array = CONSTRAINTS;
1423
- assert_eq!(
1424
- Layout::default().constraints(fixed_size_array).constraints,
1425
- CONSTRAINTS,
1426
- "constraints should be settable with an array"
1427
- );
1428
-
1429
- let slice_of_fixed_size_array = &CONSTRAINTS;
1430
- assert_eq!(
1431
- Layout::default()
1432
- .constraints(slice_of_fixed_size_array)
1433
- .constraints,
1434
- CONSTRAINTS,
1435
- "constraints should be settable with a slice"
1436
- );
1437
-
1438
- let vec = CONSTRAINTS.to_vec();
1439
- let slice_of_vec = vec.as_slice();
1440
- assert_eq!(
1441
- Layout::default().constraints(slice_of_vec).constraints,
1442
- CONSTRAINTS,
1443
- "constraints should be settable with a slice"
1444
- );
1445
-
1446
- assert_eq!(
1447
- Layout::default().constraints(vec).constraints,
1448
- CONSTRAINTS,
1449
- "constraints should be settable with a Vec"
1450
- );
1451
-
1452
- let iter = CONSTRAINTS.iter();
1453
- assert_eq!(
1454
- Layout::default().constraints(iter).constraints,
1455
- CONSTRAINTS,
1456
- "constraints should be settable with an iter"
1457
- );
1458
-
1459
- let iterator = CONSTRAINTS.iter().map(ToOwned::to_owned);
1460
- assert_eq!(
1461
- Layout::default().constraints(iterator).constraints,
1462
- CONSTRAINTS,
1463
- "constraints should be settable with an iterator"
1464
- );
1465
-
1466
- let iterator_ref = CONSTRAINTS.iter().map(AsRef::as_ref);
1467
- assert_eq!(
1468
- Layout::default().constraints(iterator_ref).constraints,
1469
- CONSTRAINTS,
1470
- "constraints should be settable with an iterator of refs"
1471
- );
1472
- }
1473
-
1474
- #[test]
1475
- fn direction() {
1476
- assert_eq!(
1477
- Layout::default().direction(Direction::Horizontal).direction,
1478
- Direction::Horizontal
1479
- );
1480
- assert_eq!(
1481
- Layout::default().direction(Direction::Vertical).direction,
1482
- Direction::Vertical
1483
- );
1484
- }
1485
-
1486
- #[test]
1487
- fn margins() {
1488
- assert_eq!(Layout::default().margin(10).margin, Margin::new(10, 10));
1489
- assert_eq!(
1490
- Layout::default().horizontal_margin(10).margin,
1491
- Margin::new(10, 0)
1492
- );
1493
- assert_eq!(
1494
- Layout::default().vertical_margin(10).margin,
1495
- Margin::new(0, 10)
1496
- );
1497
- assert_eq!(
1498
- Layout::default()
1499
- .horizontal_margin(10)
1500
- .vertical_margin(20)
1501
- .margin,
1502
- Margin::new(10, 20)
1503
- );
1504
- }
1505
-
1506
- #[test]
1507
- fn flex() {
1508
- assert_eq!(Layout::default().flex, Flex::Start);
1509
- assert_eq!(Layout::default().flex(Flex::Center).flex, Flex::Center);
1510
- }
1511
-
1512
- #[test]
1513
- fn spacing() {
1514
- assert_eq!(Layout::default().spacing(10).spacing, Spacing::Space(10));
1515
- assert_eq!(Layout::default().spacing(0).spacing, Spacing::Space(0));
1516
- assert_eq!(Layout::default().spacing(-10).spacing, Spacing::Overlap(10));
1517
- }
1518
-
1519
- /// Tests for the `Layout::split()` function.
1520
- ///
1521
- /// There are many tests in this as the number of edge cases that are caused by the interaction
1522
- /// between the constraints is quite large. The tests are split into sections based on the type
1523
- /// of constraints that are used.
1524
- ///
1525
- /// These tests are characterization tests. This means that they are testing the way the code
1526
- /// currently works, and not the way it should work. This is because the current behavior is not
1527
- /// well defined, and it is not clear what the correct behavior should be. This means that if
1528
- /// the behavior changes, these tests should be updated to match the new behavior.
1529
- ///
1530
- /// EOL comments in each test are intended to communicate the purpose of each test and to make
1531
- /// it easy to see that the tests are as exhaustive as feasible:
1532
- /// - zero: constraint is zero
1533
- /// - exact: constraint is equal to the space
1534
- /// - underflow: constraint is for less than the full space
1535
- /// - overflow: constraint is for more than the full space
1536
- mod split {
1537
- use alloc::string::ToString;
1538
- use core::ops::Range;
1539
-
1540
- use itertools::Itertools;
1541
- use pretty_assertions::assert_eq;
1542
- use rstest::rstest;
1543
-
1544
- use super::*;
1545
- use crate::buffer::Buffer;
1546
- use crate::layout::Constraint::{self, *};
1547
- use crate::layout::{Direction, Flex, Layout, Rect};
1548
- use crate::text::Text;
1549
- use crate::widgets::Widget;
1550
-
1551
- /// Test that the given constraints applied to the given area result in the expected layout.
1552
- /// Each chunk is filled with a letter repeated as many times as the width of the chunk. The
1553
- /// resulting buffer is compared to the expected string.
1554
- ///
1555
- /// This approach is used rather than testing the resulting rects directly because it is
1556
- /// easier to visualize the result, and it leads to more concise tests that are easier to
1557
- /// compare against each other. E.g. `"abc"` is much more concise than `[Rect::new(0, 0, 1,
1558
- /// 1), Rect::new(1, 0, 1, 1), Rect::new(2, 0, 1, 1)]`.
1559
- #[track_caller]
1560
- fn letters(flex: Flex, constraints: &[Constraint], width: u16, expected: &str) {
1561
- let area = Rect::new(0, 0, width, 1);
1562
- let layout = Layout::default()
1563
- .direction(Direction::Horizontal)
1564
- .constraints(constraints)
1565
- .flex(flex)
1566
- .split(area);
1567
- let mut buffer = Buffer::empty(area);
1568
- for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) {
1569
- let s = c.to_string().repeat(area.width as usize);
1570
- Text::from(s).render(area, &mut buffer);
1571
- }
1572
- assert_eq!(buffer, Buffer::with_lines([expected]));
1573
- }
1574
-
1575
- #[rstest]
1576
- // flex, width, lengths, expected
1577
- #[case(Flex::Legacy, 1, &[Length(0)], "a")] // zero
1578
- #[case(Flex::Legacy, 1, &[Length(1)], "a")] // exact
1579
- #[case(Flex::Legacy, 1, &[Length(2)], "a")] // overflow
1580
- #[case(Flex::Legacy, 2, &[Length(0)], "aa")] // zero
1581
- #[case(Flex::Legacy, 2, &[Length(1)], "aa")] // underflow
1582
- #[case(Flex::Legacy, 2, &[Length(2)], "aa")] // exact
1583
- #[case(Flex::Legacy, 2, &[Length(3)], "aa")] // overflow
1584
- #[case(Flex::Legacy, 1, &[Length(0), Length(0)], "b")] // zero, zero
1585
- #[case(Flex::Legacy, 1, &[Length(0), Length(1)], "b")] // zero, exact
1586
- #[case(Flex::Legacy, 1, &[Length(0), Length(2)], "b")] // zero, overflow
1587
- #[case(Flex::Legacy, 1, &[Length(1), Length(0)], "a")] // exact, zero
1588
- #[case(Flex::Legacy, 1, &[Length(1), Length(1)], "a")] // exact, exact
1589
- #[case(Flex::Legacy, 1, &[Length(1), Length(2)], "a")] // exact, overflow
1590
- #[case(Flex::Legacy, 1, &[Length(2), Length(0)], "a")] // overflow, zero
1591
- #[case(Flex::Legacy, 1, &[Length(2), Length(1)], "a")] // overflow, exact
1592
- #[case(Flex::Legacy, 1, &[Length(2), Length(2)], "a")] // overflow, overflow
1593
- #[case(Flex::Legacy, 2, &[Length(0), Length(0)], "bb")] // zero, zero
1594
- #[case(Flex::Legacy, 2, &[Length(0), Length(1)], "bb")] // zero, underflow
1595
- #[case(Flex::Legacy, 2, &[Length(0), Length(2)], "bb")] // zero, exact
1596
- #[case(Flex::Legacy, 2, &[Length(0), Length(3)], "bb")] // zero, overflow
1597
- #[case(Flex::Legacy, 2, &[Length(1), Length(0)], "ab")] // underflow, zero
1598
- #[case(Flex::Legacy, 2, &[Length(1), Length(1)], "ab")] // underflow, underflow
1599
- #[case(Flex::Legacy, 2, &[Length(1), Length(2)], "ab")] // underflow, exact
1600
- #[case(Flex::Legacy, 2, &[Length(1), Length(3)], "ab")] // underflow, overflow
1601
- #[case(Flex::Legacy, 2, &[Length(2), Length(0)], "aa")] // exact, zero
1602
- #[case(Flex::Legacy, 2, &[Length(2), Length(1)], "aa")] // exact, underflow
1603
- #[case(Flex::Legacy, 2, &[Length(2), Length(2)], "aa")] // exact, exact
1604
- #[case(Flex::Legacy, 2, &[Length(2), Length(3)], "aa")] // exact, overflow
1605
- #[case(Flex::Legacy, 2, &[Length(3), Length(0)], "aa")] // overflow, zero
1606
- #[case(Flex::Legacy, 2, &[Length(3), Length(1)], "aa")] // overflow, underflow
1607
- #[case(Flex::Legacy, 2, &[Length(3), Length(2)], "aa")] // overflow, exact
1608
- #[case(Flex::Legacy, 2, &[Length(3), Length(3)], "aa")] // overflow, overflow
1609
- #[case(Flex::Legacy, 3, &[Length(2), Length(2)], "aab")] // with stretchlast
1610
- fn length(
1611
- #[case] flex: Flex,
1612
- #[case] width: u16,
1613
- #[case] constraints: &[Constraint],
1614
- #[case] expected: &str,
1615
- ) {
1616
- letters(flex, constraints, width, expected);
1617
- }
1618
-
1619
- #[rstest]
1620
- #[case(Flex::Legacy, 1, &[Max(0)], "a")] // zero
1621
- #[case(Flex::Legacy, 1, &[Max(1)], "a")] // exact
1622
- #[case(Flex::Legacy, 1, &[Max(2)], "a")] // overflow
1623
- #[case(Flex::Legacy, 2, &[Max(0)], "aa")] // zero
1624
- #[case(Flex::Legacy, 2, &[Max(1)], "aa")] // underflow
1625
- #[case(Flex::Legacy, 2, &[Max(2)], "aa")] // exact
1626
- #[case(Flex::Legacy, 2, &[Max(3)], "aa")] // overflow
1627
- #[case(Flex::Legacy, 1, &[Max(0), Max(0)], "b")] // zero, zero
1628
- #[case(Flex::Legacy, 1, &[Max(0), Max(1)], "b")] // zero, exact
1629
- #[case(Flex::Legacy, 1, &[Max(0), Max(2)], "b")] // zero, overflow
1630
- #[case(Flex::Legacy, 1, &[Max(1), Max(0)], "a")] // exact, zero
1631
- #[case(Flex::Legacy, 1, &[Max(1), Max(1)], "a")] // exact, exact
1632
- #[case(Flex::Legacy, 1, &[Max(1), Max(2)], "a")] // exact, overflow
1633
- #[case(Flex::Legacy, 1, &[Max(2), Max(0)], "a")] // overflow, zero
1634
- #[case(Flex::Legacy, 1, &[Max(2), Max(1)], "a")] // overflow, exact
1635
- #[case(Flex::Legacy, 1, &[Max(2), Max(2)], "a")] // overflow, overflow
1636
- #[case(Flex::Legacy, 2, &[Max(0), Max(0)], "bb")] // zero, zero
1637
- #[case(Flex::Legacy, 2, &[Max(0), Max(1)], "bb")] // zero, underflow
1638
- #[case(Flex::Legacy, 2, &[Max(0), Max(2)], "bb")] // zero, exact
1639
- #[case(Flex::Legacy, 2, &[Max(0), Max(3)], "bb")] // zero, overflow
1640
- #[case(Flex::Legacy, 2, &[Max(1), Max(0)], "ab")] // underflow, zero
1641
- #[case(Flex::Legacy, 2, &[Max(1), Max(1)], "ab")] // underflow, underflow
1642
- #[case(Flex::Legacy, 2, &[Max(1), Max(2)], "ab")] // underflow, exact
1643
- #[case(Flex::Legacy, 2, &[Max(1), Max(3)], "ab")] // underflow, overflow
1644
- #[case(Flex::Legacy, 2, &[Max(2), Max(0)], "aa")] // exact, zero
1645
- #[case(Flex::Legacy, 2, &[Max(2), Max(1)], "aa")] // exact, underflow
1646
- #[case(Flex::Legacy, 2, &[Max(2), Max(2)], "aa")] // exact, exact
1647
- #[case(Flex::Legacy, 2, &[Max(2), Max(3)], "aa")] // exact, overflow
1648
- #[case(Flex::Legacy, 2, &[Max(3), Max(0)], "aa")] // overflow, zero
1649
- #[case(Flex::Legacy, 2, &[Max(3), Max(1)], "aa")] // overflow, underflow
1650
- #[case(Flex::Legacy, 2, &[Max(3), Max(2)], "aa")] // overflow, exact
1651
- #[case(Flex::Legacy, 2, &[Max(3), Max(3)], "aa")] // overflow, overflow
1652
- #[case(Flex::Legacy, 3, &[Max(2), Max(2)], "aab")]
1653
- fn max(
1654
- #[case] flex: Flex,
1655
- #[case] width: u16,
1656
- #[case] constraints: &[Constraint],
1657
- #[case] expected: &str,
1658
- ) {
1659
- letters(flex, constraints, width, expected);
1660
- }
1661
-
1662
- #[rstest]
1663
- #[case(Flex::Legacy, 1, &[Min(0), Min(0)], "b")] // zero, zero
1664
- #[case(Flex::Legacy, 1, &[Min(0), Min(1)], "b")] // zero, exact
1665
- #[case(Flex::Legacy, 1, &[Min(0), Min(2)], "b")] // zero, overflow
1666
- #[case(Flex::Legacy, 1, &[Min(1), Min(0)], "a")] // exact, zero
1667
- #[case(Flex::Legacy, 1, &[Min(1), Min(1)], "a")] // exact, exact
1668
- #[case(Flex::Legacy, 1, &[Min(1), Min(2)], "a")] // exact, overflow
1669
- #[case(Flex::Legacy, 1, &[Min(2), Min(0)], "a")] // overflow, zero
1670
- #[case(Flex::Legacy, 1, &[Min(2), Min(1)], "a")] // overflow, exact
1671
- #[case(Flex::Legacy, 1, &[Min(2), Min(2)], "a")] // overflow, overflow
1672
- #[case(Flex::Legacy, 2, &[Min(0), Min(0)], "bb")] // zero, zero
1673
- #[case(Flex::Legacy, 2, &[Min(0), Min(1)], "bb")] // zero, underflow
1674
- #[case(Flex::Legacy, 2, &[Min(0), Min(2)], "bb")] // zero, exact
1675
- #[case(Flex::Legacy, 2, &[Min(0), Min(3)], "bb")] // zero, overflow
1676
- #[case(Flex::Legacy, 2, &[Min(1), Min(0)], "ab")] // underflow, zero
1677
- #[case(Flex::Legacy, 2, &[Min(1), Min(1)], "ab")] // underflow, underflow
1678
- #[case(Flex::Legacy, 2, &[Min(1), Min(2)], "ab")] // underflow, exact
1679
- #[case(Flex::Legacy, 2, &[Min(1), Min(3)], "ab")] // underflow, overflow
1680
- #[case(Flex::Legacy, 2, &[Min(2), Min(0)], "aa")] // exact, zero
1681
- #[case(Flex::Legacy, 2, &[Min(2), Min(1)], "aa")] // exact, underflow
1682
- #[case(Flex::Legacy, 2, &[Min(2), Min(2)], "aa")] // exact, exact
1683
- #[case(Flex::Legacy, 2, &[Min(2), Min(3)], "aa")] // exact, overflow
1684
- #[case(Flex::Legacy, 2, &[Min(3), Min(0)], "aa")] // overflow, zero
1685
- #[case(Flex::Legacy, 2, &[Min(3), Min(1)], "aa")] // overflow, underflow
1686
- #[case(Flex::Legacy, 2, &[Min(3), Min(2)], "aa")] // overflow, exact
1687
- #[case(Flex::Legacy, 2, &[Min(3), Min(3)], "aa")] // overflow, overflow
1688
- #[case(Flex::Legacy, 3, &[Min(2), Min(2)], "aab")]
1689
- fn min(
1690
- #[case] flex: Flex,
1691
- #[case] width: u16,
1692
- #[case] constraints: &[Constraint],
1693
- #[case] expected: &str,
1694
- ) {
1695
- letters(flex, constraints, width, expected);
1696
- }
1697
-
1698
- #[rstest] // flex, width, lengths, expected
1699
- // One constraint will take all the space (width = 1)
1700
- #[case(Flex::Legacy, 1, &[Percentage(0)], "a")]
1701
- #[case(Flex::Legacy, 1, &[Percentage(25)], "a")]
1702
- #[case(Flex::Legacy, 1, &[Percentage(50)], "a")]
1703
- #[case(Flex::Legacy, 1, &[Percentage(90)], "a")]
1704
- #[case(Flex::Legacy, 1, &[Percentage(100)], "a")]
1705
- #[case(Flex::Legacy, 1, &[Percentage(200)], "a")]
1706
- // One constraint will take all the space (width = 2)
1707
- #[case(Flex::Legacy, 2, &[Percentage(0)], "aa")]
1708
- #[case(Flex::Legacy, 2, &[Percentage(10)], "aa")]
1709
- #[case(Flex::Legacy, 2, &[Percentage(25)], "aa")]
1710
- #[case(Flex::Legacy, 2, &[Percentage(50)], "aa")]
1711
- #[case(Flex::Legacy, 2, &[Percentage(66)], "aa")]
1712
- #[case(Flex::Legacy, 2, &[Percentage(100)], "aa")]
1713
- #[case(Flex::Legacy, 2, &[Percentage(200)], "aa")]
1714
- // One constraint will take all the space (width = 3)
1715
- #[case(Flex::Legacy, 10, &[Percentage(0)], "aaaaaaaaaa")]
1716
- #[case(Flex::Legacy, 10, &[Percentage(10)], "aaaaaaaaaa")]
1717
- #[case(Flex::Legacy, 10, &[Percentage(25)], "aaaaaaaaaa")]
1718
- #[case(Flex::Legacy, 10, &[Percentage(50)], "aaaaaaaaaa")]
1719
- #[case(Flex::Legacy, 10, &[Percentage(66)], "aaaaaaaaaa")]
1720
- #[case(Flex::Legacy, 10, &[Percentage(100)], "aaaaaaaaaa")]
1721
- #[case(Flex::Legacy, 10, &[Percentage(200)], "aaaaaaaaaa")]
1722
- // 0%/any allocates all the space to the second constraint
1723
- #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(0)], "b")]
1724
- #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(10)], "b")]
1725
- #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(50)], "b")]
1726
- #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(90)], "b")]
1727
- #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(100)], "b")]
1728
- #[case(Flex::Legacy, 1, &[Percentage(0), Percentage(200)], "b")]
1729
- // 10%/any allocates all the space to the second constraint (even if it is 0)
1730
- #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(0)], "b")]
1731
- #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(10)], "b")]
1732
- #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(50)], "b")]
1733
- #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(90)], "b")]
1734
- #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(100)], "b")]
1735
- #[case(Flex::Legacy, 1, &[Percentage(10), Percentage(200)], "b")]
1736
- // 50%/any allocates all the space to the first constraint
1737
- #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(0)], "a")]
1738
- #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(50)], "a")]
1739
- #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(100)], "a")]
1740
- #[case(Flex::Legacy, 1, &[Percentage(50), Percentage(200)], "a")]
1741
- // 90%/any allocates all the space to the first constraint
1742
- #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(0)], "a")]
1743
- #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(50)], "a")]
1744
- #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(100)], "a")]
1745
- #[case(Flex::Legacy, 1, &[Percentage(90), Percentage(200)], "a")]
1746
- // 100%/any allocates all the space to the first constraint
1747
- #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(0)], "a")]
1748
- #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(50)], "a")]
1749
- #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(100)], "a")]
1750
- #[case(Flex::Legacy, 1, &[Percentage(100), Percentage(200)], "a")]
1751
- // 0%/any allocates all the space to the second constraint
1752
- #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(0)], "bb")]
1753
- #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(25)], "bb")]
1754
- #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(50)], "bb")]
1755
- #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(100)], "bb")]
1756
- #[case(Flex::Legacy, 2, &[Percentage(0), Percentage(200)], "bb")]
1757
- // 10%/any allocates all the space to the second constraint
1758
- #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(0)], "bb")]
1759
- #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(25)], "bb")]
1760
- #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(50)], "bb")]
1761
- #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(100)], "bb")]
1762
- #[case(Flex::Legacy, 2, &[Percentage(10), Percentage(200)], "bb")]
1763
- // 25% * 2 = 0.5, which rounds up to 1, so the first constraint gets 1
1764
- #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(0)], "ab")]
1765
- #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(25)], "ab")]
1766
- #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(50)], "ab")]
1767
- #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(100)], "ab")]
1768
- #[case(Flex::Legacy, 2, &[Percentage(25), Percentage(200)], "ab")]
1769
- // 33% * 2 = 0.66, so the first constraint gets 1
1770
- #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(0)], "ab")]
1771
- #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(25)], "ab")]
1772
- #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(50)], "ab")]
1773
- #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(100)], "ab")]
1774
- #[case(Flex::Legacy, 2, &[Percentage(33), Percentage(200)], "ab")]
1775
- // 50% * 2 = 1, so the first constraint gets 1
1776
- #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(0)], "ab")]
1777
- #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(50)], "ab")]
1778
- #[case(Flex::Legacy, 2, &[Percentage(50), Percentage(100)], "ab")]
1779
- // 100%/any allocates all the space to the first constraint
1780
- // This is probably not the correct behavior, but it is the current behavior
1781
- #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(0)], "aa")]
1782
- #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(50)], "aa")]
1783
- #[case(Flex::Legacy, 2, &[Percentage(100), Percentage(100)], "aa")]
1784
- // 33%/any allocates 1 to the first constraint the rest to the second
1785
- #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(33)], "abb")]
1786
- #[case(Flex::Legacy, 3, &[Percentage(33), Percentage(66)], "abb")]
1787
- // 33%/any allocates 1.33 = 1 to the first constraint the rest to the second
1788
- #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(33)], "abbb")]
1789
- #[case(Flex::Legacy, 4, &[Percentage(33), Percentage(66)], "abbb")]
1790
- // Longer tests zero allocates everything to the second constraint
1791
- #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(0)], "bbbbbbbbbb" )]
1792
- #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(25)], "bbbbbbbbbb" )]
1793
- #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(50)], "bbbbbbbbbb" )]
1794
- #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1795
- #[case(Flex::Legacy, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1796
- // 10% allocates a single character to the first constraint
1797
- #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(0)], "abbbbbbbbb" )]
1798
- #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(25)], "abbbbbbbbb" )]
1799
- #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(50)], "abbbbbbbbb" )]
1800
- #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1801
- #[case(Flex::Legacy, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1802
- // 25% allocates 2.5 = 3 characters to the first constraint
1803
- #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(0)], "aaabbbbbbb" )]
1804
- #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(25)], "aaabbbbbbb" )]
1805
- #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(50)], "aaabbbbbbb" )]
1806
- #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1807
- #[case(Flex::Legacy, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1808
- // 33% allocates 3.3 = 3 characters to the first constraint
1809
- #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(0)], "aaabbbbbbb" )]
1810
- #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(25)], "aaabbbbbbb" )]
1811
- #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(50)], "aaabbbbbbb" )]
1812
- #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1813
- #[case(Flex::Legacy, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1814
- // 50% allocates 5 characters to the first constraint
1815
- #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(0)], "aaaaabbbbb" )]
1816
- #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1817
- #[case(Flex::Legacy, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1818
- // 100% allocates everything to the first constraint
1819
- #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1820
- #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(50)], "aaaaaaaaaa" )]
1821
- #[case(Flex::Legacy, 10, &[Percentage(100), Percentage(100)], "aaaaaaaaaa" )]
1822
- fn percentage(
1823
- #[case] flex: Flex,
1824
- #[case] width: u16,
1825
- #[case] constraints: &[Constraint],
1826
- #[case] expected: &str,
1827
- ) {
1828
- letters(flex, constraints, width, expected);
1829
- }
1830
-
1831
- #[rstest]
1832
- #[case(Flex::Start, 10, &[Percentage(0), Percentage(0)], " " )]
1833
- #[case(Flex::Start, 10, &[Percentage(0), Percentage(25)], "bbb " )]
1834
- #[case(Flex::Start, 10, &[Percentage(0), Percentage(50)], "bbbbb " )]
1835
- #[case(Flex::Start, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1836
- #[case(Flex::Start, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1837
- #[case(Flex::Start, 10, &[Percentage(10), Percentage(0)], "a " )]
1838
- #[case(Flex::Start, 10, &[Percentage(10), Percentage(25)], "abbb " )]
1839
- #[case(Flex::Start, 10, &[Percentage(10), Percentage(50)], "abbbbb " )]
1840
- #[case(Flex::Start, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1841
- #[case(Flex::Start, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1842
- #[case(Flex::Start, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1843
- #[case(Flex::Start, 10, &[Percentage(25), Percentage(25)], "aaabb " )]
1844
- #[case(Flex::Start, 10, &[Percentage(25), Percentage(50)], "aaabbbbb " )]
1845
- #[case(Flex::Start, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1846
- #[case(Flex::Start, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1847
- #[case(Flex::Start, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1848
- #[case(Flex::Start, 10, &[Percentage(33), Percentage(25)], "aaabbb " )]
1849
- #[case(Flex::Start, 10, &[Percentage(33), Percentage(50)], "aaabbbbb " )]
1850
- #[case(Flex::Start, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1851
- #[case(Flex::Start, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1852
- #[case(Flex::Start, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1853
- #[case(Flex::Start, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1854
- #[case(Flex::Start, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1855
- #[case(Flex::Start, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1856
- #[case(Flex::Start, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1857
- #[case(Flex::Start, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1858
- #[case(Flex::Start, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1859
- fn percentage_start(
1860
- #[case] flex: Flex,
1861
- #[case] width: u16,
1862
- #[case] constraints: &[Constraint],
1863
- #[case] expected: &str,
1864
- ) {
1865
- letters(flex, constraints, width, expected);
1866
- }
1867
-
1868
- #[rstest]
1869
- #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(0)], " " )]
1870
- #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(25)], " bb" )]
1871
- #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(50)], " bbbbb" )]
1872
- #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(100)], "bbbbbbbbbb" )]
1873
- #[case(Flex::SpaceBetween, 10, &[Percentage(0), Percentage(200)], "bbbbbbbbbb" )]
1874
- #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(0)], "a " )]
1875
- #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(25)], "a bb" )]
1876
- #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(50)], "a bbbbb" )]
1877
- #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(100)], "abbbbbbbbb" )]
1878
- #[case(Flex::SpaceBetween, 10, &[Percentage(10), Percentage(200)], "abbbbbbbbb" )]
1879
- #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(0)], "aaa " )]
1880
- #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(25)], "aaa bb" )]
1881
- #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(50)], "aaa bbbbb" )]
1882
- #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(100)], "aaabbbbbbb" )]
1883
- #[case(Flex::SpaceBetween, 10, &[Percentage(25), Percentage(200)], "aaabbbbbbb" )]
1884
- #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(0)], "aaa " )]
1885
- #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(25)], "aaa bb" )]
1886
- #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(50)], "aaa bbbbb" )]
1887
- #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(100)], "aaabbbbbbb" )]
1888
- #[case(Flex::SpaceBetween, 10, &[Percentage(33), Percentage(200)], "aaabbbbbbb" )]
1889
- #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(0)], "aaaaa " )]
1890
- #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(50)], "aaaaabbbbb" )]
1891
- #[case(Flex::SpaceBetween, 10, &[Percentage(50), Percentage(100)], "aaaaabbbbb" )]
1892
- #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(0)], "aaaaaaaaaa" )]
1893
- #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(50)], "aaaaabbbbb" )]
1894
- #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(100)], "aaaaabbbbb" )]
1895
- #[case(Flex::SpaceBetween, 10, &[Percentage(100), Percentage(200)], "aaaaabbbbb" )]
1896
- fn percentage_spacebetween(
1897
- #[case] flex: Flex,
1898
- #[case] width: u16,
1899
- #[case] constraints: &[Constraint],
1900
- #[case] expected: &str,
1901
- ) {
1902
- letters(flex, constraints, width, expected);
1903
- }
1904
-
1905
- #[rstest]
1906
- // flex, width, ratios, expected
1907
- // Just one ratio takes up the whole space
1908
- #[case(Flex::Legacy, 1, &[Ratio(0, 1)], "a")]
1909
- #[case(Flex::Legacy, 1, &[Ratio(1, 4)], "a")]
1910
- #[case(Flex::Legacy, 1, &[Ratio(1, 2)], "a")]
1911
- #[case(Flex::Legacy, 1, &[Ratio(9, 10)], "a")]
1912
- #[case(Flex::Legacy, 1, &[Ratio(1, 1)], "a")]
1913
- #[case(Flex::Legacy, 1, &[Ratio(2, 1)], "a")]
1914
- #[case(Flex::Legacy, 2, &[Ratio(0, 1)], "aa")]
1915
- #[case(Flex::Legacy, 2, &[Ratio(1, 10)], "aa")]
1916
- #[case(Flex::Legacy, 2, &[Ratio(1, 4)], "aa")]
1917
- #[case(Flex::Legacy, 2, &[Ratio(1, 2)], "aa")]
1918
- #[case(Flex::Legacy, 2, &[Ratio(2, 3)], "aa")]
1919
- #[case(Flex::Legacy, 2, &[Ratio(1, 1)], "aa")]
1920
- #[case(Flex::Legacy, 2, &[Ratio(2, 1)], "aa")]
1921
- #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(0, 1)], "b")]
1922
- #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 10)], "b")]
1923
- #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 2)], "b")]
1924
- #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(9, 10)], "b")]
1925
- #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(1, 1)], "b")]
1926
- #[case(Flex::Legacy, 1, &[Ratio(0, 1), Ratio(2, 1)], "b")]
1927
- #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(0, 1)], "b")]
1928
- #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 10)], "b")]
1929
- #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 2)], "b")]
1930
- #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(9, 10)], "b")]
1931
- #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(1, 1)], "b")]
1932
- #[case(Flex::Legacy, 1, &[Ratio(1, 10), Ratio(2, 1)], "b")]
1933
- #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(0, 1)], "a")]
1934
- #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 2)], "a")]
1935
- #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(1, 1)], "a")]
1936
- #[case(Flex::Legacy, 1, &[Ratio(1, 2), Ratio(2, 1)], "a")]
1937
- #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(0, 1)], "a")]
1938
- #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 2)], "a")]
1939
- #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(1, 1)], "a")]
1940
- #[case(Flex::Legacy, 1, &[Ratio(9, 10), Ratio(2, 1)], "a")]
1941
- #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(0, 1)], "a")]
1942
- #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 2)], "a")]
1943
- #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(1, 1)], "a")]
1944
- #[case(Flex::Legacy, 1, &[Ratio(1, 1), Ratio(2, 1)], "a")]
1945
- #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(0, 1)], "bb")]
1946
- #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 4)], "bb")]
1947
- #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 2)], "bb")]
1948
- #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(1, 1)], "bb")]
1949
- #[case(Flex::Legacy, 2, &[Ratio(0, 1), Ratio(2, 1)], "bb")]
1950
- #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(0, 1)], "bb")]
1951
- #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 4)], "bb")]
1952
- #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 2)], "bb")]
1953
- #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(1, 1)], "bb")]
1954
- #[case(Flex::Legacy, 2, &[Ratio(1, 10), Ratio(2, 1)], "bb")]
1955
- #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(0, 1)], "ab")]
1956
- #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 4)], "ab")]
1957
- #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 2)], "ab")]
1958
- #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(1, 1)], "ab")]
1959
- #[case(Flex::Legacy, 2, &[Ratio(1, 4), Ratio(2, 1)], "ab")]
1960
- #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(0, 1)], "ab")]
1961
- #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 4)], "ab")]
1962
- #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 2)], "ab")]
1963
- #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(1, 1)], "ab")]
1964
- #[case(Flex::Legacy, 2, &[Ratio(1, 3), Ratio(2, 1)], "ab")]
1965
- #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(0, 1)], "ab")]
1966
- #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 2)], "ab")]
1967
- #[case(Flex::Legacy, 2, &[Ratio(1, 2), Ratio(1, 1)], "ab")]
1968
- #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(0, 1)], "aa")]
1969
- #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 2)], "aa")]
1970
- #[case(Flex::Legacy, 2, &[Ratio(1, 1), Ratio(1, 1)], "aa")]
1971
- #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(1, 3)], "abb")]
1972
- #[case(Flex::Legacy, 3, &[Ratio(1, 3), Ratio(2,3)], "abb")]
1973
- #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(0, 1)], "bbbbbbbbbb" )]
1974
- #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbbbbbbbbb" )]
1975
- #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbbbbbbb" )]
1976
- #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
1977
- #[case(Flex::Legacy, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
1978
- #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(0, 1)], "abbbbbbbbb" )]
1979
- #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbbbbbbbb" )]
1980
- #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbbbbbb" )]
1981
- #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
1982
- #[case(Flex::Legacy, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
1983
- #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaabbbbbbb" )]
1984
- #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabbbbbbb" )]
1985
- #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbbbb" )]
1986
- #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
1987
- #[case(Flex::Legacy, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
1988
- #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaabbbbbbb" )]
1989
- #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbbbbbb" )]
1990
- #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbbbb" )]
1991
- #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
1992
- #[case(Flex::Legacy, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
1993
- #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaabbbbb" )]
1994
- #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
1995
- #[case(Flex::Legacy, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
1996
- #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
1997
- #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaaaaaaa" )]
1998
- #[case(Flex::Legacy, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaaaaaaa" )]
1999
- fn ratio(
2000
- #[case] flex: Flex,
2001
- #[case] width: u16,
2002
- #[case] constraints: &[Constraint],
2003
- #[case] expected: &str,
2004
- ) {
2005
- letters(flex, constraints, width, expected);
2006
- }
2007
-
2008
- #[rstest]
2009
- #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
2010
- #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 4)], "bbb " )]
2011
- #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 2)], "bbbbb " )]
2012
- #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
2013
- #[case(Flex::Start, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
2014
- #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
2015
- #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 4)], "abbb " )]
2016
- #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 2)], "abbbbb " )]
2017
- #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
2018
- #[case(Flex::Start, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
2019
- #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
2020
- #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaabb " )]
2021
- #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaabbbbb " )]
2022
- #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
2023
- #[case(Flex::Start, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
2024
- #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
2025
- #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaabbb " )]
2026
- #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaabbbbb " )]
2027
- #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
2028
- #[case(Flex::Start, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
2029
- #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
2030
- #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
2031
- #[case(Flex::Start, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
2032
- #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
2033
- #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
2034
- #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
2035
- #[case(Flex::Start, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
2036
- fn ratio_start(
2037
- #[case] flex: Flex,
2038
- #[case] width: u16,
2039
- #[case] constraints: &[Constraint],
2040
- #[case] expected: &str,
2041
- ) {
2042
- letters(flex, constraints, width, expected);
2043
- }
2044
-
2045
- #[rstest]
2046
- #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(0, 1)], " " )]
2047
- #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 4)], " bb" )]
2048
- #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 2)], " bbbbb" )]
2049
- #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(1, 1)], "bbbbbbbbbb" )]
2050
- #[case(Flex::SpaceBetween, 10, &[Ratio(0, 1), Ratio(2, 1)], "bbbbbbbbbb" )]
2051
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(0, 1)], "a " )]
2052
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 4)], "a bb" )]
2053
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 2)], "a bbbbb" )]
2054
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(1, 1)], "abbbbbbbbb" )]
2055
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 10), Ratio(2, 1)], "abbbbbbbbb" )]
2056
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(0, 1)], "aaa " )]
2057
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 4)], "aaa bb" )]
2058
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 2)], "aaa bbbbb" )]
2059
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(1, 1)], "aaabbbbbbb" )]
2060
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 4), Ratio(2, 1)], "aaabbbbbbb" )]
2061
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(0, 1)], "aaa " )]
2062
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 4)], "aaa bb" )]
2063
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 2)], "aaa bbbbb" )]
2064
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(1, 1)], "aaabbbbbbb" )]
2065
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 3), Ratio(2, 1)], "aaabbbbbbb" )]
2066
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(0, 1)], "aaaaa " )]
2067
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 2)], "aaaaabbbbb" )]
2068
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 2), Ratio(1, 1)], "aaaaabbbbb" )]
2069
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(0, 1)], "aaaaaaaaaa" )]
2070
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 2)], "aaaaabbbbb" )]
2071
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(1, 1)], "aaaaabbbbb" )]
2072
- #[case(Flex::SpaceBetween, 10, &[Ratio(1, 1), Ratio(2, 1)], "aaaaabbbbb" )]
2073
- fn ratio_spacebetween(
2074
- #[case] flex: Flex,
2075
- #[case] width: u16,
2076
- #[case] constraints: &[Constraint],
2077
- #[case] expected: &str,
2078
- ) {
2079
- letters(flex, constraints, width, expected);
2080
- }
2081
-
2082
- #[test]
2083
- fn vertical_split_by_height() {
2084
- let target = Rect {
2085
- x: 2,
2086
- y: 2,
2087
- width: 10,
2088
- height: 10,
2089
- };
2090
-
2091
- let chunks = Layout::default()
2092
- .direction(Direction::Vertical)
2093
- .constraints([
2094
- Constraint::Percentage(10),
2095
- Constraint::Max(5),
2096
- Constraint::Min(1),
2097
- ])
2098
- .split(target);
2099
-
2100
- assert_eq!(chunks.iter().map(|r| r.height).sum::<u16>(), target.height);
2101
- chunks.windows(2).for_each(|w| assert!(w[0].y <= w[1].y));
2102
- }
2103
-
2104
- #[test]
2105
- fn edge_cases() {
2106
- // stretches into last
2107
- let layout = Layout::default()
2108
- .constraints([
2109
- Constraint::Percentage(50),
2110
- Constraint::Percentage(50),
2111
- Constraint::Min(0),
2112
- ])
2113
- .split(Rect::new(0, 0, 1, 1));
2114
- assert_eq!(
2115
- layout[..],
2116
- [
2117
- Rect::new(0, 0, 1, 1),
2118
- Rect::new(0, 1, 1, 0),
2119
- Rect::new(0, 1, 1, 0)
2120
- ]
2121
- );
2122
-
2123
- // stretches into last
2124
- let layout = Layout::default()
2125
- .constraints([
2126
- Constraint::Max(1),
2127
- Constraint::Percentage(99),
2128
- Constraint::Min(0),
2129
- ])
2130
- .split(Rect::new(0, 0, 1, 1));
2131
- assert_eq!(
2132
- layout[..],
2133
- [
2134
- Rect::new(0, 0, 1, 0),
2135
- Rect::new(0, 0, 1, 1),
2136
- Rect::new(0, 1, 1, 0)
2137
- ]
2138
- );
2139
-
2140
- // minimal bug from
2141
- // https://github.com/ratatui/ratatui/pull/404#issuecomment-1681850644
2142
- // TODO: check if this bug is now resolved?
2143
- let layout = Layout::default()
2144
- .constraints([Min(1), Length(0), Min(1)])
2145
- .direction(Direction::Horizontal)
2146
- .split(Rect::new(0, 0, 1, 1));
2147
- assert_eq!(
2148
- layout[..],
2149
- [
2150
- Rect::new(0, 0, 1, 1),
2151
- Rect::new(1, 0, 0, 1),
2152
- Rect::new(1, 0, 0, 1),
2153
- ]
2154
- );
2155
-
2156
- // This stretches the 2nd last length instead of the last min based on ranking
2157
- let layout = Layout::default()
2158
- .constraints([Length(3), Min(4), Length(1), Min(4)])
2159
- .direction(Direction::Horizontal)
2160
- .split(Rect::new(0, 0, 7, 1));
2161
- assert_eq!(
2162
- layout[..],
2163
- [
2164
- Rect::new(0, 0, 0, 1),
2165
- Rect::new(0, 0, 4, 1),
2166
- Rect::new(4, 0, 0, 1),
2167
- Rect::new(4, 0, 3, 1),
2168
- ]
2169
- );
2170
- }
2171
-
2172
- #[rstest]
2173
- #[case::len_min1(vec![Length(25), Min(100)], vec![0..0, 0..100])]
2174
- #[case::len_min2(vec![Length(25), Min(0)], vec![0..25, 25..100])]
2175
- #[case::len_max1(vec![Length(25), Max(0)], vec![0..100, 100..100])]
2176
- #[case::len_max2(vec![Length(25), Max(100)], vec![0..25, 25..100])]
2177
- #[case::len_perc(vec![Length(25), Percentage(25)], vec![0..25, 25..100])]
2178
- #[case::perc_len(vec![Percentage(25), Length(25)], vec![0..75, 75..100])]
2179
- #[case::len_ratio(vec![Length(25), Ratio(1, 4)], vec![0..25, 25..100])]
2180
- #[case::ratio_len(vec![Ratio(1, 4), Length(25)], vec![0..75, 75..100])]
2181
- #[case::len_len(vec![Length(25), Length(25)], vec![0..25, 25..100])]
2182
- #[case::len1(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2183
- #[case::len2(vec![Length(15), Length(35), Length(25)], vec![0..15, 15..50, 50..100])]
2184
- #[case::len3(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2185
- fn constraint_length(
2186
- #[case] constraints: Vec<Constraint>,
2187
- #[case] expected: Vec<Range<u16>>,
2188
- ) {
2189
- let rect = Rect::new(0, 0, 100, 1);
2190
- let ranges = Layout::horizontal(constraints)
2191
- .flex(Flex::Legacy)
2192
- .split(rect)
2193
- .iter()
2194
- .map(|r| r.left()..r.right())
2195
- .collect_vec();
2196
- assert_eq!(ranges, expected);
2197
- }
2198
-
2199
- #[rstest]
2200
- #[case(7, vec![Length(4), Length(4)], vec![0..3, 4..7])]
2201
- #[case(4, vec![Length(4), Length(4)], vec![0..2, 3..4])]
2202
- fn table_length(
2203
- #[case] width: u16,
2204
- #[case] constraints: Vec<Constraint>,
2205
- #[case] expected: Vec<Range<u16>>,
2206
- ) {
2207
- let rect = Rect::new(0, 0, width, 1);
2208
- let ranges = Layout::horizontal(constraints)
2209
- .spacing(1)
2210
- .flex(Flex::Start)
2211
- .split(rect)
2212
- .iter()
2213
- .map(|r| r.left()..r.right())
2214
- .collect::<Vec<Range<u16>>>();
2215
- assert_eq!(ranges, expected);
2216
- }
2217
-
2218
- #[rstest]
2219
- #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![0..50, 50..75, 75..100])]
2220
- #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![0..25, 25..50, 50..100])]
2221
- #[case::len_len_len(vec![Length(33), Length(33), Length(33)], vec![0..33, 33..66, 66..100])]
2222
- #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2223
- #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![0..25, 25..50, 50..100])]
2224
- #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![0..25, 25..75, 75..100])]
2225
- #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![0..50, 50..75, 75..100])]
2226
- #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![0..50, 50..75, 75..100])]
2227
- #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![0..80, 80..80, 80..100])]
2228
- #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![0..20, 20..21, 21..100])]
2229
- #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(1)], vec![0..45, 45..55, 55..100])]
2230
- #[case::fill_len_fill_2(vec![Fill(1), Length(10), Fill(2)], vec![0..30, 30..40, 40..100])]
2231
- #[case::fill_len_fill_4(vec![Fill(1), Length(10), Fill(4)], vec![0..18, 18..28, 28..100])]
2232
- #[case::fill_len_fill_5(vec![Fill(1), Length(10), Fill(5)], vec![0..15, 15..25, 25..100])]
2233
- #[case::len_len_len_25(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2234
- #[case::unstable_test(vec![Length(25), Length(25), Length(25)], vec![0..25, 25..50, 50..100])]
2235
- fn length_is_higher_priority(
2236
- #[case] constraints: Vec<Constraint>,
2237
- #[case] expected: Vec<Range<u16>>,
2238
- ) {
2239
- let rect = Rect::new(0, 0, 100, 1);
2240
- let ranges = Layout::horizontal(constraints)
2241
- .flex(Flex::Legacy)
2242
- .split(rect)
2243
- .iter()
2244
- .map(|r| r.left()..r.right())
2245
- .collect_vec();
2246
- assert_eq!(ranges, expected);
2247
- }
2248
-
2249
- #[rstest]
2250
- #[case::min_len_max(vec![Min(25), Length(25), Max(25)], vec![50, 25, 25])]
2251
- #[case::max_len_min(vec![Max(25), Length(25), Min(25)], vec![25, 25, 50])]
2252
- #[case::len_len_len1(vec![Length(33), Length(33), Length(33)], vec![33, 33, 33])]
2253
- #[case::len_len_len2(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2254
- #[case::perc_len_ratio(vec![Percentage(25), Length(25), Ratio(1, 4)], vec![25, 25, 25])]
2255
- #[case::len_ratio_perc(vec![Length(25), Ratio(1, 4), Percentage(25)], vec![25, 25, 25])]
2256
- #[case::ratio_len_perc(vec![Ratio(1, 4), Length(25), Percentage(25)], vec![25, 25, 25])]
2257
- #[case::ratio_perc_len(vec![Ratio(1, 4), Percentage(25), Length(25)], vec![25, 25, 25])]
2258
- #[case::len_len_min(vec![Length(100), Length(1), Min(20)], vec![79, 1, 20])]
2259
- #[case::min_len_len(vec![Min(20), Length(1), Length(100)], vec![20, 1, 79])]
2260
- #[case::fill_len_fill1(vec![Fill(1), Length(10), Fill(1)], vec![45, 10, 45])]
2261
- #[case::fill_len_fill2(vec![Fill(1), Length(10), Fill(2)], vec![30, 10, 60])]
2262
- #[case::fill_len_fill4(vec![Fill(1), Length(10), Fill(4)], vec![18, 10, 72])]
2263
- #[case::fill_len_fill5(vec![Fill(1), Length(10), Fill(5)], vec![15, 10, 75])]
2264
- #[case::len_len_len3(vec![Length(25), Length(25), Length(25)], vec![25, 25, 25])]
2265
- fn length_is_higher_priority_in_flex(
2266
- #[case] constraints: Vec<Constraint>,
2267
- #[case] expected: Vec<u16>,
2268
- ) {
2269
- let rect = Rect::new(0, 0, 100, 1);
2270
- for flex in [
2271
- Flex::Start,
2272
- Flex::End,
2273
- Flex::Center,
2274
- Flex::SpaceAround,
2275
- Flex::SpaceEvenly,
2276
- Flex::SpaceBetween,
2277
- ] {
2278
- let widths = Layout::horizontal(&constraints)
2279
- .flex(flex)
2280
- .split(rect)
2281
- .iter()
2282
- .map(|r| r.width)
2283
- .collect_vec();
2284
- assert_eq!(widths, expected);
2285
- }
2286
- }
2287
-
2288
- #[rstest]
2289
- #[case::fill_len_fill(vec![Fill(1), Length(10), Fill(2)], vec![0..13, 13..23, 23..50])]
2290
- #[case::len_fill_fill(vec![Length(10), Fill(2), Fill(1)], vec![0..10, 10..37, 37..50])] // might be unstable?
2291
- fn fixed_with_50_width(
2292
- #[case] constraints: Vec<Constraint>,
2293
- #[case] expected: Vec<Range<u16>>,
2294
- ) {
2295
- let rect = Rect::new(0, 0, 50, 1);
2296
- let ranges = Layout::horizontal(constraints)
2297
- .flex(Flex::Legacy)
2298
- .split(rect)
2299
- .iter()
2300
- .map(|r| r.left()..r.right())
2301
- .collect_vec();
2302
- assert_eq!(ranges, expected);
2303
- }
2304
-
2305
- #[rstest]
2306
- #[case::same_fill(vec![Fill(1), Fill(2), Fill(1), Fill(1)], vec![0..20, 20..60, 60..80, 80..100])]
2307
- #[case::inc_fill(vec![Fill(1), Fill(2), Fill(3), Fill(4)], vec![0..10, 10..30, 30..60, 60..100])]
2308
- #[case::dec_fill(vec![Fill(4), Fill(3), Fill(2), Fill(1)], vec![0..40, 40..70, 70..90, 90..100])]
2309
- #[case::rand_fill1(vec![Fill(1), Fill(3), Fill(2), Fill(4)], vec![0..10, 10..40, 40..60, 60..100])]
2310
- #[case::rand_fill2(vec![Fill(1), Fill(3), Length(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2311
- #[case::rand_fill3(vec![Fill(1), Fill(3), Percentage(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2312
- #[case::rand_fill4(vec![Fill(1), Fill(3), Min(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2313
- #[case::rand_fill5(vec![Fill(1), Fill(3), Max(50), Fill(2), Fill(4)], vec![0..5, 5..20, 20..70, 70..80, 80..100])]
2314
- #[case::zero_fill1(vec![Fill(0), Fill(1), Fill(0)], vec![0..0, 0..100, 100..100])]
2315
- #[case::zero_fill2(vec![Fill(0), Length(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2316
- #[case::zero_fill3(vec![Fill(0), Percentage(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2317
- #[case::zero_fill4(vec![Fill(0), Min(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2318
- #[case::zero_fill5(vec![Fill(0), Max(1), Fill(0)], vec![0..50, 50..51, 51..100])]
2319
- #[case::zero_fill6(vec![Fill(0), Fill(2), Fill(0), Fill(1)], vec![0..0, 0..67, 67..67, 67..100])]
2320
- #[case::space_fill1(vec![Fill(0), Fill(2), Percentage(20)], vec![0..0, 0..80, 80..100])]
2321
- #[case::space_fill2(vec![Fill(0), Fill(0), Percentage(20)], vec![0..40, 40..80, 80..100])]
2322
- #[case::space_fill3(vec![Fill(0), Ratio(1, 5)], vec![0..80, 80..100])]
2323
- #[case::space_fill4(vec![Fill(0), Fill(u16::MAX)], vec![0..0, 0..100])]
2324
- #[case::space_fill5(vec![Fill(u16::MAX), Fill(0)], vec![0..100, 100..100])]
2325
- #[case::space_fill6(vec![Fill(0), Percentage(20)], vec![0..80, 80..100])]
2326
- #[case::space_fill7(vec![Fill(1), Percentage(20)], vec![0..80, 80..100])]
2327
- #[case::space_fill8(vec![Fill(u16::MAX), Percentage(20)], vec![0..80, 80..100])]
2328
- #[case::space_fill9(vec![Fill(u16::MAX), Fill(0), Percentage(20)], vec![0..80, 80..80, 80..100])]
2329
- #[case::space_fill10(vec![Fill(0), Length(20)], vec![0..80, 80..100])]
2330
- #[case::space_fill11(vec![Fill(0), Min(20)], vec![0..80, 80..100])]
2331
- #[case::space_fill12(vec![Fill(0), Max(20)], vec![0..80, 80..100])]
2332
- #[case::fill_collapse1(vec![Fill(1), Fill(1), Fill(1), Min(30), Length(50)], vec![0..7, 7..13, 13..20, 20..50, 50..100])]
2333
- #[case::fill_collapse2(vec![Fill(1), Fill(1), Fill(1), Length(50), Length(50)], vec![0..0, 0..0, 0..0, 0..50, 50..100])]
2334
- #[case::fill_collapse3(vec![Fill(1), Fill(1), Fill(1), Length(75), Length(50)], vec![0..0, 0..0, 0..0, 0..75, 75..100])]
2335
- #[case::fill_collapse4(vec![Fill(1), Fill(1), Fill(1), Min(50), Max(50)], vec![0..0, 0..0, 0..0, 0..50, 50..100])]
2336
- #[case::fill_collapse5(vec![Fill(1), Fill(1), Fill(1), Ratio(1, 1)], vec![0..0, 0..0, 0..0, 0..100])]
2337
- #[case::fill_collapse6(vec![Fill(1), Fill(1), Fill(1), Percentage(100)], vec![0..0, 0..0, 0..0, 0..100])]
2338
- fn fill(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2339
- let rect = Rect::new(0, 0, 100, 1);
2340
- let ranges = Layout::horizontal(constraints)
2341
- .flex(Flex::Legacy)
2342
- .split(rect)
2343
- .iter()
2344
- .map(|r| r.left()..r.right())
2345
- .collect_vec();
2346
- assert_eq!(ranges, expected);
2347
- }
2348
-
2349
- #[rstest]
2350
- #[case::min_percentage(vec![Min(0), Percentage(20)], vec![0..80, 80..100])]
2351
- #[case::max_percentage(vec![Max(0), Percentage(20)], vec![0..0, 0..100])]
2352
- fn percentage_parameterized(
2353
- #[case] constraints: Vec<Constraint>,
2354
- #[case] expected: Vec<Range<u16>>,
2355
- ) {
2356
- let rect = Rect::new(0, 0, 100, 1);
2357
- let ranges = Layout::horizontal(constraints)
2358
- .flex(Flex::Legacy)
2359
- .split(rect)
2360
- .iter()
2361
- .map(|r| r.left()..r.right())
2362
- .collect_vec();
2363
- assert_eq!(ranges, expected);
2364
- }
2365
-
2366
- #[rstest]
2367
- #[case::max_min(vec![Max(100), Min(0)], vec![0..100, 100..100])]
2368
- #[case::min_max(vec![Min(0), Max(100)], vec![0..0, 0..100])]
2369
- #[case::length_min(vec![Length(u16::MAX), Min(10)], vec![0..90, 90..100])]
2370
- #[case::min_length(vec![Min(10), Length(u16::MAX)], vec![0..10, 10..100])]
2371
- #[case::length_max(vec![Length(0), Max(10)], vec![0..90, 90..100])]
2372
- #[case::max_length(vec![Max(10), Length(0)], vec![0..10, 10..100])]
2373
- fn min_max(#[case] constraints: Vec<Constraint>, #[case] expected: Vec<Range<u16>>) {
2374
- let rect = Rect::new(0, 0, 100, 1);
2375
- let ranges = Layout::horizontal(constraints)
2376
- .flex(Flex::Legacy)
2377
- .split(rect)
2378
- .iter()
2379
- .map(|r| r.left()..r.right())
2380
- .collect_vec();
2381
- assert_eq!(ranges, expected);
2382
- }
2383
-
2384
- #[rstest]
2385
- #[case::length_legacy(vec![Length(50)], vec![0..100], Flex::Legacy)]
2386
- #[case::length_start(vec![Length(50)], vec![0..50], Flex::Start)]
2387
- #[case::length_end(vec![Length(50)], vec![50..100], Flex::End)]
2388
- #[case::length_center(vec![Length(50)], vec![25..75], Flex::Center)]
2389
- #[case::ratio_legacy(vec![Ratio(1, 2)], vec![0..100], Flex::Legacy)]
2390
- #[case::ratio_start(vec![Ratio(1, 2)], vec![0..50], Flex::Start)]
2391
- #[case::ratio_end(vec![Ratio(1, 2)], vec![50..100], Flex::End)]
2392
- #[case::ratio_center(vec![Ratio(1, 2)], vec![25..75], Flex::Center)]
2393
- #[case::percent_legacy(vec![Percentage(50)], vec![0..100], Flex::Legacy)]
2394
- #[case::percent_start(vec![Percentage(50)], vec![0..50], Flex::Start)]
2395
- #[case::percent_end(vec![Percentage(50)], vec![50..100], Flex::End)]
2396
- #[case::percent_center(vec![Percentage(50)], vec![25..75], Flex::Center)]
2397
- #[case::min_legacy(vec![Min(50)], vec![0..100], Flex::Legacy)]
2398
- #[case::min_start(vec![Min(50)], vec![0..100], Flex::Start)]
2399
- #[case::min_end(vec![Min(50)], vec![0..100], Flex::End)]
2400
- #[case::min_center(vec![Min(50)], vec![0..100], Flex::Center)]
2401
- #[case::max_legacy(vec![Max(50)], vec![0..100], Flex::Legacy)]
2402
- #[case::max_start(vec![Max(50)], vec![0..50], Flex::Start)]
2403
- #[case::max_end(vec![Max(50)], vec![50..100], Flex::End)]
2404
- #[case::max_center(vec![Max(50)], vec![25..75], Flex::Center)]
2405
- #[case::spacebetween_becomes_stretch1(vec![Min(1)], vec![0..100], Flex::SpaceBetween)]
2406
- #[case::spacebetween_becomes_stretch2(vec![Max(20)], vec![0..100], Flex::SpaceBetween)]
2407
- #[case::spacebetween_becomes_stretch3(vec![Length(20)], vec![0..100], Flex::SpaceBetween)]
2408
- #[case::length_legacy2(vec![Length(25), Length(25)], vec![0..25, 25..100], Flex::Legacy)]
2409
- #[case::length_start2(vec![Length(25), Length(25)], vec![0..25, 25..50], Flex::Start)]
2410
- #[case::length_center2(vec![Length(25), Length(25)], vec![25..50, 50..75], Flex::Center)]
2411
- #[case::length_end2(vec![Length(25), Length(25)], vec![50..75, 75..100], Flex::End)]
2412
- #[case::length_spacebetween(vec![Length(25), Length(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2413
- #[case::length_spaceevenly(vec![Length(25), Length(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2414
- #[case::length_spacearound(vec![Length(25), Length(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2415
- #[case::percentage_legacy(vec![Percentage(25), Percentage(25)], vec![0..25, 25..100], Flex::Legacy)]
2416
- #[case::percentage_start(vec![Percentage(25), Percentage(25)], vec![0..25, 25..50], Flex::Start)]
2417
- #[case::percentage_center(vec![Percentage(25), Percentage(25)], vec![25..50, 50..75], Flex::Center)]
2418
- #[case::percentage_end(vec![Percentage(25), Percentage(25)], vec![50..75, 75..100], Flex::End)]
2419
- #[case::percentage_spacebetween(vec![Percentage(25), Percentage(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2420
- #[case::percentage_spaceevenly(vec![Percentage(25), Percentage(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2421
- #[case::percentage_spacearound(vec![Percentage(25), Percentage(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2422
- #[case::min_legacy2(vec![Min(25), Min(25)], vec![0..25, 25..100], Flex::Legacy)]
2423
- #[case::min_start2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Start)]
2424
- #[case::min_center2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::Center)]
2425
- #[case::min_end2(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::End)]
2426
- #[case::min_spacebetween(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceBetween)]
2427
- #[case::min_spaceevenly(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceEvenly)]
2428
- #[case::min_spacearound(vec![Min(25), Min(25)], vec![0..50, 50..100], Flex::SpaceAround)]
2429
- #[case::max_legacy2(vec![Max(25), Max(25)], vec![0..25, 25..100], Flex::Legacy)]
2430
- #[case::max_start2(vec![Max(25), Max(25)], vec![0..25, 25..50], Flex::Start)]
2431
- #[case::max_center2(vec![Max(25), Max(25)], vec![25..50, 50..75], Flex::Center)]
2432
- #[case::max_end2(vec![Max(25), Max(25)], vec![50..75, 75..100], Flex::End)]
2433
- #[case::max_spacebetween(vec![Max(25), Max(25)], vec![0..25, 75..100], Flex::SpaceBetween)]
2434
- #[case::max_spaceevenly(vec![Max(25), Max(25)], vec![17..42, 58..83], Flex::SpaceEvenly)]
2435
- #[case::max_spacearound(vec![Max(25), Max(25)], vec![13..38, 63..88], Flex::SpaceAround)]
2436
- #[case::length_spaced_around(vec![Length(25), Length(25), Length(25)], vec![0..25, 38..63, 75..100], Flex::SpaceBetween)]
2437
- #[case::one_segment_legacy(vec![Length(50)], vec![0..100], Flex::Legacy)]
2438
- #[case::one_segment_start(vec![Length(50)], vec![0..50], Flex::Start)]
2439
- #[case::one_segment_end(vec![Length(50)], vec![50..100], Flex::End)]
2440
- #[case::one_segment_center(vec![Length(50)], vec![25..75], Flex::Center)]
2441
- #[case::one_segment_spacebetween(vec![Length(50)], vec![0..100], Flex::SpaceBetween)]
2442
- #[case::one_segment_spaceevenly(vec![Length(50)], vec![25..75], Flex::SpaceEvenly)]
2443
- #[case::one_segment_spacearound(vec![Length(50)], vec![25..75], Flex::SpaceAround)]
2444
- fn flex_constraint(
2445
- #[case] constraints: Vec<Constraint>,
2446
- #[case] expected: Vec<Range<u16>>,
2447
- #[case] flex: Flex,
2448
- ) {
2449
- let rect = Rect::new(0, 0, 100, 1);
2450
- let ranges = Layout::horizontal(constraints)
2451
- .flex(flex)
2452
- .split(rect)
2453
- .iter()
2454
- .map(|r| r.left()..r.right())
2455
- .collect_vec();
2456
- assert_eq!(ranges, expected);
2457
- }
2458
-
2459
- #[rstest]
2460
- #[case::length_overlap1(vec![(0 , 20) , (20 , 20) , (40 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , 0)]
2461
- #[case::length_overlap2(vec![(0 , 20) , (19 , 20) , (38 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Start , -1)]
2462
- #[case::length_overlap3(vec![(21 , 20) , (40 , 20) , (59 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Center , -1)]
2463
- #[case::length_overlap4(vec![(42 , 20) , (61 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::End , -1)]
2464
- #[case::length_overlap5(vec![(0 , 20) , (19 , 20) , (38 , 62)] , vec![Length(20) , Length(20) , Length(20)] , Flex::Legacy , -1)]
2465
- #[case::length_overlap6(vec![(0 , 20) , (40 , 20) , (80 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceBetween , -1)]
2466
- #[case::length_overlap7(vec![(10 , 20) , (40 , 20) , (70 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceEvenly , -1)]
2467
- #[case::length_overlap7(vec![(7 , 20) , (40 , 20) , (73 , 20)] , vec![Length(20) , Length(20) , Length(20)] , Flex::SpaceAround , -1)]
2468
- fn flex_overlap(
2469
- #[case] expected: Vec<(u16, u16)>,
2470
- #[case] constraints: Vec<Constraint>,
2471
- #[case] flex: Flex,
2472
- #[case] spacing: i16,
2473
- ) {
2474
- let rect = Rect::new(0, 0, 100, 1);
2475
- let r = Layout::horizontal(constraints)
2476
- .flex(flex)
2477
- .spacing(spacing)
2478
- .split(rect);
2479
- let result = r
2480
- .iter()
2481
- .map(|r| (r.x, r.width))
2482
- .collect::<Vec<(u16, u16)>>();
2483
-
2484
- assert_eq!(result, expected);
2485
- }
2486
-
2487
- #[rstest]
2488
- #[case::length_spacing(vec![(0 , 20), (20, 20) , (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 0)]
2489
- #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start , 2)]
2490
- #[case::length_spacing(vec![(18, 20), (40, 20) , (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center , 2)]
2491
- #[case::length_spacing(vec![(36, 20), (58, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End , 2)]
2492
- #[case::length_spacing(vec![(0 , 20), (22, 20) , (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy , 2)]
2493
- #[case::length_spacing(vec![(0 , 20), (40, 20) , (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceBetween, 2)]
2494
- #[case::length_spacing(vec![(10, 20), (40, 20) , (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceEvenly, 2)]
2495
- #[case::length_spacing(vec![(7, 20), (40, 20) , (73, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2496
- fn flex_spacing(
2497
- #[case] expected: Vec<(u16, u16)>,
2498
- #[case] constraints: Vec<Constraint>,
2499
- #[case] flex: Flex,
2500
- #[case] spacing: i16,
2501
- ) {
2502
- let rect = Rect::new(0, 0, 100, 1);
2503
- let r = Layout::horizontal(constraints)
2504
- .flex(flex)
2505
- .spacing(spacing)
2506
- .split(rect);
2507
- let result = r
2508
- .iter()
2509
- .map(|r| (r.x, r.width))
2510
- .collect::<Vec<(u16, u16)>>();
2511
- assert_eq!(result, expected);
2512
- }
2513
-
2514
- #[rstest]
2515
- #[case::a(vec![(0, 25), (25, 75)], vec![Length(25), Length(25)])]
2516
- #[case::b(vec![(0, 25), (25, 75)], vec![Length(25), Percentage(25)])]
2517
- #[case::c(vec![(0, 75), (75, 25)], vec![Percentage(25), Length(25)])]
2518
- #[case::d(vec![(0, 75), (75, 25)], vec![Min(25), Percentage(25)])]
2519
- #[case::e(vec![(0, 25), (25, 75)], vec![Percentage(25), Min(25)])]
2520
- #[case::f(vec![(0, 25), (25, 75)], vec![Min(25), Percentage(100)])]
2521
- #[case::g(vec![(0, 75), (75, 25)], vec![Percentage(100), Min(25)])]
2522
- #[case::h(vec![(0, 25), (25, 75)], vec![Max(75), Percentage(75)])]
2523
- #[case::i(vec![(0, 75), (75, 25)], vec![Percentage(75), Max(75)])]
2524
- #[case::j(vec![(0, 25), (25, 75)], vec![Max(25), Percentage(25)])]
2525
- #[case::k(vec![(0, 75), (75, 25)], vec![Percentage(25), Max(25)])]
2526
- #[case::l(vec![(0, 25), (25, 75)], vec![Length(25), Ratio(1, 4)])]
2527
- #[case::m(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Length(25)])]
2528
- #[case::n(vec![(0, 25), (25, 75)], vec![Percentage(25), Ratio(1, 4)])]
2529
- #[case::o(vec![(0, 75), (75, 25)], vec![Ratio(1, 4), Percentage(25)])]
2530
- #[case::p(vec![(0, 25), (25, 75)], vec![Ratio(1, 4), Fill(25)])]
2531
- #[case::q(vec![(0, 75), (75, 25)], vec![Fill(25), Ratio(1, 4)])]
2532
- fn constraint_specification_tests_for_priority(
2533
- #[case] expected: Vec<(u16, u16)>,
2534
- #[case] constraints: Vec<Constraint>,
2535
- ) {
2536
- let rect = Rect::new(0, 0, 100, 1);
2537
- let r = Layout::horizontal(constraints)
2538
- .flex(Flex::Legacy)
2539
- .split(rect)
2540
- .iter()
2541
- .map(|r| (r.x, r.width))
2542
- .collect::<Vec<(u16, u16)>>();
2543
- assert_eq!(r, expected);
2544
- }
2545
-
2546
- #[rstest]
2547
- #[case::a(vec![(0, 20), (20, 20), (40, 20)], vec![Length(20), Length(20), Length(20)], Flex::Start, 0)]
2548
- #[case::b(vec![(18, 20), (40, 20), (62, 20)], vec![Length(20), Length(20), Length(20)], Flex::Center, 2)]
2549
- #[case::c(vec![(36, 20), (58, 20), (80, 20)], vec![Length(20), Length(20), Length(20)], Flex::End, 2)]
2550
- #[case::d(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2551
- #[case::e(vec![(0, 20), (22, 20), (44, 56)], vec![Length(20), Length(20), Length(20)], Flex::Legacy, 2)]
2552
- #[case::f(vec![(10, 20), (40, 20), (70, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceEvenly, 2)]
2553
- #[case::f(vec![(7, 20), (40, 20), (73, 20)], vec![Length(20), Length(20), Length(20)], Flex::SpaceAround, 2)]
2554
- fn constraint_specification_tests_for_priority_with_spacing(
2555
- #[case] expected: Vec<(u16, u16)>,
2556
- #[case] constraints: Vec<Constraint>,
2557
- #[case] flex: Flex,
2558
- #[case] spacing: i16,
2559
- ) {
2560
- let rect = Rect::new(0, 0, 100, 1);
2561
- let r = Layout::horizontal(constraints)
2562
- .spacing(spacing)
2563
- .flex(flex)
2564
- .split(rect)
2565
- .iter()
2566
- .map(|r| (r.x, r.width))
2567
- .collect::<Vec<(u16, u16)>>();
2568
- assert_eq!(r, expected);
2569
- }
2570
-
2571
- #[rstest]
2572
- #[case::prop(vec![(0 , 10), (10, 80), (90 , 10)] , vec![Length(10), Fill(1), Length(10)], Flex::Legacy)]
2573
- #[case::flex(vec![(0 , 10), (90 , 10)] , vec![Length(10), Length(10)], Flex::SpaceBetween)]
2574
- #[case::prop(vec![(0 , 27), (27, 10), (37, 26), (63, 10), (73, 27)] , vec![Fill(1), Length(10), Fill(1), Length(10), Fill(1)], Flex::Legacy)]
2575
- #[case::flex(vec![(27 , 10), (63, 10)] , vec![Length(10), Length(10)], Flex::SpaceEvenly)]
2576
- #[case::prop(vec![(0 , 10), (10, 10), (20 , 80)] , vec![Length(10), Length(10), Fill(1)], Flex::Legacy)]
2577
- #[case::flex(vec![(0 , 10), (10, 10)] , vec![Length(10), Length(10)], Flex::Start)]
2578
- #[case::prop(vec![(0 , 80), (80 , 10), (90, 10)] , vec![Fill(1), Length(10), Length(10)], Flex::Legacy)]
2579
- #[case::flex(vec![(80 , 10), (90, 10)] , vec![Length(10), Length(10)], Flex::End)]
2580
- #[case::prop(vec![(0 , 40), (40, 10), (50, 10), (60, 40)] , vec![Fill(1), Length(10), Length(10), Fill(1)], Flex::Legacy)]
2581
- #[case::flex(vec![(40 , 10), (50, 10)] , vec![Length(10), Length(10)], Flex::Center)]
2582
- #[case::flex(vec![(20 , 10), (70, 10)] , vec![Length(10), Length(10)], Flex::SpaceAround)]
2583
- fn fill_vs_flex(
2584
- #[case] expected: Vec<(u16, u16)>,
2585
- #[case] constraints: Vec<Constraint>,
2586
- #[case] flex: Flex,
2587
- ) {
2588
- let rect = Rect::new(0, 0, 100, 1);
2589
- let r = Layout::horizontal(constraints).flex(flex).split(rect);
2590
- let result = r
2591
- .iter()
2592
- .map(|r| (r.x, r.width))
2593
- .collect::<Vec<(u16, u16)>>();
2594
- assert_eq!(result, expected);
2595
- }
2596
-
2597
- #[rstest]
2598
- #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , 0)]
2599
- #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , 0)]
2600
- #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 0)]
2601
- #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 0)]
2602
- #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , 0)]
2603
- #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , 0)]
2604
- #[case::flex0(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , 0)]
2605
- #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Legacy , 10)]
2606
- #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Start , 10)]
2607
- #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::Center , 10)]
2608
- #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::End , 10)]
2609
- #[case::flex10(vec![(10 , 35), (55 , 35)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , 10)]
2610
- #[case::flex10(vec![(0 , 45), (55 , 45)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , 10)]
2611
- #[case::flex10(vec![(10 , 30), (60 , 30)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , 10)]
2612
- #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 0)]
2613
- #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , 0)]
2614
- #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 0)]
2615
- #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 0)]
2616
- #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 0)]
2617
- #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 0)]
2618
- #[case::flex_length0(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 0)]
2619
- #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , 10)]
2620
- #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , 10)]
2621
- #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , 10)]
2622
- #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , 10)]
2623
- #[case::flex_length10(vec![(10 , 25), (45, 10), (65 , 25)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , 10)]
2624
- #[case::flex_length10(vec![(0 , 35), (45, 10), (65 , 35)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , 10)]
2625
- #[case::flex_length10(vec![(10 , 15), (45, 10), (75 , 15)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , 10)]
2626
- fn fill_spacing(
2627
- #[case] expected: Vec<(u16, u16)>,
2628
- #[case] constraints: Vec<Constraint>,
2629
- #[case] flex: Flex,
2630
- #[case] spacing: i16,
2631
- ) {
2632
- let rect = Rect::new(0, 0, 100, 1);
2633
- let r = Layout::horizontal(constraints)
2634
- .flex(flex)
2635
- .spacing(spacing)
2636
- .split(rect);
2637
- let result = r
2638
- .iter()
2639
- .map(|r| (r.x, r.width))
2640
- .collect::<Vec<(u16, u16)>>();
2641
- assert_eq!(expected, result);
2642
- }
2643
-
2644
- #[rstest]
2645
- #[case::flex0_1(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Legacy , -10)]
2646
- #[case::flex0_2(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -10)]
2647
- #[case::flex0_3(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -10)]
2648
- #[case::flex0_4(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Start , -10)]
2649
- #[case::flex0_5(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::Center , -10)]
2650
- #[case::flex0_6(vec![(0 , 55), (45 , 55)] , vec![Fill(1), Fill(1)], Flex::End , -10)]
2651
- #[case::flex0_7(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , -10)]
2652
- #[case::flex10_1(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Legacy , -1)]
2653
- #[case::flex10_2(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Start , -1)]
2654
- #[case::flex10_3(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::Center , -1)]
2655
- #[case::flex10_4(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::End , -1)]
2656
- #[case::flex10_5(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceAround , -1)]
2657
- #[case::flex10_6(vec![(0 , 51), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceBetween , -1)]
2658
- #[case::flex10_7(vec![(0 , 50), (50 , 50)] , vec![Fill(1), Fill(1)], Flex::SpaceEvenly , -1)]
2659
- #[case::flex_length0_1(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -10)]
2660
- #[case::flex_length0_2(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -10)]
2661
- #[case::flex_length0_3(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -10)]
2662
- #[case::flex_length0_4(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -10)]
2663
- #[case::flex_length0_5(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -10)]
2664
- #[case::flex_length0_6(vec![(0 , 55), (45, 10), (45 , 55)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -10)]
2665
- #[case::flex_length0_7(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , -10)]
2666
- #[case::flex_length10_1(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Legacy , -1)]
2667
- #[case::flex_length10_2(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Start , -1)]
2668
- #[case::flex_length10_3(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::Center , -1)]
2669
- #[case::flex_length10_4(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::End , -1)]
2670
- #[case::flex_length10_5(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceAround , -1)]
2671
- #[case::flex_length10_6(vec![(0 , 46), (45, 10), (54 , 46)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceBetween , -1)]
2672
- #[case::flex_length10_7(vec![(0 , 45), (45, 10), (55 , 45)] , vec![Fill(1), Length(10), Fill(1)], Flex::SpaceEvenly , -1)]
2673
- fn fill_overlap(
2674
- #[case] expected: Vec<(u16, u16)>,
2675
- #[case] constraints: Vec<Constraint>,
2676
- #[case] flex: Flex,
2677
- #[case] spacing: i16,
2678
- ) {
2679
- let rect = Rect::new(0, 0, 100, 1);
2680
- let r = Layout::horizontal(constraints)
2681
- .flex(flex)
2682
- .spacing(spacing)
2683
- .split(rect);
2684
- let result = r
2685
- .iter()
2686
- .map(|r| (r.x, r.width))
2687
- .collect::<Vec<(u16, u16)>>();
2688
- assert_eq!(result, expected);
2689
- }
2690
-
2691
- #[rstest]
2692
- #[case::flex_length10(vec![(0, 10), (90, 10)], vec![Length(10), Length(10)], Flex::Center, 80)]
2693
- fn flex_spacing_lower_priority_than_user_spacing(
2694
- #[case] expected: Vec<(u16, u16)>,
2695
- #[case] constraints: Vec<Constraint>,
2696
- #[case] flex: Flex,
2697
- #[case] spacing: i16,
2698
- ) {
2699
- let rect = Rect::new(0, 0, 100, 1);
2700
- let r = Layout::horizontal(constraints)
2701
- .flex(flex)
2702
- .spacing(spacing)
2703
- .split(rect);
2704
- let result = r
2705
- .iter()
2706
- .map(|r| (r.x, r.width))
2707
- .collect::<Vec<(u16, u16)>>();
2708
- assert_eq!(result, expected);
2709
- }
2710
-
2711
- #[rstest]
2712
- #[case::spacers(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy)]
2713
- #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween)]
2714
- #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly)]
2715
- #[case::spacers(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround)]
2716
- #[case::spacers(vec![(0, 0), (10, 0), (20, 80)], vec![Length(10), Length(10)], Flex::Start)]
2717
- #[case::spacers(vec![(0, 40), (50, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center)]
2718
- #[case::spacers(vec![(0, 80), (90, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End)]
2719
- fn split_with_spacers_no_spacing(
2720
- #[case] expected: Vec<(u16, u16)>,
2721
- #[case] constraints: Vec<Constraint>,
2722
- #[case] flex: Flex,
2723
- ) {
2724
- let rect = Rect::new(0, 0, 100, 1);
2725
- let (_, s) = Layout::horizontal(&constraints)
2726
- .flex(flex)
2727
- .split_with_spacers(rect);
2728
- assert_eq!(s.len(), constraints.len() + 1);
2729
- let result = s
2730
- .iter()
2731
- .map(|r| (r.x, r.width))
2732
- .collect::<Vec<(u16, u16)>>();
2733
- assert_eq!(result, expected);
2734
- }
2735
-
2736
- #[rstest]
2737
- #[case::spacers(vec![(0, 0), (10, 5), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 5)]
2738
- #[case::spacers(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 5)]
2739
- #[case::spacers(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly, 5)]
2740
- #[case::spacers(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround, 5)]
2741
- #[case::spacers(vec![(0, 0), (10, 5), (25, 75)], vec![Length(10), Length(10)], Flex::Start, 5)]
2742
- #[case::spacers(vec![(0, 38), (48, 5), (63, 37)], vec![Length(10), Length(10)], Flex::Center, 5)]
2743
- #[case::spacers(vec![(0, 75), (85, 5), (100, 0)], vec![Length(10), Length(10)], Flex::End, 5)]
2744
- fn split_with_spacers_and_spacing(
2745
- #[case] expected: Vec<(u16, u16)>,
2746
- #[case] constraints: Vec<Constraint>,
2747
- #[case] flex: Flex,
2748
- #[case] spacing: i16,
2749
- ) {
2750
- let rect = Rect::new(0, 0, 100, 1);
2751
- let (_, s) = Layout::horizontal(&constraints)
2752
- .flex(flex)
2753
- .spacing(spacing)
2754
- .split_with_spacers(rect);
2755
- assert_eq!(s.len(), constraints.len() + 1);
2756
- let result = s
2757
- .iter()
2758
- .map(|r| (r.x, r.width))
2759
- .collect::<Vec<(u16, u16)>>();
2760
- assert_eq!(expected, result);
2761
- }
2762
-
2763
- #[rstest]
2764
- #[case::spacers_1(vec![(0, 0), (10, 0), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, -1)]
2765
- #[case::spacers_2(vec![(0, 0), (10, 80), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, -1)]
2766
- #[case::spacers_3(vec![(0, 27), (37, 26), (73, 27)], vec![Length(10), Length(10)], Flex::SpaceEvenly, -1)]
2767
- #[case::spacers_3(vec![(0, 20), (30, 40), (80, 20)], vec![Length(10), Length(10)], Flex::SpaceAround, -1)]
2768
- #[case::spacers_4(vec![(0, 0), (10, 0), (19, 81)], vec![Length(10), Length(10)], Flex::Start, -1)]
2769
- #[case::spacers_5(vec![(0, 41), (51, 0), (60, 40)], vec![Length(10), Length(10)], Flex::Center, -1)]
2770
- #[case::spacers_6(vec![(0, 81), (91, 0), (100, 0)], vec![Length(10), Length(10)], Flex::End, -1)]
2771
- fn split_with_spacers_and_overlap(
2772
- #[case] expected: Vec<(u16, u16)>,
2773
- #[case] constraints: Vec<Constraint>,
2774
- #[case] flex: Flex,
2775
- #[case] spacing: i16,
2776
- ) {
2777
- let rect = Rect::new(0, 0, 100, 1);
2778
- let (_, s) = Layout::horizontal(&constraints)
2779
- .flex(flex)
2780
- .spacing(spacing)
2781
- .split_with_spacers(rect);
2782
- assert_eq!(s.len(), constraints.len() + 1);
2783
- let result = s
2784
- .iter()
2785
- .map(|r| (r.x, r.width))
2786
- .collect::<Vec<(u16, u16)>>();
2787
- assert_eq!(result, expected);
2788
- }
2789
-
2790
- #[rstest]
2791
- #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Legacy, 200)]
2792
- #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::SpaceBetween, 200)]
2793
- #[case::spacers(vec![(0, 33), (33, 34), (67, 33)], vec![Length(10), Length(10)], Flex::SpaceEvenly, 200)]
2794
- #[case::spacers(vec![(0, 25), (25, 50), (75, 25)], vec![Length(10), Length(10)], Flex::SpaceAround, 200)]
2795
- #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Start, 200)]
2796
- #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::Center, 200)]
2797
- #[case::spacers(vec![(0, 0), (0, 100), (100, 0)], vec![Length(10), Length(10)], Flex::End, 200)]
2798
- fn split_with_spacers_and_too_much_spacing(
2799
- #[case] expected: Vec<(u16, u16)>,
2800
- #[case] constraints: Vec<Constraint>,
2801
- #[case] flex: Flex,
2802
- #[case] spacing: i16,
2803
- ) {
2804
- let rect = Rect::new(0, 0, 100, 1);
2805
- let (_, s) = Layout::horizontal(&constraints)
2806
- .flex(flex)
2807
- .spacing(spacing)
2808
- .split_with_spacers(rect);
2809
- assert_eq!(s.len(), constraints.len() + 1);
2810
- let result = s
2811
- .iter()
2812
- .map(|r| (r.x, r.width))
2813
- .collect::<Vec<(u16, u16)>>();
2814
- assert_eq!(result, expected);
2815
- }
2816
-
2817
- #[rstest]
2818
- #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Legacy)]
2819
- #[case::compare(vec![(0, 90), (90, 10)], vec![Min(10), Length(10)], Flex::Start)]
2820
- #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Legacy)]
2821
- #[case::compare(vec![(0, 10), (10, 90)], vec![Min(10), Percentage(100)], Flex::Start)]
2822
- #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Legacy)]
2823
- #[case::compare(vec![(0, 50), (50, 50)], vec![Percentage(50), Percentage(50)], Flex::Start)]
2824
- fn legacy_vs_default(
2825
- #[case] expected: Vec<(u16, u16)>,
2826
- #[case] constraints: Vec<Constraint>,
2827
- #[case] flex: Flex,
2828
- ) {
2829
- let rect = Rect::new(0, 0, 100, 1);
2830
- let r = Layout::horizontal(constraints).flex(flex).split(rect);
2831
- let result = r
2832
- .iter()
2833
- .map(|r| (r.x, r.width))
2834
- .collect::<Vec<(u16, u16)>>();
2835
- assert_eq!(result, expected);
2836
- }
2837
- }
2838
- }