Krolyxon
Commit 99a16bb

Branches

Refs →

budctlcratesbudctl-guisrcoverview.rs

139 lines · 4.3 KB · Rust

1//! The Overview page: battery, noise control, sound, and the buds' other settings.
2
3use budctl_core::status::{AncLevel, Charge, NoiseMode, Switch};
4use iced::Element;
5
6use crate::demo::Buds;
7use crate::glyphs;
8use crate::theme::Look;
9use crate::ui::{self, Opt};
10use crate::{Message, Page};
11
12/// The switches the "sound" section shows, in this order. The others go under "buds".
13const SOUND: [&str; 3] = ["game", "spatial", "bass-boost"];
14
15/// The line under a switch's name, for the switches the GUI knows.
16fn about(key: &str) -> Option<&'static str> {
17 match key {
18 "game" => Some("lower latency, for games and video"),
19 "spatial" => Some("wider, surround-like sound"),
20 "bass-boost" => Some("stronger low end"),
21 "wind" => Some("less wind noise outdoors"),
22 "dual" => Some("stay connected to two devices at once"),
23 _ => None,
24 }
25}
26
27pub fn view<'a>(look: Look, buds: &'a Buds, anc_level: AncLevel) -> Element<'a, Message> {
28 let status = &buds.status;
29 let switch = |s: &Switch| {
30 ui::switch_row(
31 look,
32 s.label.to_lowercase(),
33 about(s.key),
34 s.on,
35 Message::Switch(s.key, !s.on),
36 )
37 };
38
39 let mut sound = vec![ui::section(look, "sound")];
40 sound.extend(
41 SOUND
42 .iter()
43 .filter_map(|key| status.switches.iter().find(|s| s.key == *key))
44 .map(switch),
45 );
46 sound.push(ui::link_row(
47 look,
48 "equalizer",
49 Some("presets, and your own"),
50 Some(buds.eq_name.clone()),
51 Message::Open(Page::Equalizer),
52 ));
53
54 let mut rest = vec![ui::section(look, "buds")];
55 rest.extend(status.switches.iter().filter(|s| !SOUND.contains(&s.key)).map(switch));
56 rest.push(ui::link_row(
57 look,
58 "gestures",
59 Some("taps and holds on each bud"),
60 None,
61 Message::Open(Page::Gestures),
62 ));
63 rest.push(ui::link_row(
64 look,
65 "earbuds",
66 Some("find my earbuds, firmware"),
67 None,
68 Message::Open(Page::Earbuds),
69 ));
70
71 let mut sections = vec![battery(look, buds)];
72 sections.extend(
73 status
74 .noise_control
75 .as_ref()
76 .map(|now| noise_control(look, &now.mode, anc_level)),
77 );
78 sections.extend([sound, rest]);
79 ui::sections(sections)
80}
81
82fn battery<'a>(look: Look, buds: &Buds) -> Vec<Element<'a, Message>> {
83 let p = look.p;
84 let bar = |name: &'a str, charge: Option<Charge>| match charge {
85 Some(charge) => {
86 let low = charge.level <= 15 && !charge.charging;
87 let value = if charge.charging {
88 format!("{}% charging", charge.level)
89 } else {
90 format!("{}%", charge.level)
91 };
92 let fraction = f32::from(charge.level.min(100)) / 100.0;
93 ui::stat_bar(look, name, fraction, value, if low { p.err } else { p.accent })
94 }
95 None => ui::stat_bar(look, name, 0.0, "unknown".to_owned(), p.accent),
96 };
97 let battery = buds.status.battery;
98 vec![
99 ui::section(look, "battery"),
100 bar("left", battery.left),
101 bar("right", battery.right),
102 bar("case", battery.case),
103 ]
104}
105
106fn noise_control<'a>(look: Look, mode: &str, anc_level: AncLevel) -> Vec<Element<'a, Message>> {
107 let modes = [
108 (glyphs::NOISE_OFF, "off", NoiseMode::Off),
109 (glyphs::ANC, "noise cancelling", NoiseMode::Anc(anc_level)),
110 (glyphs::TRANSPARENCY, "transparency", NoiseMode::Transparency),
111 ];
112 let options = modes
113 .into_iter()
114 .map(|(glyph, words, set)| Opt {
115 glyph: Some(glyph),
116 words,
117 chosen: set.name() == mode,
118 on_press: Message::Noise(set),
119 })
120 .collect();
121 let mut rows = vec![ui::section(look, "noise control"), ui::choice(look, "mode", options)];
122 if mode == NoiseMode::Anc(anc_level).name() {
123 let levels = [
124 (AncLevel::Light, "light"),
125 (AncLevel::Medium, "medium"),
126 (AncLevel::Deep, "deep"),
127 ]
128 .into_iter()
129 .map(|(level, words)| Opt {
130 glyph: None,
131 words,
132 chosen: level == anc_level,
133 on_press: Message::Noise(NoiseMode::Anc(level)),
134 })
135 .collect();
136 rows.push(ui::choice(look, "strength", levels));
137 }
138 rows
139}