| @@ -311,6 +311,8 @@ enum Message { |
| 311 | 311 | | DeletePreset(u8), |
| 312 | 312 | | /// The answer to "Delete the preset?". |
| 313 | 313 | | ConfirmDelete(bool), |
| 314 | + | /// The answer to "Turn dual connection off?". |
| 315 | + | ConfirmDual(bool), |
| 314 | 316 | | ShowHelp, |
| 315 | 317 | | /// Read the buds again. |
| 316 | 318 | | Refresh, |
| @@ -366,6 +368,8 @@ struct App { |
| 366 | 368 | | confirm_delete: Option<u8>, |
| 367 | 369 | | /// The custom preset whose bands are open on the Equalizer page. |
| 368 | 370 | | editing: Option<u8>, |
| 371 | + | /// "Turn dual connection off?" is open. |
| 372 | + | confirm_dual: bool, |
| 369 | 373 | | } |
| 370 | 374 | | |
| 371 | 375 | | impl App { |
| @@ -406,6 +410,7 @@ impl App { |
| 406 | 410 | | prompt: None, |
| 407 | 411 | | confirm_delete: None, |
| 408 | 412 | | editing: None, |
| 413 | + | confirm_dual: false, |
| 409 | 414 | | }; |
| 410 | 415 | | app.refresh_palette(); |
| 411 | 416 | | app |
| @@ -415,7 +420,15 @@ impl App { |
| 415 | 420 | | match message { |
| 416 | 421 | | Message::Open(page) => return self.open(page), |
| 417 | 422 | | Message::Noise(mode) => self.write(Item::NoiseMode, Command::Noise(mode)), |
| 423 | + | // Turning dual connection off may drop one of two connected devices: ask first. |
| 424 | + | Message::Switch(earbuds::DUAL, false) => self.confirm_dual = true, |
| 418 | 425 | | Message::Switch(key, on) => self.write(Item::Switch(key), Command::Switch(key, on)), |
| 426 | + | Message::ConfirmDual(yes) => { |
| 427 | + | self.confirm_dual = false; |
| 428 | + | if yes { |
| 429 | + | self.write(Item::Switch(earbuds::DUAL), Command::Switch(earbuds::DUAL, false)); |
| 430 | + | } |
| 431 | + | } |
| 419 | 432 | | Message::Preset(id) => self.select(id), |
| 420 | 433 | | Message::Band { preset, hz, gain, save } => { |
| 421 | 434 | | self.move_band(preset, hz, gain); |
| @@ -947,6 +960,11 @@ impl App { |
| 947 | 960 | | } |
| 948 | 961 | | |
| 949 | 962 | | fn key(&mut self, key: &Key, modifiers: Modifiers) -> Task<Message> { |
| 963 | + | if self.confirm_dual { |
| 964 | + | // Only Enter turns it off; any other key says no. |
| 965 | + | let yes = matches!(key, Key::Named(keyboard::key::Named::Enter)); |
| 966 | + | return self.update(Message::ConfirmDual(yes)); |
| 967 | + | } |
| 950 | 968 | | if self.confirm_delete.is_some() { |
| 951 | 969 | | // Only Enter deletes; any other key says no. |
| 952 | 970 | | let yes = matches!(key, Key::Named(keyboard::key::Named::Enter)); |
| @@ -1008,7 +1026,7 @@ impl App { |
| 1008 | 1026 | | Page::Overview if self.has_reading() => overview::items(&self.buds), |
| 1009 | 1027 | | Page::Equalizer if self.has_reading() => equalizer::items(&self.buds, self.editing), |
| 1010 | 1028 | | Page::Gestures if self.has_reading() => gestures::items(&self.buds), |
| 1011 | | − | Page::Earbuds if self.has_reading() => earbuds::items(), |
| 1029 | + | Page::Earbuds if self.has_reading() => earbuds::items(&self.buds), |
| 1012 | 1030 | | _ => Vec::new(), |
| 1013 | 1031 | | } |
| 1014 | 1032 | | } |
| @@ -1271,6 +1289,8 @@ impl App { |
| 1271 | 1289 | | stack![window, self.name_prompt(prompt)].into() |
| 1272 | 1290 | | } else if let Some(id) = self.confirm_delete { |
| 1273 | 1291 | | stack![window, self.confirm_delete(id)].into() |
| 1292 | + | } else if self.confirm_dual { |
| 1293 | + | stack![window, self.confirm_dual()].into() |
| 1274 | 1294 | | } else { |
| 1275 | 1295 | | window.into() |
| 1276 | 1296 | | } |
| @@ -1421,6 +1441,34 @@ impl App { |
| 1421 | 1441 | | opaque(backdrop) |
| 1422 | 1442 | | } |
| 1423 | 1443 | | |
| 1444 | + | /// "Turn dual connection off?", over a dimmed window: with two devices connected, the buds may |
| 1445 | + | /// drop one. |
| 1446 | + | fn confirm_dual(&self) -> Element<'_, Message> { |
| 1447 | + | let look = self.look; |
| 1448 | + | let p = look.p; |
| 1449 | + | let words = column![ |
| 1450 | + | text("Turn dual connection off?").size(22).font(look.bold()).color(p.fg), |
| 1451 | + | text("If two devices are connected to your buds, they may drop one of them.") |
| 1452 | + | .size(14) |
| 1453 | + | .color(p.dim), |
| 1454 | + | ] |
| 1455 | + | .spacing(8); |
| 1456 | + | let buttons = row![ |
| 1457 | + | space::horizontal(), |
| 1458 | + | ui::pill(look, "Cancel", false, Message::ConfirmDual(false)), |
| 1459 | + | ui::pill(look, "Turn off", true, Message::ConfirmDual(true)), |
| 1460 | + | ] |
| 1461 | + | .spacing(10); |
| 1462 | + | let dialog = ui::panel(look, column![words, buttons].spacing(22).into()); |
| 1463 | + | let backdrop = container(container(dialog).width(460)) |
| 1464 | + | .center(Length::Fill) |
| 1465 | + | .style(|_| container::Style { |
| 1466 | + | background: Some(Color { a: 0.6, ..Color::BLACK }.into()), |
| 1467 | + | ..container::Style::default() |
| 1468 | + | }); |
| 1469 | + | opaque(backdrop) |
| 1470 | + | } |
| 1471 | + | |
| 1424 | 1472 | | /// "Delete the preset?", over a dimmed window: its bands are lost. |
| 1425 | 1473 | | fn confirm_delete(&self, id: u8) -> Element<'_, Message> { |
| 1426 | 1474 | | let look = self.look; |
| @@ -1994,7 +2042,7 @@ mod tests { |
| 1994 | 2042 | | #[test] |
| 1995 | 2043 | | fn ringing_needs_a_yes() { |
| 1996 | 2044 | | let mut app = app(); |
| 1997 | | − | press(&mut app, "4 j Enter"); |
| 2045 | + | press(&mut app, "4 j j Enter"); |
| 1998 | 2046 | | assert_eq!(app.page, Page::Earbuds); |
| 1999 | 2047 | | assert!(app.confirm_find); |
| 2000 | 2048 | | press(&mut app, "Esc"); |
| @@ -2178,6 +2226,32 @@ mod tests { |
| 2178 | 2226 | | assert!(!bands(&app)); |
| 2179 | 2227 | | } |
| 2180 | 2228 | | |
| 2229 | + | #[test] |
| 2230 | + | fn dual_connection_asks_before_going_off() { |
| 2231 | + | let mut app = app(); |
| 2232 | + | press(&mut app, "4 j"); |
| 2233 | + | assert_eq!(app.focus(), Some(Item::Switch("dual")), "first on Earbud settings"); |
| 2234 | + | press(&mut app, "Enter"); |
| 2235 | + | assert!(app.confirm_dual); |
| 2236 | + | press(&mut app, "Esc"); |
| 2237 | + | assert!(!app.confirm_dual); |
| 2238 | + | assert!(switch(&app, "dual")); |
| 2239 | + | press(&mut app, "Enter Enter"); |
| 2240 | + | assert!(!switch(&app, "dual")); |
| 2241 | + | // Turning it on asks nothing. |
| 2242 | + | press(&mut app, "Enter"); |
| 2243 | + | assert!(!app.confirm_dual); |
| 2244 | + | assert!(switch(&app, "dual")); |
| 2245 | + | |
| 2246 | + | let (mut app, commands) = live(); |
| 2247 | + | let _ = commands.try_recv(); |
| 2248 | + | let _ = app.update(Message::Worker(Event::Read(Box::new(reading())))); |
| 2249 | + | let _ = app.update(Message::Switch("dual", false)); |
| 2250 | + | assert!(commands.try_recv().is_err()); |
| 2251 | + | let _ = app.update(Message::ConfirmDual(true)); |
| 2252 | + | assert!(matches!(commands.try_recv(), Ok(Command::Switch("dual", false)))); |
| 2253 | + | } |
| 2254 | + | |
| 2181 | 2255 | | #[test] |
| 2182 | 2256 | | fn steps() { |
| 2183 | 2257 | | let abc = ['a', 'b', 'c']; |
| @@ -2235,7 +2309,7 @@ mod tests { |
| 2235 | 2309 | | shots.push(("equalizer-cursor".to_owned(), 1100.0, editing)); |
| 2236 | 2310 | | shots.push(("equalizer-pencil".to_owned(), 800.0, on(Page::Equalizer, "j j j j j j"))); |
| 2237 | 2311 | | shots.push(("gestures-cursor".to_owned(), 1000.0, on(Page::Gestures, "j j"))); |
| 2238 | | − | shots.push(("find-confirm".to_owned(), 800.0, on(Page::Earbuds, "j Enter"))); |
| 2312 | + | shots.push(("find-confirm".to_owned(), 800.0, on(Page::Earbuds, "j j Enter"))); |
| 2239 | 2313 | | shots.push(( |
| 2240 | 2314 | | "overview-bass".to_owned(), |
| 2241 | 2315 | | 1340.0, |
| @@ -2247,6 +2321,9 @@ mod tests { |
| 2247 | 2321 | | let _ = naming.update(Message::SubmitName); |
| 2248 | 2322 | | shots.push(("name-prompt".to_owned(), 800.0, naming)); |
| 2249 | 2323 | | shots.push(("equalizer-builtin".to_owned(), 1000.0, on(Page::Equalizer, "j l"))); |
| 2324 | + | let mut dual = on(Page::Earbuds, "j"); |
| 2325 | + | let _ = dual.update(Message::Switch("dual", false)); |
| 2326 | + | shots.push(("dual-confirm".to_owned(), 800.0, dual)); |
| 2250 | 2327 | | let mut deleting = on(Page::Equalizer, ""); |
| 2251 | 2328 | | let _ = deleting.update(Message::DeletePreset(4)); |
| 2252 | 2329 | | shots.push(("delete-confirm".to_owned(), 800.0, deleting)); |