1#[must_use = "ContainerBuilder does nothing until .col(), .row(), .line(), or .draw() is called"]
22pub struct ContainerBuilder<'a> {
23 ctx: &'a mut Context,
24 gap: u32,
25 row_gap: Option<u32>,
26 col_gap: Option<u32>,
27 align: Align,
28 align_self_value: Option<Align>,
29 justify: Justify,
30 border: Option<Border>,
31 border_sides: BorderSides,
32 border_style: Style,
33 bg: Option<Color>,
34 text_color: Option<Color>,
35 dark_bg: Option<Color>,
36 dark_border_style: Option<Style>,
37 group_hover_bg: Option<Color>,
38 group_hover_border_style: Option<Style>,
39 group_name: Option<String>,
40 padding: Padding,
41 margin: Margin,
42 constraints: Constraints,
43 title: Option<(String, Style)>,
44 grow: u16,
45 scroll_offset: Option<u32>,
46}
47
48#[derive(Debug, Clone, Copy)]
55struct CanvasPixel {
56 bits: u32,
57 color: Color,
58}
59
60#[derive(Debug, Clone)]
62struct CanvasLabel {
63 x: usize,
64 y: usize,
65 text: String,
66 color: Color,
67}
68
69#[derive(Debug, Clone)]
71struct CanvasLayer {
72 grid: Vec<Vec<CanvasPixel>>,
73 labels: Vec<CanvasLabel>,
74}
75
76pub struct CanvasContext {
78 layers: Vec<CanvasLayer>,
79 cols: usize,
80 rows: usize,
81 px_w: usize,
82 px_h: usize,
83 current_color: Color,
84}
85
86impl CanvasContext {
87 fn new(cols: usize, rows: usize) -> Self {
88 Self {
89 layers: vec![Self::new_layer(cols, rows)],
90 cols,
91 rows,
92 px_w: cols * 2,
93 px_h: rows * 4,
94 current_color: Color::Reset,
95 }
96 }
97
98 fn new_layer(cols: usize, rows: usize) -> CanvasLayer {
99 CanvasLayer {
100 grid: vec![
101 vec![
102 CanvasPixel {
103 bits: 0,
104 color: Color::Reset,
105 };
106 cols
107 ];
108 rows
109 ],
110 labels: Vec::new(),
111 }
112 }
113
114 fn current_layer_mut(&mut self) -> Option<&mut CanvasLayer> {
115 self.layers.last_mut()
116 }
117
118 fn dot_with_color(&mut self, x: usize, y: usize, color: Color) {
119 if x >= self.px_w || y >= self.px_h {
120 return;
121 }
122
123 let char_col = x / 2;
124 let char_row = y / 4;
125 let sub_col = x % 2;
126 let sub_row = y % 4;
127 const LEFT_BITS: [u32; 4] = [0x01, 0x02, 0x04, 0x40];
128 const RIGHT_BITS: [u32; 4] = [0x08, 0x10, 0x20, 0x80];
129
130 let bit = if sub_col == 0 {
131 LEFT_BITS[sub_row]
132 } else {
133 RIGHT_BITS[sub_row]
134 };
135
136 if let Some(layer) = self.current_layer_mut() {
137 let cell = &mut layer.grid[char_row][char_col];
138 let new_bits = cell.bits | bit;
139 if new_bits != cell.bits {
140 cell.bits = new_bits;
141 cell.color = color;
142 }
143 }
144 }
145
146 fn dot_isize(&mut self, x: isize, y: isize) {
147 if x >= 0 && y >= 0 {
148 self.dot(x as usize, y as usize);
149 }
150 }
151
152 pub fn width(&self) -> usize {
154 self.px_w
155 }
156
157 pub fn height(&self) -> usize {
159 self.px_h
160 }
161
162 pub fn dot(&mut self, x: usize, y: usize) {
164 self.dot_with_color(x, y, self.current_color);
165 }
166
167 pub fn line(&mut self, x0: usize, y0: usize, x1: usize, y1: usize) {
169 let (mut x, mut y) = (x0 as isize, y0 as isize);
170 let (x1, y1) = (x1 as isize, y1 as isize);
171 let dx = (x1 - x).abs();
172 let dy = -(y1 - y).abs();
173 let sx = if x < x1 { 1 } else { -1 };
174 let sy = if y < y1 { 1 } else { -1 };
175 let mut err = dx + dy;
176
177 loop {
178 self.dot_isize(x, y);
179 if x == x1 && y == y1 {
180 break;
181 }
182 let e2 = 2 * err;
183 if e2 >= dy {
184 err += dy;
185 x += sx;
186 }
187 if e2 <= dx {
188 err += dx;
189 y += sy;
190 }
191 }
192 }
193
194 pub fn rect(&mut self, x: usize, y: usize, w: usize, h: usize) {
196 if w == 0 || h == 0 {
197 return;
198 }
199
200 self.line(x, y, x + w.saturating_sub(1), y);
201 self.line(
202 x + w.saturating_sub(1),
203 y,
204 x + w.saturating_sub(1),
205 y + h.saturating_sub(1),
206 );
207 self.line(
208 x + w.saturating_sub(1),
209 y + h.saturating_sub(1),
210 x,
211 y + h.saturating_sub(1),
212 );
213 self.line(x, y + h.saturating_sub(1), x, y);
214 }
215
216 pub fn circle(&mut self, cx: usize, cy: usize, r: usize) {
218 let mut x = r as isize;
219 let mut y: isize = 0;
220 let mut err: isize = 1 - x;
221 let (cx, cy) = (cx as isize, cy as isize);
222
223 while x >= y {
224 for &(dx, dy) in &[
225 (x, y),
226 (y, x),
227 (-x, y),
228 (-y, x),
229 (x, -y),
230 (y, -x),
231 (-x, -y),
232 (-y, -x),
233 ] {
234 let px = cx + dx;
235 let py = cy + dy;
236 self.dot_isize(px, py);
237 }
238
239 y += 1;
240 if err < 0 {
241 err += 2 * y + 1;
242 } else {
243 x -= 1;
244 err += 2 * (y - x) + 1;
245 }
246 }
247 }
248
249 pub fn set_color(&mut self, color: Color) {
251 self.current_color = color;
252 }
253
254 pub fn color(&self) -> Color {
256 self.current_color
257 }
258
259 pub fn filled_rect(&mut self, x: usize, y: usize, w: usize, h: usize) {
261 if w == 0 || h == 0 {
262 return;
263 }
264
265 let x_end = x.saturating_add(w).min(self.px_w);
266 let y_end = y.saturating_add(h).min(self.px_h);
267 if x >= x_end || y >= y_end {
268 return;
269 }
270
271 for yy in y..y_end {
272 self.line(x, yy, x_end.saturating_sub(1), yy);
273 }
274 }
275
276 pub fn filled_circle(&mut self, cx: usize, cy: usize, r: usize) {
278 let (cx, cy, r) = (cx as isize, cy as isize, r as isize);
279 for y in (cy - r)..=(cy + r) {
280 let dy = y - cy;
281 let span_sq = (r * r - dy * dy).max(0);
282 let dx = (span_sq as f64).sqrt() as isize;
283 for x in (cx - dx)..=(cx + dx) {
284 self.dot_isize(x, y);
285 }
286 }
287 }
288
289 pub fn triangle(&mut self, x0: usize, y0: usize, x1: usize, y1: usize, x2: usize, y2: usize) {
291 self.line(x0, y0, x1, y1);
292 self.line(x1, y1, x2, y2);
293 self.line(x2, y2, x0, y0);
294 }
295
296 pub fn filled_triangle(
298 &mut self,
299 x0: usize,
300 y0: usize,
301 x1: usize,
302 y1: usize,
303 x2: usize,
304 y2: usize,
305 ) {
306 let vertices = [
307 (x0 as isize, y0 as isize),
308 (x1 as isize, y1 as isize),
309 (x2 as isize, y2 as isize),
310 ];
311 let min_y = vertices.iter().map(|(_, y)| *y).min().unwrap_or(0);
312 let max_y = vertices.iter().map(|(_, y)| *y).max().unwrap_or(-1);
313
314 for y in min_y..=max_y {
315 let mut intersections: Vec<f64> = Vec::new();
316
317 for edge in [(0usize, 1usize), (1usize, 2usize), (2usize, 0usize)] {
318 let (x_a, y_a) = vertices[edge.0];
319 let (x_b, y_b) = vertices[edge.1];
320 if y_a == y_b {
321 continue;
322 }
323
324 let (x_start, y_start, x_end, y_end) = if y_a < y_b {
325 (x_a, y_a, x_b, y_b)
326 } else {
327 (x_b, y_b, x_a, y_a)
328 };
329
330 if y < y_start || y >= y_end {
331 continue;
332 }
333
334 let t = (y - y_start) as f64 / (y_end - y_start) as f64;
335 intersections.push(x_start as f64 + t * (x_end - x_start) as f64);
336 }
337
338 intersections.sort_by(|a, b| a.total_cmp(b));
339 let mut i = 0usize;
340 while i + 1 < intersections.len() {
341 let x_start = intersections[i].ceil() as isize;
342 let x_end = intersections[i + 1].floor() as isize;
343 for x in x_start..=x_end {
344 self.dot_isize(x, y);
345 }
346 i += 2;
347 }
348 }
349
350 self.triangle(x0, y0, x1, y1, x2, y2);
351 }
352
353 pub fn points(&mut self, pts: &[(usize, usize)]) {
355 for &(x, y) in pts {
356 self.dot(x, y);
357 }
358 }
359
360 pub fn polyline(&mut self, pts: &[(usize, usize)]) {
362 for window in pts.windows(2) {
363 if let [(x0, y0), (x1, y1)] = window {
364 self.line(*x0, *y0, *x1, *y1);
365 }
366 }
367 }
368
369 pub fn print(&mut self, x: usize, y: usize, text: &str) {
372 if text.is_empty() {
373 return;
374 }
375
376 let color = self.current_color;
377 if let Some(layer) = self.current_layer_mut() {
378 layer.labels.push(CanvasLabel {
379 x,
380 y,
381 text: text.to_string(),
382 color,
383 });
384 }
385 }
386
387 pub fn layer(&mut self) {
389 self.layers.push(Self::new_layer(self.cols, self.rows));
390 }
391
392 pub(crate) fn render(&self) -> Vec<Vec<(String, Color)>> {
393 let mut final_grid = vec![
394 vec![
395 CanvasPixel {
396 bits: 0,
397 color: Color::Reset,
398 };
399 self.cols
400 ];
401 self.rows
402 ];
403 let mut labels_overlay: Vec<Vec<Option<(char, Color)>>> =
404 vec![vec![None; self.cols]; self.rows];
405
406 for layer in &self.layers {
407 for (row, final_row) in final_grid.iter_mut().enumerate().take(self.rows) {
408 for (col, dst) in final_row.iter_mut().enumerate().take(self.cols) {
409 let src = layer.grid[row][col];
410 if src.bits == 0 {
411 continue;
412 }
413
414 let merged = dst.bits | src.bits;
415 if merged != dst.bits {
416 dst.bits = merged;
417 dst.color = src.color;
418 }
419 }
420 }
421
422 for label in &layer.labels {
423 let row = label.y / 4;
424 if row >= self.rows {
425 continue;
426 }
427 let start_col = label.x / 2;
428 for (offset, ch) in label.text.chars().enumerate() {
429 let col = start_col + offset;
430 if col >= self.cols {
431 break;
432 }
433 labels_overlay[row][col] = Some((ch, label.color));
434 }
435 }
436 }
437
438 let mut lines: Vec<Vec<(String, Color)>> = Vec::with_capacity(self.rows);
439 for row in 0..self.rows {
440 let mut segments: Vec<(String, Color)> = Vec::new();
441 let mut current_color: Option<Color> = None;
442 let mut current_text = String::new();
443
444 for col in 0..self.cols {
445 let (ch, color) = if let Some((label_ch, label_color)) = labels_overlay[row][col] {
446 (label_ch, label_color)
447 } else {
448 let bits = final_grid[row][col].bits;
449 let ch = char::from_u32(0x2800 + bits).unwrap_or(' ');
450 (ch, final_grid[row][col].color)
451 };
452
453 match current_color {
454 Some(c) if c == color => {
455 current_text.push(ch);
456 }
457 Some(c) => {
458 segments.push((std::mem::take(&mut current_text), c));
459 current_text.push(ch);
460 current_color = Some(color);
461 }
462 None => {
463 current_text.push(ch);
464 current_color = Some(color);
465 }
466 }
467 }
468
469 if let Some(color) = current_color {
470 segments.push((current_text, color));
471 }
472 lines.push(segments);
473 }
474
475 lines
476 }
477}
478
479macro_rules! define_breakpoint_methods {
480 (
481 base = $base:ident,
482 arg = $arg:ident : $arg_ty:ty,
483 xs = $xs_fn:ident => [$( $xs_doc:literal ),* $(,)?],
484 sm = $sm_fn:ident => [$( $sm_doc:literal ),* $(,)?],
485 md = $md_fn:ident => [$( $md_doc:literal ),* $(,)?],
486 lg = $lg_fn:ident => [$( $lg_doc:literal ),* $(,)?],
487 xl = $xl_fn:ident => [$( $xl_doc:literal ),* $(,)?],
488 at = $at_fn:ident => [$( $at_doc:literal ),* $(,)?]
489 ) => {
490 $(#[doc = $xs_doc])*
491 pub fn $xs_fn(self, $arg: $arg_ty) -> Self {
492 if self.ctx.breakpoint() == Breakpoint::Xs {
493 self.$base($arg)
494 } else {
495 self
496 }
497 }
498
499 $(#[doc = $sm_doc])*
500 pub fn $sm_fn(self, $arg: $arg_ty) -> Self {
501 if self.ctx.breakpoint() == Breakpoint::Sm {
502 self.$base($arg)
503 } else {
504 self
505 }
506 }
507
508 $(#[doc = $md_doc])*
509 pub fn $md_fn(self, $arg: $arg_ty) -> Self {
510 if self.ctx.breakpoint() == Breakpoint::Md {
511 self.$base($arg)
512 } else {
513 self
514 }
515 }
516
517 $(#[doc = $lg_doc])*
518 pub fn $lg_fn(self, $arg: $arg_ty) -> Self {
519 if self.ctx.breakpoint() == Breakpoint::Lg {
520 self.$base($arg)
521 } else {
522 self
523 }
524 }
525
526 $(#[doc = $xl_doc])*
527 pub fn $xl_fn(self, $arg: $arg_ty) -> Self {
528 if self.ctx.breakpoint() == Breakpoint::Xl {
529 self.$base($arg)
530 } else {
531 self
532 }
533 }
534
535 $(#[doc = $at_doc])*
536 pub fn $at_fn(self, bp: Breakpoint, $arg: $arg_ty) -> Self {
537 if self.ctx.breakpoint() == bp {
538 self.$base($arg)
539 } else {
540 self
541 }
542 }
543 };
544}
545
546impl<'a> ContainerBuilder<'a> {
547 pub fn apply(mut self, style: &ContainerStyle) -> Self {
552 if let Some(v) = style.border {
553 self.border = Some(v);
554 }
555 if let Some(v) = style.border_sides {
556 self.border_sides = v;
557 }
558 if let Some(v) = style.border_style {
559 self.border_style = v;
560 }
561 if let Some(v) = style.bg {
562 self.bg = Some(v);
563 }
564 if let Some(v) = style.dark_bg {
565 self.dark_bg = Some(v);
566 }
567 if let Some(v) = style.dark_border_style {
568 self.dark_border_style = Some(v);
569 }
570 if let Some(v) = style.padding {
571 self.padding = v;
572 }
573 if let Some(v) = style.margin {
574 self.margin = v;
575 }
576 if let Some(v) = style.gap {
577 self.gap = v;
578 }
579 if let Some(v) = style.row_gap {
580 self.row_gap = Some(v);
581 }
582 if let Some(v) = style.col_gap {
583 self.col_gap = Some(v);
584 }
585 if let Some(v) = style.grow {
586 self.grow = v;
587 }
588 if let Some(v) = style.align {
589 self.align = v;
590 }
591 if let Some(v) = style.align_self {
592 self.align_self_value = Some(v);
593 }
594 if let Some(v) = style.justify {
595 self.justify = v;
596 }
597 if let Some(v) = style.text_color {
598 self.text_color = Some(v);
599 }
600 if let Some(w) = style.w {
601 self.constraints.min_width = Some(w);
602 self.constraints.max_width = Some(w);
603 }
604 if let Some(h) = style.h {
605 self.constraints.min_height = Some(h);
606 self.constraints.max_height = Some(h);
607 }
608 if let Some(v) = style.min_w {
609 self.constraints.min_width = Some(v);
610 }
611 if let Some(v) = style.max_w {
612 self.constraints.max_width = Some(v);
613 }
614 if let Some(v) = style.min_h {
615 self.constraints.min_height = Some(v);
616 }
617 if let Some(v) = style.max_h {
618 self.constraints.max_height = Some(v);
619 }
620 if let Some(v) = style.w_pct {
621 self.constraints.width_pct = Some(v);
622 }
623 if let Some(v) = style.h_pct {
624 self.constraints.height_pct = Some(v);
625 }
626 self
627 }
628
629 pub fn border(mut self, border: Border) -> Self {
631 self.border = Some(border);
632 self
633 }
634
635 pub fn border_top(mut self, show: bool) -> Self {
637 self.border_sides.top = show;
638 self
639 }
640
641 pub fn border_right(mut self, show: bool) -> Self {
643 self.border_sides.right = show;
644 self
645 }
646
647 pub fn border_bottom(mut self, show: bool) -> Self {
649 self.border_sides.bottom = show;
650 self
651 }
652
653 pub fn border_left(mut self, show: bool) -> Self {
655 self.border_sides.left = show;
656 self
657 }
658
659 pub fn border_sides(mut self, sides: BorderSides) -> Self {
661 self.border_sides = sides;
662 self
663 }
664
665 pub fn border_x(self) -> Self {
667 self.border_sides(BorderSides {
668 top: false,
669 right: true,
670 bottom: false,
671 left: true,
672 })
673 }
674
675 pub fn border_y(self) -> Self {
677 self.border_sides(BorderSides {
678 top: true,
679 right: false,
680 bottom: true,
681 left: false,
682 })
683 }
684
685 pub fn rounded(self) -> Self {
687 self.border(Border::Rounded)
688 }
689
690 pub fn border_style(mut self, style: Style) -> Self {
692 self.border_style = style;
693 self
694 }
695
696 pub fn border_fg(mut self, color: Color) -> Self {
698 self.border_style = self.border_style.fg(color);
699 self
700 }
701
702 pub fn dark_border_style(mut self, style: Style) -> Self {
704 self.dark_border_style = Some(style);
705 self
706 }
707
708 pub fn bg(mut self, color: Color) -> Self {
710 self.bg = Some(color);
711 self
712 }
713
714 pub fn text_color(mut self, color: Color) -> Self {
717 self.text_color = Some(color);
718 self
719 }
720
721 pub fn dark_bg(mut self, color: Color) -> Self {
723 self.dark_bg = Some(color);
724 self
725 }
726
727 pub fn group_hover_bg(mut self, color: Color) -> Self {
729 self.group_hover_bg = Some(color);
730 self
731 }
732
733 pub fn group_hover_border_style(mut self, style: Style) -> Self {
735 self.group_hover_border_style = Some(style);
736 self
737 }
738
739 pub fn p(self, value: u32) -> Self {
743 self.pad(value)
744 }
745
746 pub fn pad(mut self, value: u32) -> Self {
748 self.padding = Padding::all(value);
749 self
750 }
751
752 pub fn px(mut self, value: u32) -> Self {
754 self.padding.left = value;
755 self.padding.right = value;
756 self
757 }
758
759 pub fn py(mut self, value: u32) -> Self {
761 self.padding.top = value;
762 self.padding.bottom = value;
763 self
764 }
765
766 pub fn pt(mut self, value: u32) -> Self {
768 self.padding.top = value;
769 self
770 }
771
772 pub fn pr(mut self, value: u32) -> Self {
774 self.padding.right = value;
775 self
776 }
777
778 pub fn pb(mut self, value: u32) -> Self {
780 self.padding.bottom = value;
781 self
782 }
783
784 pub fn pl(mut self, value: u32) -> Self {
786 self.padding.left = value;
787 self
788 }
789
790 pub fn padding(mut self, padding: Padding) -> Self {
792 self.padding = padding;
793 self
794 }
795
796 pub fn m(mut self, value: u32) -> Self {
800 self.margin = Margin::all(value);
801 self
802 }
803
804 pub fn mx(mut self, value: u32) -> Self {
806 self.margin.left = value;
807 self.margin.right = value;
808 self
809 }
810
811 pub fn my(mut self, value: u32) -> Self {
813 self.margin.top = value;
814 self.margin.bottom = value;
815 self
816 }
817
818 pub fn mt(mut self, value: u32) -> Self {
820 self.margin.top = value;
821 self
822 }
823
824 pub fn mr(mut self, value: u32) -> Self {
826 self.margin.right = value;
827 self
828 }
829
830 pub fn mb(mut self, value: u32) -> Self {
832 self.margin.bottom = value;
833 self
834 }
835
836 pub fn ml(mut self, value: u32) -> Self {
838 self.margin.left = value;
839 self
840 }
841
842 pub fn margin(mut self, margin: Margin) -> Self {
844 self.margin = margin;
845 self
846 }
847
848 pub fn w(mut self, value: u32) -> Self {
852 self.constraints.min_width = Some(value);
853 self.constraints.max_width = Some(value);
854 self
855 }
856
857 define_breakpoint_methods!(
858 base = w,
859 arg = value: u32,
860 xs = xs_w => [
861 "Width applied only at Xs breakpoint (< 40 cols).",
862 "",
863 "# Example",
864 "```ignore",
865 "ui.container().w(20).md_w(40).lg_w(60).col(|ui| { ... });",
866 "```"
867 ],
868 sm = sm_w => ["Width applied only at Sm breakpoint (40-79 cols)."],
869 md = md_w => ["Width applied only at Md breakpoint (80-119 cols)."],
870 lg = lg_w => ["Width applied only at Lg breakpoint (120-159 cols)."],
871 xl = xl_w => ["Width applied only at Xl breakpoint (>= 160 cols)."],
872 at = w_at => ["Width applied only at the given breakpoint."]
873 );
874
875 pub fn h(mut self, value: u32) -> Self {
877 self.constraints.min_height = Some(value);
878 self.constraints.max_height = Some(value);
879 self
880 }
881
882 define_breakpoint_methods!(
883 base = h,
884 arg = value: u32,
885 xs = xs_h => ["Height applied only at Xs breakpoint (< 40 cols)."],
886 sm = sm_h => ["Height applied only at Sm breakpoint (40-79 cols)."],
887 md = md_h => ["Height applied only at Md breakpoint (80-119 cols)."],
888 lg = lg_h => ["Height applied only at Lg breakpoint (120-159 cols)."],
889 xl = xl_h => ["Height applied only at Xl breakpoint (>= 160 cols)."],
890 at = h_at => ["Height applied only at the given breakpoint."]
891 );
892
893 pub fn min_w(mut self, value: u32) -> Self {
895 self.constraints.min_width = Some(value);
896 self
897 }
898
899 define_breakpoint_methods!(
900 base = min_w,
901 arg = value: u32,
902 xs = xs_min_w => ["Minimum width applied only at Xs breakpoint (< 40 cols)."],
903 sm = sm_min_w => ["Minimum width applied only at Sm breakpoint (40-79 cols)."],
904 md = md_min_w => ["Minimum width applied only at Md breakpoint (80-119 cols)."],
905 lg = lg_min_w => ["Minimum width applied only at Lg breakpoint (120-159 cols)."],
906 xl = xl_min_w => ["Minimum width applied only at Xl breakpoint (>= 160 cols)."],
907 at = min_w_at => ["Minimum width applied only at the given breakpoint."]
908 );
909
910 pub fn max_w(mut self, value: u32) -> Self {
912 self.constraints.max_width = Some(value);
913 self
914 }
915
916 define_breakpoint_methods!(
917 base = max_w,
918 arg = value: u32,
919 xs = xs_max_w => ["Maximum width applied only at Xs breakpoint (< 40 cols)."],
920 sm = sm_max_w => ["Maximum width applied only at Sm breakpoint (40-79 cols)."],
921 md = md_max_w => ["Maximum width applied only at Md breakpoint (80-119 cols)."],
922 lg = lg_max_w => ["Maximum width applied only at Lg breakpoint (120-159 cols)."],
923 xl = xl_max_w => ["Maximum width applied only at Xl breakpoint (>= 160 cols)."],
924 at = max_w_at => ["Maximum width applied only at the given breakpoint."]
925 );
926
927 pub fn min_h(mut self, value: u32) -> Self {
929 self.constraints.min_height = Some(value);
930 self
931 }
932
933 pub fn max_h(mut self, value: u32) -> Self {
935 self.constraints.max_height = Some(value);
936 self
937 }
938
939 pub fn min_width(mut self, value: u32) -> Self {
941 self.constraints.min_width = Some(value);
942 self
943 }
944
945 pub fn max_width(mut self, value: u32) -> Self {
947 self.constraints.max_width = Some(value);
948 self
949 }
950
951 pub fn min_height(mut self, value: u32) -> Self {
953 self.constraints.min_height = Some(value);
954 self
955 }
956
957 pub fn max_height(mut self, value: u32) -> Self {
959 self.constraints.max_height = Some(value);
960 self
961 }
962
963 pub fn w_pct(mut self, pct: u8) -> Self {
965 self.constraints.width_pct = Some(pct.min(100));
966 self
967 }
968
969 pub fn h_pct(mut self, pct: u8) -> Self {
971 self.constraints.height_pct = Some(pct.min(100));
972 self
973 }
974
975 pub fn constraints(mut self, constraints: Constraints) -> Self {
977 self.constraints = constraints;
978 self
979 }
980
981 pub fn gap(mut self, gap: u32) -> Self {
985 self.gap = gap;
986 self
987 }
988
989 pub fn row_gap(mut self, value: u32) -> Self {
992 self.row_gap = Some(value);
993 self
994 }
995
996 pub fn col_gap(mut self, value: u32) -> Self {
999 self.col_gap = Some(value);
1000 self
1001 }
1002
1003 define_breakpoint_methods!(
1004 base = gap,
1005 arg = value: u32,
1006 xs = xs_gap => ["Gap applied only at Xs breakpoint (< 40 cols)."],
1007 sm = sm_gap => ["Gap applied only at Sm breakpoint (40-79 cols)."],
1008 md = md_gap => [
1009 "Gap applied only at Md breakpoint (80-119 cols).",
1010 "",
1011 "# Example",
1012 "```ignore",
1013 "ui.container().gap(0).md_gap(2).col(|ui| { ... });",
1014 "```"
1015 ],
1016 lg = lg_gap => ["Gap applied only at Lg breakpoint (120-159 cols)."],
1017 xl = xl_gap => ["Gap applied only at Xl breakpoint (>= 160 cols)."],
1018 at = gap_at => ["Gap applied only at the given breakpoint."]
1019 );
1020
1021 pub fn grow(mut self, grow: u16) -> Self {
1023 self.grow = grow;
1024 self
1025 }
1026
1027 define_breakpoint_methods!(
1028 base = grow,
1029 arg = value: u16,
1030 xs = xs_grow => ["Grow factor applied only at Xs breakpoint (< 40 cols)."],
1031 sm = sm_grow => ["Grow factor applied only at Sm breakpoint (40-79 cols)."],
1032 md = md_grow => ["Grow factor applied only at Md breakpoint (80-119 cols)."],
1033 lg = lg_grow => ["Grow factor applied only at Lg breakpoint (120-159 cols)."],
1034 xl = xl_grow => ["Grow factor applied only at Xl breakpoint (>= 160 cols)."],
1035 at = grow_at => ["Grow factor applied only at the given breakpoint."]
1036 );
1037
1038 define_breakpoint_methods!(
1039 base = p,
1040 arg = value: u32,
1041 xs = xs_p => ["Uniform padding applied only at Xs breakpoint (< 40 cols)."],
1042 sm = sm_p => ["Uniform padding applied only at Sm breakpoint (40-79 cols)."],
1043 md = md_p => ["Uniform padding applied only at Md breakpoint (80-119 cols)."],
1044 lg = lg_p => ["Uniform padding applied only at Lg breakpoint (120-159 cols)."],
1045 xl = xl_p => ["Uniform padding applied only at Xl breakpoint (>= 160 cols)."],
1046 at = p_at => ["Padding applied only at the given breakpoint."]
1047 );
1048
1049 pub fn align(mut self, align: Align) -> Self {
1053 self.align = align;
1054 self
1055 }
1056
1057 pub fn center(self) -> Self {
1059 self.align(Align::Center)
1060 }
1061
1062 pub fn justify(mut self, justify: Justify) -> Self {
1064 self.justify = justify;
1065 self
1066 }
1067
1068 pub fn space_between(self) -> Self {
1070 self.justify(Justify::SpaceBetween)
1071 }
1072
1073 pub fn space_around(self) -> Self {
1075 self.justify(Justify::SpaceAround)
1076 }
1077
1078 pub fn space_evenly(self) -> Self {
1080 self.justify(Justify::SpaceEvenly)
1081 }
1082
1083 pub fn flex_center(self) -> Self {
1085 self.justify(Justify::Center).align(Align::Center)
1086 }
1087
1088 pub fn align_self(mut self, align: Align) -> Self {
1091 self.align_self_value = Some(align);
1092 self
1093 }
1094
1095 pub fn title(self, title: impl Into<String>) -> Self {
1099 self.title_styled(title, Style::new())
1100 }
1101
1102 pub fn title_styled(mut self, title: impl Into<String>, style: Style) -> Self {
1104 self.title = Some((title.into(), style));
1105 self
1106 }
1107
1108 pub fn scroll_offset(mut self, offset: u32) -> Self {
1112 self.scroll_offset = Some(offset);
1113 self
1114 }
1115
1116 fn group_name(mut self, name: String) -> Self {
1117 self.group_name = Some(name);
1118 self
1119 }
1120
1121 pub fn col(self, f: impl FnOnce(&mut Context)) -> Response {
1126 self.finish(Direction::Column, f)
1127 }
1128
1129 pub fn row(self, f: impl FnOnce(&mut Context)) -> Response {
1134 self.finish(Direction::Row, f)
1135 }
1136
1137 pub fn line(mut self, f: impl FnOnce(&mut Context)) -> Response {
1142 self.gap = 0;
1143 self.finish(Direction::Row, f)
1144 }
1145
1146 pub fn draw(self, f: impl FnOnce(&mut crate::buffer::Buffer, Rect) + 'static) {
1161 let draw_id = self.ctx.deferred_draws.len();
1162 self.ctx.deferred_draws.push(Some(Box::new(f)));
1163 self.ctx.interaction_count += 1;
1164 self.ctx.commands.push(Command::RawDraw {
1165 draw_id,
1166 constraints: self.constraints,
1167 grow: self.grow,
1168 margin: self.margin,
1169 });
1170 }
1171
1172 fn finish(mut self, direction: Direction, f: impl FnOnce(&mut Context)) -> Response {
1173 let interaction_id = self.ctx.next_interaction_id();
1174 let resolved_gap = match direction {
1175 Direction::Column => self.row_gap.unwrap_or(self.gap),
1176 Direction::Row => self.col_gap.unwrap_or(self.gap),
1177 };
1178
1179 let in_hovered_group = self
1180 .group_name
1181 .as_ref()
1182 .map(|name| self.ctx.is_group_hovered(name))
1183 .unwrap_or(false)
1184 || self
1185 .ctx
1186 .group_stack
1187 .last()
1188 .map(|name| self.ctx.is_group_hovered(name))
1189 .unwrap_or(false);
1190 let in_focused_group = self
1191 .group_name
1192 .as_ref()
1193 .map(|name| self.ctx.is_group_focused(name))
1194 .unwrap_or(false)
1195 || self
1196 .ctx
1197 .group_stack
1198 .last()
1199 .map(|name| self.ctx.is_group_focused(name))
1200 .unwrap_or(false);
1201
1202 let resolved_bg = if self.ctx.dark_mode {
1203 self.dark_bg.or(self.bg)
1204 } else {
1205 self.bg
1206 };
1207 let resolved_border_style = if self.ctx.dark_mode {
1208 self.dark_border_style.unwrap_or(self.border_style)
1209 } else {
1210 self.border_style
1211 };
1212 let bg_color = if in_hovered_group || in_focused_group {
1213 self.group_hover_bg.or(resolved_bg)
1214 } else {
1215 resolved_bg
1216 };
1217 let border_style = if in_hovered_group || in_focused_group {
1218 self.group_hover_border_style
1219 .unwrap_or(resolved_border_style)
1220 } else {
1221 resolved_border_style
1222 };
1223 let group_name = self.group_name.take();
1224 let is_group_container = group_name.is_some();
1225
1226 if let Some(scroll_offset) = self.scroll_offset {
1227 self.ctx.commands.push(Command::BeginScrollable {
1228 grow: self.grow,
1229 border: self.border,
1230 border_sides: self.border_sides,
1231 border_style,
1232 padding: self.padding,
1233 margin: self.margin,
1234 constraints: self.constraints,
1235 title: self.title,
1236 scroll_offset,
1237 });
1238 } else {
1239 self.ctx.commands.push(Command::BeginContainer {
1240 direction,
1241 gap: resolved_gap,
1242 align: self.align,
1243 align_self: self.align_self_value,
1244 justify: self.justify,
1245 border: self.border,
1246 border_sides: self.border_sides,
1247 border_style,
1248 bg_color,
1249 padding: self.padding,
1250 margin: self.margin,
1251 constraints: self.constraints,
1252 title: self.title,
1253 grow: self.grow,
1254 group_name,
1255 });
1256 }
1257 self.ctx.text_color_stack.push(self.text_color);
1258 f(self.ctx);
1259 self.ctx.text_color_stack.pop();
1260 self.ctx.commands.push(Command::EndContainer);
1261 self.ctx.last_text_idx = None;
1262
1263 if is_group_container {
1264 self.ctx.group_stack.pop();
1265 self.ctx.group_count = self.ctx.group_count.saturating_sub(1);
1266 }
1267
1268 self.ctx.response_for(interaction_id)
1269 }
1270}
1271