| 50 |
50 |
|
//! nothing here places anything relative to anything else, because the moment
|
| 51 |
51 |
|
//! it did it would be a layout engine with one consumer's flow baked into it.
|
| 52 |
52 |
|
|
| 53 |
|
- |
use makeover_layout::{Act, Awaiting, Field, FieldKind, Figure, Heading, Meter, Token, Tone};
|
|
53 |
+ |
use makeover_layout::{
|
|
54 |
+ |
Act, Awaiting, Field, FieldKind, Figure, Heading, Meter, ThemeVariant, Token, Tone,
|
|
55 |
+ |
};
|
| 54 |
56 |
|
use ratatui::buffer::Buffer;
|
| 55 |
57 |
|
use ratatui::layout::Rect;
|
| 56 |
58 |
|
use ratatui::style::{Modifier, Style};
|
| 572 |
574 |
|
// the source is the text, and drawing it as text is honest.
|
| 573 |
575 |
|
kind if kind.multiline() => 3,
|
| 574 |
576 |
|
kind if kind.offers_options() => u16::try_from(field.options.len()).unwrap_or(u16::MAX),
|
|
577 |
+ |
// A row per theme, a row per group heading, and a row for the follow
|
|
578 |
+ |
// entry when there is one. The headings are counted by walking the
|
|
579 |
+ |
// variants rather than by assuming three, because a machine with only
|
|
580 |
+ |
// dark themes installed draws one heading and reserving three would
|
|
581 |
+ |
// leave two blank rows under every picker.
|
|
582 |
+ |
kind if kind.offers_themes() => {
|
|
583 |
+ |
let mut variants = 0u16;
|
|
584 |
+ |
let mut open: Option<ThemeVariant> = None;
|
|
585 |
+ |
for theme in field.themes {
|
|
586 |
+ |
if open != Some(theme.variant) {
|
|
587 |
+ |
variants = variants.saturating_add(1);
|
|
588 |
+ |
open = Some(theme.variant);
|
|
589 |
+ |
}
|
|
590 |
+ |
}
|
|
591 |
+ |
let rows = u16::try_from(field.themes.len()).unwrap_or(u16::MAX);
|
|
592 |
+ |
rows.saturating_add(variants)
|
|
593 |
+ |
.saturating_add(u16::from(field.follows.is_some()))
|
|
594 |
+ |
}
|
| 575 |
595 |
|
_ => 1,
|
| 576 |
596 |
|
};
|
| 577 |
597 |
|
let note = message_of(style, field).map_or(0, |(text, _)| text::height(text, width));
|
| 638 |
658 |
|
let line = interval_line(style, field, held, well);
|
| 639 |
659 |
|
text::draw_line(&line, below(area, used), buf)
|
| 640 |
660 |
|
}
|
|
661 |
+ |
// The grouping comes out of the order, not out of a group list:
|
|
662 |
+ |
// `Field::themes` arrives sorted by variant, so the run of one variant
|
|
663 |
+ |
// is the group and a heading opens whenever the variant changes. Same
|
|
664 |
+ |
// walk the other two renderers do, which is what keeps three renderers
|
|
665 |
+ |
// from disagreeing about where a group starts.
|
|
666 |
+ |
//
|
|
667 |
+ |
// Drawn as the radio group above rather than as a closed control,
|
|
668 |
+ |
// because a terminal has no closed control: the list is already on
|
|
669 |
+ |
// screen and always was, so the group headings cost a row each and buy
|
|
670 |
+ |
// the structure the description finally carries.
|
|
671 |
+ |
kind if kind.offers_themes() => {
|
|
672 |
+ |
let mut rows = 0;
|
|
673 |
+ |
if let Some(follow) = field.follows {
|
|
674 |
+ |
// First, and under no heading. It names no theme and sits in no
|
|
675 |
+ |
// variant, so a heading over it would be inventing a fourth
|
|
676 |
+ |
// variant for one row.
|
|
677 |
+ |
let chosen = held.text() == follow.value;
|
|
678 |
+ |
let (mark, painted) = if chosen {
|
|
679 |
+ |
("(*)", well)
|
|
680 |
+ |
} else {
|
|
681 |
+ |
("( )", style.secondary)
|
|
682 |
+ |
};
|
|
683 |
+ |
rows += text::draw(
|
|
684 |
+ |
&format!("{mark} {}", follow.label),
|
|
685 |
+ |
painted,
|
|
686 |
+ |
below(area, used + rows),
|
|
687 |
+ |
buf,
|
|
688 |
+ |
);
|
|
689 |
+ |
}
|
|
690 |
+ |
let mut open: Option<ThemeVariant> = None;
|
|
691 |
+ |
for theme in field.themes {
|
|
692 |
+ |
if open != Some(theme.variant) {
|
|
693 |
+ |
// Muted, which is the one place it is the truth rather than
|
|
694 |
+ |
// the lie: a heading will not answer, exactly as an
|
|
695 |
+ |
// unavailable option will not.
|
|
696 |
+ |
rows += text::draw(
|
|
697 |
+ |
theme.variant.heading(),
|
|
698 |
+ |
style.muted,
|
|
699 |
+ |
below(area, used + rows),
|
|
700 |
+ |
buf,
|
|
701 |
+ |
);
|
|
702 |
+ |
open = Some(theme.variant);
|
|
703 |
+ |
}
|
|
704 |
+ |
let chosen = held.text() == theme.id;
|
|
705 |
+ |
let (mark, painted) = if chosen {
|
|
706 |
+ |
("(*)", well)
|
|
707 |
+ |
} else {
|
|
708 |
+ |
("( )", style.secondary)
|
|
709 |
+ |
};
|
|
710 |
+ |
rows += text::draw(
|
|
711 |
+ |
&format!("{mark} {} [{}]", theme.name, theme.contrast.badge()),
|
|
712 |
+ |
painted,
|
|
713 |
+ |
below(area, used + rows),
|
|
714 |
+ |
buf,
|
|
715 |
+ |
);
|
|
716 |
+ |
}
|
|
717 |
+ |
rows
|
|
718 |
+ |
}
|
| 641 |
719 |
|
kind if kind.offers_options() => {
|
| 642 |
720 |
|
let mut rows = 0;
|
| 643 |
721 |
|
for choice in field.options {
|
| 1532 |
1610 |
|
);
|
| 1533 |
1611 |
|
assert!(empty.spans[0].content.starts_with('-'));
|
| 1534 |
1612 |
|
}
|
|
1613 |
+ |
|
|
1614 |
+ |
#[test]
|
|
1615 |
+ |
fn a_theme_picker_heads_each_group_and_marks_each_tier() {
|
|
1616 |
+ |
const THEMES: &[makeover_layout::ThemeChoice<'_>] = &[
|
|
1617 |
+ |
makeover_layout::ThemeChoice::new(
|
|
1618 |
+ |
"goingson",
|
|
1619 |
+ |
"GoingsOn",
|
|
1620 |
+ |
ThemeVariant::Light,
|
|
1621 |
+ |
makeover_layout::Contrast::High,
|
|
1622 |
+ |
),
|
|
1623 |
+ |
makeover_layout::ThemeChoice::new(
|
|
1624 |
+ |
"carbonfox",
|
|
1625 |
+ |
"Carbonfox",
|
|
1626 |
+ |
ThemeVariant::Dark,
|
|
1627 |
+ |
makeover_layout::Contrast::Standard,
|
|
1628 |
+ |
),
|
|
1629 |
+ |
];
|
|
1630 |
+ |
let style = style();
|
|
1631 |
+ |
let field_ = Field::theme("theme", "Theme", THEMES)
|
|
1632 |
+ |
.following(makeover_layout::Choice::new("system", "Follow System"));
|
|
1633 |
+ |
let mut buf = buffer(32, 8);
|
|
1634 |
+ |
field(
|
|
1635 |
+ |
&style,
|
|
1636 |
+ |
&field_,
|
|
1637 |
+ |
Held::Text("carbonfox"),
|
|
1638 |
+ |
false,
|
|
1639 |
+ |
buf.area,
|
|
1640 |
+ |
&mut buf,
|
|
1641 |
+ |
);
|
|
1642 |
+ |
|
|
1643 |
+ |
let rows = rows(&buf);
|
|
1644 |
+ |
assert_eq!(rows[1], "( ) Follow System");
|
|
1645 |
+ |
assert_eq!(rows[2], "Light");
|
|
1646 |
+ |
assert_eq!(rows[3], "( ) GoingsOn [AA]");
|
|
1647 |
+ |
assert_eq!(rows[4], "Dark");
|
|
1648 |
+ |
assert_eq!(rows[5], "(*) Carbonfox [OK]");
|
|
1649 |
+ |
}
|
|
1650 |
+ |
|
|
1651 |
+ |
#[test]
|
|
1652 |
+ |
fn a_theme_picker_asks_for_the_rows_it_draws() {
|
|
1653 |
+ |
// Label, follow, two headings, two themes. A height that counted the
|
|
1654 |
+ |
// themes alone would clip the last group off every picker.
|
|
1655 |
+ |
const THEMES: &[makeover_layout::ThemeChoice<'_>] = &[
|
|
1656 |
+ |
makeover_layout::ThemeChoice::new(
|
|
1657 |
+ |
"goingson",
|
|
1658 |
+ |
"GoingsOn",
|
|
1659 |
+ |
ThemeVariant::Light,
|
|
1660 |
+ |
makeover_layout::Contrast::High,
|
|
1661 |
+ |
),
|
|
1662 |
+ |
makeover_layout::ThemeChoice::new(
|
|
1663 |
+ |
"carbonfox",
|
|
1664 |
+ |
"Carbonfox",
|
|
1665 |
+ |
ThemeVariant::Dark,
|
|
1666 |
+ |
makeover_layout::Contrast::Standard,
|
|
1667 |
+ |
),
|
|
1668 |
+ |
];
|
|
1669 |
+ |
let style = style();
|
|
1670 |
+ |
let field_ = Field::theme("theme", "Theme", THEMES)
|
|
1671 |
+ |
.following(makeover_layout::Choice::new("system", "Follow System"));
|
|
1672 |
+ |
assert_eq!(field_height(&style, &field_, 32), 6);
|
|
1673 |
+ |
|
|
1674 |
+ |
// One variant, no follow row: one heading, not three.
|
|
1675 |
+ |
let one = Field::theme("theme", "Theme", &THEMES[..1]);
|
|
1676 |
+ |
assert_eq!(field_height(&style, &one, 32), 3);
|
|
1677 |
+ |
}
|
| 1535 |
1678 |
|
}
|