diff --git a/src/apps.rs b/src/apps.rs index 4e71d2b..4c9bfd9 100644 --- a/src/apps.rs +++ b/src/apps.rs @@ -2,9 +2,9 @@ pub struct BeoApps { pub apps: Vec>, } -struct MainMenu { - pub last_id: usize, - pub default_id: usize, +pub struct MainMenu { + // Can either be the default, or the current + pub selected_id: usize, pub names: Vec, } @@ -21,6 +21,7 @@ impl AppBase { pub trait App { fn base(&self) -> &AppBase; + fn base_mut(&mut self) -> &mut AppBase; // fn main_menu(&self) -> &MainMenu; } @@ -34,12 +35,15 @@ impl Spotify { base: AppBase { name: "Spotify".to_string(), main_menu: MainMenu { - last_id: 0, - default_id: 0, + selected_id: 0, names: vec![ "Playlists".to_string(), "Artists".to_string(), "Albums".to_string(), + "Songs".to_string(), + "Genres".to_string(), + "New Releases".to_string(), + "Charts".to_string(), ], }, }, @@ -51,6 +55,9 @@ impl App for Spotify { fn base(&self) -> &AppBase { &self.base } + fn base_mut(&mut self) -> &mut AppBase { + &mut self.base + } } // Similar implementations for other apps like Radio and Settings @@ -64,8 +71,7 @@ impl Radio { base: AppBase { name: "Radio".to_string(), main_menu: MainMenu { - last_id: 0, - default_id: 0, + selected_id: 0, names: vec![ "Favorites".to_string(), "Local".to_string(), @@ -81,6 +87,9 @@ impl App for Radio { fn base(&self) -> &AppBase { &self.base } + fn base_mut(&mut self) -> &mut AppBase { + &mut self.base + } } struct Settings { @@ -93,8 +102,7 @@ impl Settings { base: AppBase { name: "Settings".to_string(), main_menu: MainMenu { - last_id: 0, - default_id: 0, + selected_id: 0, names: vec![ "Display".to_string(), "Sound".to_string(), @@ -110,6 +118,9 @@ impl App for Settings { fn base(&self) -> &AppBase { &self.base } + fn base_mut(&mut self) -> &mut AppBase { + &mut self.base + } } pub fn get_beo_apps() -> BeoApps { diff --git a/src/hid.rs b/src/hid.rs index e1a7838..aeb3d14 100644 --- a/src/hid.rs +++ b/src/hid.rs @@ -133,23 +133,25 @@ impl Beo5Device { _ => {} } } - evdev::InputEventKind::Switch(switch) => { - /* + evdev::InputEventKind::Key(switch) => { match switch { - evdev::SwitchType::LE=> { + evdev::Key::BTN_DPAD_RIGHT => { if ev.value() == 1 { - return Beo5Event::RightButtonPressed; + return Some(Beo5Event::RightButtonPressed); } else { - return Beo5Event::RightButtonReleased; + return Some(Beo5Event::RightButtonReleased); } } - evdev::Switch::SW_LEFT => { + evdev::Key::BTN_DPAD_LEFT => { if ev.value() == 1 { - return Beo5Event::LeftButtonPressed; - } else { - return Beo5Event::LeftButtonReleased; + if ev.value() == 1 { + return Some(Beo5Event::LeftButtonPressed); + } else { + return Some(Beo5Event::LeftButtonReleased); + } } } + /* evdev::Switch::SW_GO => { if ev.value() == 1 { return Beo5Event::GoButtonPressed; @@ -164,12 +166,12 @@ impl Beo5Device { return Beo5Event::PowerButtonReleased; } } + */ // Shouldn't be any other switches _ => { panic!("Unknown switch event: {ev:?}") } } - */ } _ => {} //SYN et al } diff --git a/src/main.rs b/src/main.rs index aa3a337..32c8e68 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,9 +13,13 @@ use winit::{ mod helpers; use helpers::PerfGraph; +// XXX rename to smth else struct Fonts { sans: FontId, bold: FontId, + app_title: Paint, + app_menu: Paint, + app_menu_selected: Paint, } fn main() { @@ -41,13 +45,36 @@ fn run( surface: glutin::surface::Surface, window: Window, ) { + let font_bold = canvas + .add_font_mem(&resource!("assets/Roboto-Bold.ttf")) + .expect("Cannot add font"); + let font_sans = canvas + .add_font_mem(&resource!("assets/Roboto-Regular.ttf")) + .expect("Cannot add font"); + + let mut app_title = Paint::color(Color::hex("FFFFFF")); + app_title.set_font(&[font_bold]); + app_title.set_text_baseline(Baseline::Top); + app_title.set_text_align(Align::Center); + app_title.set_font_size(20.); + + let mut app_menu = Paint::color(Color::hex("F0F0FF")); + app_menu.set_font(&[font_sans]); + app_menu.set_text_baseline(Baseline::Top); + app_menu.set_text_align(Align::Center); + app_menu.set_font_size(14.); + let mut app_menu_selected = Paint::color(Color::hex("F0F0FF")); + app_menu_selected.set_font(&[font_bold]); + app_menu_selected.set_text_baseline(Baseline::Top); + app_menu_selected.set_text_align(Align::Center); + app_menu_selected.set_font_size(14.); + let fonts = Fonts { - sans: canvas - .add_font_mem(&resource!("assets/Roboto-Regular.ttf")) - .expect("Cannot add font"), - bold: canvas - .add_font_mem(&resource!("assets/Roboto-Bold.ttf")) - .expect("Cannot add font"), + sans: font_sans, + bold: font_bold, + app_title, + app_menu, + app_menu_selected, }; let apps = get_beo_apps(); diff --git a/src/ui.rs b/src/ui.rs index 56a2900..cde188a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -6,9 +6,14 @@ use crate::apps::{App, BeoApps}; use crate::roundy_math; pub struct BeoUi { - pub beo_apps: BeoApps, - pub current_app_id: Option, - pub laser_pct: f32, + beo_apps: BeoApps, + current_app_id: Option, + + // Basically the id of the current menu. However it is float, because + // it's UI and it will move smoothly on the screen. + // XXX for items in menu + //displayed_menu_item_id: Option, + laser_pct: f32, // XXX this could be removed as only used when laser moved, could be given to fn directly } const CANVAS_HEIGHT: f32 = 768.; @@ -36,6 +41,18 @@ impl BeoUi { self.laser_pct = pct; self.choose_app_from_laser_pct(); } + Beo5Event::LeftButtonPressed => { + println!("Left button pressed"); + if let Some(selected_app) = self.current_app_mut() { + selected_app.base_mut().main_menu.selected_id += 1; + } + } + Beo5Event::RightButtonPressed => { + println!("Right button pressed"); + if let Some(selected_app) = self.current_app_mut() { + selected_app.base_mut().main_menu.selected_id -= 1; + } + } _ => { // TODO: pass event to current app } @@ -44,18 +61,9 @@ impl BeoUi { pub fn draw(&self, canvas: &mut Canvas, fonts: &Fonts) { self.draw_main_menu(canvas, fonts); + if let Some(selected_app) = self.current_app() { - let mut paint_title = Paint::color(Color::hex("FFFFFF")); - paint_title.set_font(&[fonts.bold]); - paint_title.set_text_baseline(Baseline::Top); - paint_title.set_text_align(Align::Center); - paint_title.set_font_size(20.); - let _ = canvas.fill_text( - CANVAS_WIDTH / 2., - 10., - selected_app.base().name(), - &paint_title, - ); + self.draw_app(canvas, fonts, selected_app); } } @@ -75,7 +83,6 @@ impl BeoUi { if delta < LASER_EPS_MATCH { self.current_app_id = Some(i); return; - //Some(&self.beo_apps.apps[i]); } } self.current_app_id = None; @@ -84,6 +91,9 @@ impl BeoUi { fn current_app(&self) -> Option<&Box> { self.current_app_id.map(|id| &self.beo_apps.apps[id]) } + fn current_app_mut(&mut self) -> Option<&mut Box> { + self.current_app_id.map(|id| &mut self.beo_apps.apps[id]) + } fn draw_main_menu(&self, canvas: &mut Canvas, fonts: &Fonts) { let canvas_width = canvas.width(); @@ -103,10 +113,11 @@ impl BeoUi { y: canvas_height as f32, }; - let apps = &self.beo_apps.apps; // draw the main apps in the circle + let apps = &self.beo_apps.apps; let pts = main_menu_circle.get_equidistant_points(apps.len(), canvas_size); + // XXX To be taken from global struct let mut paint_normal = Paint::color(Color::hex("B7410E")); paint_normal.set_font(&[fonts.sans]); paint_normal.set_text_baseline(Baseline::Top); @@ -115,15 +126,16 @@ impl BeoUi { paint_selected.set_font(&[fonts.bold]); paint_selected.set_text_baseline(Baseline::Top); - let mut i = 0; - for app in apps { - //if self.current_app == Some(app) { - // let _ = - // canvas.fill_text(pts[i].x, pts[i].y, apps[i].base().name(), &paint_selected); - //} else { - let _ = canvas.fill_text(pts[i].x, pts[i].y, app.base().name(), &paint_normal); - // } - i += 1; + for appid in 0..apps.len() { + let paint; + if self.current_app_id == Some(appid) { + paint = &paint_selected; + } else { + paint = &paint_normal; + } + + let app = &apps[appid]; + let _ = canvas.fill_text(pts[appid].x, pts[appid].y, app.base().name(), paint); } // draw the laser @@ -135,4 +147,72 @@ impl BeoUi { path.ellipse(ex + 15., ey, 30., 10.); canvas.fill_path(&path, &ellipse_color); } + + fn draw_app(&self, canvas: &mut Canvas, fonts: &Fonts, app: &Box) { + /* + let biggest_menu_name = app + .base() + .main_menu + .names + .iter() + .max_by_key(|x| x.len()) + .unwrap(); + + let menu_separated_px = biggest_menu_name.len() as f32 * 8.; + */ + + let num_menu_elements_before = 1; + let num_menu_elements_after = 2; + // That means max 1 + >1< + 2 = 4 elements in total + // Minimum aronud 3: 0 + >1< + 2 + + let current_menu_id = app.base().main_menu.selected_id; + let menu_elements = &app.base().main_menu.names; + + // Calculate indices for slices + let start_before = + (current_menu_id as isize - num_menu_elements_before as isize).max(0) as usize; + let end_before = current_menu_id; + let start_after = current_menu_id + 1; + let end_after = + ((current_menu_id + 1 + num_menu_elements_after).min(menu_elements.len())) as usize; + + // Safely getting slices using clamping + let menu_elems_before = &menu_elements[start_before..end_before]; + let menu_elem_selected = &menu_elements[current_menu_id]; + let menu_elems_after = &menu_elements[start_after..end_after]; + + //println!("menu_elems_before: {:?}", menu_elems_before); + //println!("current_menu: {:?}", menu_elem_selected); + //println!("menu_elems_after: {:?}", menu_elems_after); + + let mut toti = 0.; + for i in 0..menu_elems_before.len() { + let _ = canvas.fill_text( + toti * 100. + 50., + CANVAS_HEIGHT - 80., + &menu_elems_before[i], + &fonts.app_menu, + ); + toti += 1.; + } + + let _ = canvas.fill_text( + toti * 100. + 50., + CANVAS_HEIGHT - 80., + &menu_elem_selected, + &fonts.app_menu_selected, + ); + toti += 1.; + + for i in 0..menu_elems_after.len() { + let _ = canvas.fill_text( + toti * 100. + 50., + CANVAS_HEIGHT - 80., + &menu_elems_after[i], + &fonts.app_menu, + ); + toti += 1.; + } + } }