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 {
560 if let Some(base) = style.extends {
562 self = self.apply(base);
563 }
564 if let Some(v) = style.border {
565 self.border = Some(v);
566 }
567 if let Some(v) = style.border_sides {
568 self.border_sides = v;
569 }
570 if let Some(v) = style.border_style {
571 self.border_style = v;
572 }
573 if let Some(v) = style.bg {
574 self.bg = Some(v);
575 }
576 if let Some(v) = style.dark_bg {
577 self.dark_bg = Some(v);
578 }
579 if let Some(v) = style.dark_border_style {
580 self.dark_border_style = Some(v);
581 }
582 if let Some(v) = style.padding {
583 self.padding = v;
584 }
585 if let Some(v) = style.margin {
586 self.margin = v;
587 }
588 if let Some(v) = style.gap {
589 self.gap = v;
590 }
591 if let Some(v) = style.row_gap {
592 self.row_gap = Some(v);
593 }
594 if let Some(v) = style.col_gap {
595 self.col_gap = Some(v);
596 }
597 if let Some(v) = style.grow {
598 self.grow = v;
599 }
600 if let Some(v) = style.align {
601 self.align = v;
602 }
603 if let Some(v) = style.align_self {
604 self.align_self_value = Some(v);
605 }
606 if let Some(v) = style.justify {
607 self.justify = v;
608 }
609 if let Some(v) = style.text_color {
610 self.text_color = Some(v);
611 }
612 if let Some(w) = style.w {
613 self.constraints.min_width = Some(w);
614 self.constraints.max_width = Some(w);
615 }
616 if let Some(h) = style.h {
617 self.constraints.min_height = Some(h);
618 self.constraints.max_height = Some(h);
619 }
620 if let Some(v) = style.min_w {
621 self.constraints.min_width = Some(v);
622 }
623 if let Some(v) = style.max_w {
624 self.constraints.max_width = Some(v);
625 }
626 if let Some(v) = style.min_h {
627 self.constraints.min_height = Some(v);
628 }
629 if let Some(v) = style.max_h {
630 self.constraints.max_height = Some(v);
631 }
632 if let Some(v) = style.w_pct {
633 self.constraints.width_pct = Some(v);
634 }
635 if let Some(v) = style.h_pct {
636 self.constraints.height_pct = Some(v);
637 }
638 if let Some(tc) = style.theme_bg {
640 self.bg = Some(self.ctx.theme.resolve(tc));
641 }
642 if let Some(tc) = style.theme_text_color {
643 self.text_color = Some(self.ctx.theme.resolve(tc));
644 }
645 if let Some(tc) = style.theme_border_fg {
646 let color = self.ctx.theme.resolve(tc);
647 self.border_style = Style::new().fg(color);
648 }
649 self
650 }
651
652 pub fn border(mut self, border: Border) -> Self {
654 self.border = Some(border);
655 self
656 }
657
658 pub fn border_top(mut self, show: bool) -> Self {
660 self.border_sides.top = show;
661 self
662 }
663
664 pub fn border_right(mut self, show: bool) -> Self {
666 self.border_sides.right = show;
667 self
668 }
669
670 pub fn border_bottom(mut self, show: bool) -> Self {
672 self.border_sides.bottom = show;
673 self
674 }
675
676 pub fn border_left(mut self, show: bool) -> Self {
678 self.border_sides.left = show;
679 self
680 }
681
682 pub fn border_sides(mut self, sides: BorderSides) -> Self {
684 self.border_sides = sides;
685 self
686 }
687
688 pub fn border_x(self) -> Self {
690 self.border_sides(BorderSides {
691 top: false,
692 right: true,
693 bottom: false,
694 left: true,
695 })
696 }
697
698 pub fn border_y(self) -> Self {
700 self.border_sides(BorderSides {
701 top: true,
702 right: false,
703 bottom: true,
704 left: false,
705 })
706 }
707
708 pub fn rounded(self) -> Self {
710 self.border(Border::Rounded)
711 }
712
713 pub fn border_style(mut self, style: Style) -> Self {
715 self.border_style = style;
716 self
717 }
718
719 pub fn border_fg(mut self, color: Color) -> Self {
721 self.border_style = self.border_style.fg(color);
722 self
723 }
724
725 pub fn dark_border_style(mut self, style: Style) -> Self {
727 self.dark_border_style = Some(style);
728 self
729 }
730
731 pub fn bg(mut self, color: Color) -> Self {
733 self.bg = Some(color);
734 self
735 }
736
737 pub fn text_color(mut self, color: Color) -> Self {
740 self.text_color = Some(color);
741 self
742 }
743
744 pub fn dark_bg(mut self, color: Color) -> Self {
746 self.dark_bg = Some(color);
747 self
748 }
749
750 pub fn group_hover_bg(mut self, color: Color) -> Self {
752 self.group_hover_bg = Some(color);
753 self
754 }
755
756 pub fn group_hover_border_style(mut self, style: Style) -> Self {
758 self.group_hover_border_style = Some(style);
759 self
760 }
761
762 pub fn p(self, value: u32) -> Self {
766 self.pad(value)
767 }
768
769 pub fn pad(mut self, value: u32) -> Self {
771 self.padding = Padding::all(value);
772 self
773 }
774
775 pub fn px(mut self, value: u32) -> Self {
777 self.padding.left = value;
778 self.padding.right = value;
779 self
780 }
781
782 pub fn py(mut self, value: u32) -> Self {
784 self.padding.top = value;
785 self.padding.bottom = value;
786 self
787 }
788
789 pub fn pt(mut self, value: u32) -> Self {
791 self.padding.top = value;
792 self
793 }
794
795 pub fn pr(mut self, value: u32) -> Self {
797 self.padding.right = value;
798 self
799 }
800
801 pub fn pb(mut self, value: u32) -> Self {
803 self.padding.bottom = value;
804 self
805 }
806
807 pub fn pl(mut self, value: u32) -> Self {
809 self.padding.left = value;
810 self
811 }
812
813 pub fn padding(mut self, padding: Padding) -> Self {
815 self.padding = padding;
816 self
817 }
818
819 pub fn m(mut self, value: u32) -> Self {
823 self.margin = Margin::all(value);
824 self
825 }
826
827 pub fn mx(mut self, value: u32) -> Self {
829 self.margin.left = value;
830 self.margin.right = value;
831 self
832 }
833
834 pub fn my(mut self, value: u32) -> Self {
836 self.margin.top = value;
837 self.margin.bottom = value;
838 self
839 }
840
841 pub fn mt(mut self, value: u32) -> Self {
843 self.margin.top = value;
844 self
845 }
846
847 pub fn mr(mut self, value: u32) -> Self {
849 self.margin.right = value;
850 self
851 }
852
853 pub fn mb(mut self, value: u32) -> Self {
855 self.margin.bottom = value;
856 self
857 }
858
859 pub fn ml(mut self, value: u32) -> Self {
861 self.margin.left = value;
862 self
863 }
864
865 pub fn margin(mut self, margin: Margin) -> Self {
867 self.margin = margin;
868 self
869 }
870
871 pub fn w(mut self, value: u32) -> Self {
875 self.constraints.min_width = Some(value);
876 self.constraints.max_width = Some(value);
877 self
878 }
879
880 define_breakpoint_methods!(
881 base = w,
882 arg = value: u32,
883 xs = xs_w => [
884 "Width applied only at Xs breakpoint (< 40 cols).",
885 "",
886 "# Example",
887 "```ignore",
888 "ui.container().w(20).md_w(40).lg_w(60).col(|ui| { ... });",
889 "```"
890 ],
891 sm = sm_w => ["Width applied only at Sm breakpoint (40-79 cols)."],
892 md = md_w => ["Width applied only at Md breakpoint (80-119 cols)."],
893 lg = lg_w => ["Width applied only at Lg breakpoint (120-159 cols)."],
894 xl = xl_w => ["Width applied only at Xl breakpoint (>= 160 cols)."],
895 at = w_at => ["Width applied only at the given breakpoint."]
896 );
897
898 pub fn h(mut self, value: u32) -> Self {
900 self.constraints.min_height = Some(value);
901 self.constraints.max_height = Some(value);
902 self
903 }
904
905 define_breakpoint_methods!(
906 base = h,
907 arg = value: u32,
908 xs = xs_h => ["Height applied only at Xs breakpoint (< 40 cols)."],
909 sm = sm_h => ["Height applied only at Sm breakpoint (40-79 cols)."],
910 md = md_h => ["Height applied only at Md breakpoint (80-119 cols)."],
911 lg = lg_h => ["Height applied only at Lg breakpoint (120-159 cols)."],
912 xl = xl_h => ["Height applied only at Xl breakpoint (>= 160 cols)."],
913 at = h_at => ["Height applied only at the given breakpoint."]
914 );
915
916 pub fn min_w(mut self, value: u32) -> Self {
918 self.constraints.min_width = Some(value);
919 self
920 }
921
922 define_breakpoint_methods!(
923 base = min_w,
924 arg = value: u32,
925 xs = xs_min_w => ["Minimum width applied only at Xs breakpoint (< 40 cols)."],
926 sm = sm_min_w => ["Minimum width applied only at Sm breakpoint (40-79 cols)."],
927 md = md_min_w => ["Minimum width applied only at Md breakpoint (80-119 cols)."],
928 lg = lg_min_w => ["Minimum width applied only at Lg breakpoint (120-159 cols)."],
929 xl = xl_min_w => ["Minimum width applied only at Xl breakpoint (>= 160 cols)."],
930 at = min_w_at => ["Minimum width applied only at the given breakpoint."]
931 );
932
933 pub fn max_w(mut self, value: u32) -> Self {
935 self.constraints.max_width = Some(value);
936 self
937 }
938
939 define_breakpoint_methods!(
940 base = max_w,
941 arg = value: u32,
942 xs = xs_max_w => ["Maximum width applied only at Xs breakpoint (< 40 cols)."],
943 sm = sm_max_w => ["Maximum width applied only at Sm breakpoint (40-79 cols)."],
944 md = md_max_w => ["Maximum width applied only at Md breakpoint (80-119 cols)."],
945 lg = lg_max_w => ["Maximum width applied only at Lg breakpoint (120-159 cols)."],
946 xl = xl_max_w => ["Maximum width applied only at Xl breakpoint (>= 160 cols)."],
947 at = max_w_at => ["Maximum width applied only at the given breakpoint."]
948 );
949
950 pub fn min_h(mut self, value: u32) -> Self {
952 self.constraints.min_height = Some(value);
953 self
954 }
955
956 pub fn max_h(mut self, value: u32) -> Self {
958 self.constraints.max_height = Some(value);
959 self
960 }
961
962 pub fn min_width(mut self, value: u32) -> Self {
964 self.constraints.min_width = Some(value);
965 self
966 }
967
968 pub fn max_width(mut self, value: u32) -> Self {
970 self.constraints.max_width = Some(value);
971 self
972 }
973
974 pub fn min_height(mut self, value: u32) -> Self {
976 self.constraints.min_height = Some(value);
977 self
978 }
979
980 pub fn max_height(mut self, value: u32) -> Self {
982 self.constraints.max_height = Some(value);
983 self
984 }
985
986 pub fn w_pct(mut self, pct: u8) -> Self {
988 self.constraints.width_pct = Some(pct.min(100));
989 self
990 }
991
992 pub fn h_pct(mut self, pct: u8) -> Self {
994 self.constraints.height_pct = Some(pct.min(100));
995 self
996 }
997
998 pub fn constraints(mut self, constraints: Constraints) -> Self {
1000 self.constraints = constraints;
1001 self
1002 }
1003
1004 pub fn gap(mut self, gap: u32) -> Self {
1008 self.gap = gap;
1009 self
1010 }
1011
1012 pub fn row_gap(mut self, value: u32) -> Self {
1015 self.row_gap = Some(value);
1016 self
1017 }
1018
1019 pub fn col_gap(mut self, value: u32) -> Self {
1022 self.col_gap = Some(value);
1023 self
1024 }
1025
1026 define_breakpoint_methods!(
1027 base = gap,
1028 arg = value: u32,
1029 xs = xs_gap => ["Gap applied only at Xs breakpoint (< 40 cols)."],
1030 sm = sm_gap => ["Gap applied only at Sm breakpoint (40-79 cols)."],
1031 md = md_gap => [
1032 "Gap applied only at Md breakpoint (80-119 cols).",
1033 "",
1034 "# Example",
1035 "```ignore",
1036 "ui.container().gap(0).md_gap(2).col(|ui| { ... });",
1037 "```"
1038 ],
1039 lg = lg_gap => ["Gap applied only at Lg breakpoint (120-159 cols)."],
1040 xl = xl_gap => ["Gap applied only at Xl breakpoint (>= 160 cols)."],
1041 at = gap_at => ["Gap applied only at the given breakpoint."]
1042 );
1043
1044 pub fn grow(mut self, grow: u16) -> Self {
1046 self.grow = grow;
1047 self
1048 }
1049
1050 define_breakpoint_methods!(
1051 base = grow,
1052 arg = value: u16,
1053 xs = xs_grow => ["Grow factor applied only at Xs breakpoint (< 40 cols)."],
1054 sm = sm_grow => ["Grow factor applied only at Sm breakpoint (40-79 cols)."],
1055 md = md_grow => ["Grow factor applied only at Md breakpoint (80-119 cols)."],
1056 lg = lg_grow => ["Grow factor applied only at Lg breakpoint (120-159 cols)."],
1057 xl = xl_grow => ["Grow factor applied only at Xl breakpoint (>= 160 cols)."],
1058 at = grow_at => ["Grow factor applied only at the given breakpoint."]
1059 );
1060
1061 define_breakpoint_methods!(
1062 base = p,
1063 arg = value: u32,
1064 xs = xs_p => ["Uniform padding applied only at Xs breakpoint (< 40 cols)."],
1065 sm = sm_p => ["Uniform padding applied only at Sm breakpoint (40-79 cols)."],
1066 md = md_p => ["Uniform padding applied only at Md breakpoint (80-119 cols)."],
1067 lg = lg_p => ["Uniform padding applied only at Lg breakpoint (120-159 cols)."],
1068 xl = xl_p => ["Uniform padding applied only at Xl breakpoint (>= 160 cols)."],
1069 at = p_at => ["Padding applied only at the given breakpoint."]
1070 );
1071
1072 pub fn align(mut self, align: Align) -> Self {
1076 self.align = align;
1077 self
1078 }
1079
1080 pub fn center(self) -> Self {
1082 self.align(Align::Center)
1083 }
1084
1085 pub fn justify(mut self, justify: Justify) -> Self {
1087 self.justify = justify;
1088 self
1089 }
1090
1091 pub fn space_between(self) -> Self {
1093 self.justify(Justify::SpaceBetween)
1094 }
1095
1096 pub fn space_around(self) -> Self {
1098 self.justify(Justify::SpaceAround)
1099 }
1100
1101 pub fn space_evenly(self) -> Self {
1103 self.justify(Justify::SpaceEvenly)
1104 }
1105
1106 pub fn flex_center(self) -> Self {
1108 self.justify(Justify::Center).align(Align::Center)
1109 }
1110
1111 pub fn align_self(mut self, align: Align) -> Self {
1114 self.align_self_value = Some(align);
1115 self
1116 }
1117
1118 pub fn title(self, title: impl Into<String>) -> Self {
1122 self.title_styled(title, Style::new())
1123 }
1124
1125 pub fn title_styled(mut self, title: impl Into<String>, style: Style) -> Self {
1127 self.title = Some((title.into(), style));
1128 self
1129 }
1130
1131 pub fn scroll_offset(mut self, offset: u32) -> Self {
1135 self.scroll_offset = Some(offset);
1136 self
1137 }
1138
1139 pub(crate) fn group_name(mut self, name: String) -> Self {
1140 self.group_name = Some(name);
1141 self
1142 }
1143
1144 pub fn col(self, f: impl FnOnce(&mut Context)) -> Response {
1149 self.finish(Direction::Column, f)
1150 }
1151
1152 pub fn row(self, f: impl FnOnce(&mut Context)) -> Response {
1157 self.finish(Direction::Row, f)
1158 }
1159
1160 pub fn line(mut self, f: impl FnOnce(&mut Context)) -> Response {
1165 self.gap = 0;
1166 self.finish(Direction::Row, f)
1167 }
1168
1169 pub fn draw(self, f: impl FnOnce(&mut crate::buffer::Buffer, Rect) + 'static) {
1184 let draw_id = self.ctx.deferred_draws.len();
1185 self.ctx.deferred_draws.push(Some(Box::new(f)));
1186 self.ctx.skip_interaction_slot();
1187 self.ctx.commands.push(Command::RawDraw {
1188 draw_id,
1189 constraints: self.constraints,
1190 grow: self.grow,
1191 margin: self.margin,
1192 });
1193 }
1194
1195 pub fn draw_with<D: 'static>(
1219 self,
1220 data: D,
1221 f: impl FnOnce(&mut crate::buffer::Buffer, Rect, &D) + 'static,
1222 ) {
1223 let draw_id = self.ctx.deferred_draws.len();
1224 self.ctx
1225 .deferred_draws
1226 .push(Some(Box::new(move |buf, rect| f(buf, rect, &data))));
1227 self.ctx.skip_interaction_slot();
1228 self.ctx.commands.push(Command::RawDraw {
1229 draw_id,
1230 constraints: self.constraints,
1231 grow: self.grow,
1232 margin: self.margin,
1233 });
1234 }
1235
1236 pub fn draw_interactive(
1257 self,
1258 f: impl FnOnce(&mut crate::buffer::Buffer, Rect) + 'static,
1259 ) -> Response {
1260 let draw_id = self.ctx.deferred_draws.len();
1261 self.ctx.deferred_draws.push(Some(Box::new(f)));
1262 let interaction_id = self.ctx.next_interaction_id();
1263 self.ctx.commands.push(Command::RawDraw {
1264 draw_id,
1265 constraints: self.constraints,
1266 grow: self.grow,
1267 margin: self.margin,
1268 });
1269 self.ctx.response_for(interaction_id)
1270 }
1271
1272 fn finish(mut self, direction: Direction, f: impl FnOnce(&mut Context)) -> Response {
1273 let interaction_id = self.ctx.next_interaction_id();
1274 let resolved_gap = match direction {
1275 Direction::Column => self.row_gap.unwrap_or(self.gap),
1276 Direction::Row => self.col_gap.unwrap_or(self.gap),
1277 };
1278
1279 let in_hovered_group = self
1280 .group_name
1281 .as_ref()
1282 .map(|name| self.ctx.is_group_hovered(name))
1283 .unwrap_or(false)
1284 || self
1285 .ctx
1286 .rollback
1287 .group_stack
1288 .last()
1289 .map(|name| self.ctx.is_group_hovered(name))
1290 .unwrap_or(false);
1291 let in_focused_group = self
1292 .group_name
1293 .as_ref()
1294 .map(|name| self.ctx.is_group_focused(name))
1295 .unwrap_or(false)
1296 || self
1297 .ctx
1298 .rollback
1299 .group_stack
1300 .last()
1301 .map(|name| self.ctx.is_group_focused(name))
1302 .unwrap_or(false);
1303
1304 let resolved_bg = if self.ctx.rollback.dark_mode {
1305 self.dark_bg.or(self.bg)
1306 } else {
1307 self.bg
1308 };
1309 let resolved_border_style = if self.ctx.rollback.dark_mode {
1310 self.dark_border_style.unwrap_or(self.border_style)
1311 } else {
1312 self.border_style
1313 };
1314 let bg_color = if in_hovered_group || in_focused_group {
1315 self.group_hover_bg.or(resolved_bg)
1316 } else {
1317 resolved_bg
1318 };
1319 let border_style = if in_hovered_group || in_focused_group {
1320 self.group_hover_border_style
1321 .unwrap_or(resolved_border_style)
1322 } else {
1323 resolved_border_style
1324 };
1325 let group_name = self.group_name.take();
1326 let is_group_container = group_name.is_some();
1327
1328 if let Some(scroll_offset) = self.scroll_offset {
1329 self.ctx
1330 .commands
1331 .push(Command::BeginScrollable(Box::new(BeginScrollableArgs {
1332 grow: self.grow,
1333 border: self.border,
1334 border_sides: self.border_sides,
1335 border_style,
1336 padding: self.padding,
1337 margin: self.margin,
1338 constraints: self.constraints,
1339 title: self.title,
1340 scroll_offset,
1341 })));
1342 } else {
1343 self.ctx
1344 .commands
1345 .push(Command::BeginContainer(Box::new(BeginContainerArgs {
1346 direction,
1347 gap: resolved_gap,
1348 align: self.align,
1349 align_self: self.align_self_value,
1350 justify: self.justify,
1351 border: self.border,
1352 border_sides: self.border_sides,
1353 border_style,
1354 bg_color,
1355 padding: self.padding,
1356 margin: self.margin,
1357 constraints: self.constraints,
1358 title: self.title,
1359 grow: self.grow,
1360 group_name,
1361 })));
1362 }
1363 self.ctx.rollback.text_color_stack.push(self.text_color);
1364 f(self.ctx);
1365 self.ctx.rollback.text_color_stack.pop();
1366 self.ctx.commands.push(Command::EndContainer);
1367 self.ctx.rollback.last_text_idx = None;
1368
1369 if is_group_container {
1370 self.ctx.rollback.group_stack.pop();
1371 self.ctx.rollback.group_count = self.ctx.rollback.group_count.saturating_sub(1);
1372 }
1373
1374 self.ctx.response_for(interaction_id)
1375 }
1376}