返回 CodeWhale
atmosphere.rs
根目录 / vendor / codewhale-ratatui / src / gallery / atmosphere.rs
1 //! Five atmospheres over real components, one entry per [`WaterPalette`].
2 //!
3 //! Every entry paints the same illustrative workspace — a heading, the four
4 //! state words, a deep-water panel with the whale and a little marine life —
5 //! through the usual components and then finishes it with one
6 //! [`Ombre::apply`], so the five previews differ only in the atmosphere. All
7 //! content is illustrative host data, never a live measurement.
8
9 use std::time::Duration;
10
11 use ratatui::{buffer::Buffer, layout::Rect};
12
13 use super::Entry;
14 use crate::{
15 BubbleField, Depth, FishSchool, HabitatDensity, Heading, Message, MotionMode, Ombre, Paint,
16 Panel, Picker, PickerItem, PickerState, Role, State, StatusMark, Theme, WaterPalette, Whale,
17 WhaleState,
18 };
19
20 /// The rows a band starts at, clamped to the area it is cut from.
21 fn band(area: Rect, top: u16, height: u16) -> Rect {
22 let top = top.min(area.height);
23 Rect::new(
24 area.x,
25 area.y.saturating_add(top),
26 area.width,
27 height.min(area.height.saturating_sub(top)),
28 )
29 }
30
31 /// One atmosphere over a workspace card, the four state words and the whale,
32 /// finished last by the ombre.
33 fn scene(area: Rect, buf: &mut Buffer, theme: &Theme, palette: WaterPalette) {
34 let area = area.intersection(buf.area);
35 if area.is_empty() {
36 return;
37 }
38 Heading::new(format!("Codewhale / {} atmosphere", palette.name()))
39 .meta("Finishing pass")
40 .paint(band(area, 0, 1), buf, theme);
41 let body = band(area, 1, area.height.saturating_sub(1));
42 if body.is_empty() {
43 return;
44 }
45
46 let rail_width = 20.min(body.width);
47 let rail = Rect {
48 width: rail_width,
49 ..body
50 };
51 let inner = Panel::new(Depth::Deep)
52 .title("Palettes")
53 .aside("wash")
54 .draw(rail, buf, theme);
55 let items: Vec<PickerItem> = WaterPalette::ALL
56 .iter()
57 .map(|palette| PickerItem::new(palette.name()))
58 .collect();
59 let selected = WaterPalette::ALL
60 .iter()
61 .position(|candidate| *candidate == palette)
62 .unwrap_or(0);
63 Picker::new(&items, PickerState::new(selected)).paint(inner, buf, theme);
64
65 let gap = u16::from(body.width > rail_width);
66 let stage = Rect {
67 x: rail.right().saturating_add(gap),
68 width: body
69 .right()
70 .saturating_sub(rail.right().saturating_add(gap)),
71 ..body
72 };
73 if stage.is_empty() {
74 return;
75 }
76 Message::new("You", "Finish this scene with one atmosphere.")
77 .role(Role::Primary)
78 .paint(band(stage, 0, 2), buf, theme);
79
80 let turn = Panel::new(Depth::Raised)
81 .title("Turn")
82 .draw(band(stage, 2, 7), buf, theme);
83 for (row, state) in [State::Working, State::NeedsYou, State::Failed, State::Done]
84 .into_iter()
85 .enumerate()
86 {
87 let row = u16::try_from(row).unwrap_or(0);
88 StatusMark::new(state).paint(band(turn, row, 1), buf, theme);
89 }
90
91 let water = Panel::new(Depth::Deep).title("Deep water").draw(
92 band(stage, 9, stage.height.saturating_sub(9)),
93 buf,
94 theme,
95 );
96 let whale = Rect {
97 width: 24.min(water.width),
98 height: water.height.min(11),
99 ..water
100 };
101 Whale::new(WhaleState::Busy).paint(whale, buf, theme);
102 let life = Rect {
103 x: water.x.saturating_add(whale.width.saturating_add(1)),
104 width: water.width.saturating_sub(whale.width.saturating_add(1)),
105 height: water.height.min(7),
106 ..water
107 };
108 if !life.is_empty() {
109 FishSchool::new(Duration::from_millis(2_400), MotionMode::Full)
110 .density(HabitatDensity::Normal)
111 .paint(life, buf, theme);
112 BubbleField::new(Duration::from_millis(1_200), MotionMode::Full)
113 .density(HabitatDensity::Normal)
114 .paint(life, buf, theme);
115 }
116
117 // The finishing pass, last: one cohesive atmosphere across the scene.
118 Ombre::new(palette).apply(area, buf, theme);
119 }
120
121 fn ocean(area: Rect, buf: &mut Buffer, theme: &Theme) {
122 scene(area, buf, theme, WaterPalette::Ocean);
123 }
124
125 fn lagoon(area: Rect, buf: &mut Buffer, theme: &Theme) {
126 scene(area, buf, theme, WaterPalette::Lagoon);
127 }
128
129 fn dusk(area: Rect, buf: &mut Buffer, theme: &Theme) {
130 scene(area, buf, theme, WaterPalette::Dusk);
131 }
132
133 fn coral(area: Rect, buf: &mut Buffer, theme: &Theme) {
134 scene(area, buf, theme, WaterPalette::Coral);
135 }
136
137 fn graphite(area: Rect, buf: &mut Buffer, theme: &Theme) {
138 scene(area, buf, theme, WaterPalette::Graphite);
139 }
140
141 pub(crate) fn entries() -> Vec<Entry> {
142 vec![
143 Entry {
144 name: "atmosphere-ocean",
145 width: 104,
146 height: 24,
147 draw: ocean,
148 },
149 Entry {
150 name: "atmosphere-lagoon",
151 width: 104,
152 height: 24,
153 draw: lagoon,
154 },
155 Entry {
156 name: "atmosphere-dusk",
157 width: 104,
158 height: 24,
159 draw: dusk,
160 },
161 Entry {
162 name: "atmosphere-coral",
163 width: 104,
164 height: 24,
165 draw: coral,
166 },
167 Entry {
168 name: "atmosphere-graphite",
169 width: 104,
170 height: 24,
171 draw: graphite,
172 },
173 ]
174 }
175
175 lines RUST