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