From c653bad9354a92398b4176a55456cd5a0f7068c2 Mon Sep 17 00:00:00 2001 From: Wynd Date: Sat, 19 Oct 2024 19:59:11 +0300 Subject: [PATCH] Added some better headers, layouts and joystick canvases --- src/app.rs | 211 +++++++++++++++++++++++++++++------------------------ 1 file changed, 115 insertions(+), 96 deletions(-) diff --git a/src/app.rs b/src/app.rs index af1138d..24dc777 100644 --- a/src/app.rs +++ b/src/app.rs @@ -9,7 +9,8 @@ use ratatui::{ style::{palette::tailwind, Color, Style, Stylize}, symbols::Marker, widgets::{ - canvas::{Canvas, Circle, Shape}, + block::Position, + canvas::{Canvas, Circle, Rectangle, Shape}, Block, BorderType, Gauge, Padding, Paragraph, Widget, }, DefaultTerminal, @@ -18,6 +19,10 @@ use Constraint::{Fill, Length, Min, Percentage}; use crate::gamepad_manager::GamepadManager; +const MAX_DEADZONE: f64 = 0.7; +const MIN_DEADZONE: f64 = 0.6; +const REST_DEADZONE: f64 = 0.05; + pub struct App { pub manager: GamepadManager, @@ -96,39 +101,13 @@ impl App { self.state = AppState::Quitting; } - // fn render_title(area: Rect, buf: &mut Buffer) { - // Paragraph::new("App Example Title") - // .block( - // Block::bordered() - // .border_type(BorderType::Rounded) - // .padding(Padding::top(1)), - // ) - // .alignment(Alignment::Center) - // .render(area, buf); - // } - // - // fn render_connected_gamepad(&self, area: Rect, buf: &mut Buffer) { - // Paragraph::new(format!( - // "{} connected gamepads", - // self.manager.connected_gamepads() - // )) - // .block( - // Block::bordered() - // .border_type(BorderType::Rounded) - // .padding(Padding::top(1)), - // ) - // .alignment(Alignment::Center) - // .bold() - // .render(area, buf); - // } - fn render_action_buttons(&self, area: Rect, buf: &mut Buffer) { Block::bordered().render(area, buf); - let north_button = self.create_action_button_ui(gilrs::Button::North); - let east_button = self.create_action_button_ui(gilrs::Button::East); - let south_button = self.create_action_button_ui(gilrs::Button::South); - let west_button = self.create_action_button_ui(gilrs::Button::West); + let north_button = self.create_button_ui(gilrs::Button::North, "Y"); + let east_button = self.create_button_ui(gilrs::Button::East, "B"); + let south_button = self.create_button_ui(gilrs::Button::South, "A"); + let west_button = self.create_button_ui(gilrs::Button::West, "X"); let layers = Layout::vertical([Fill(1), Fill(1), Fill(1)]); let [top, mid, bot] = layers.areas(area); @@ -151,10 +130,10 @@ impl App { fn render_dpad_buttons(&self, area: Rect, buf: &mut Buffer) { Block::bordered().render(area, buf); - let up_button = self.create_action_button_ui(gilrs::Button::DPadUp); - let right_button = self.create_action_button_ui(gilrs::Button::DPadRight); - let down_button = self.create_action_button_ui(gilrs::Button::DPadDown); - let left_button = self.create_action_button_ui(gilrs::Button::DPadLeft); + let up_button = self.create_button_ui(gilrs::Button::DPadUp, "Up"); + let right_button = self.create_button_ui(gilrs::Button::DPadRight, "Right"); + let down_button = self.create_button_ui(gilrs::Button::DPadDown, "Down"); + let left_button = self.create_button_ui(gilrs::Button::DPadLeft, "Left"); let layers = Layout::vertical([Fill(1), Fill(1), Fill(1)]); let [top, mid, bot] = layers.areas(area); @@ -174,32 +153,28 @@ impl App { left_button.render(left, buf); } - fn render_setting_buttons(&self, area: Rect, buf: &mut Buffer) { - Block::bordered().render(area, buf); - - let select_button = self.create_action_button_ui(gilrs::Button::Select); - let start_button = self.create_action_button_ui(gilrs::Button::Start); + fn render_menu_buttons(&self, area: Rect, buf: &mut Buffer) { + let select_button = self.create_button_ui(gilrs::Button::Select, "Select"); + let start_button = self.create_button_ui(gilrs::Button::Start, "Start"); let layers = Layout::horizontal([Fill(1), Fill(1), Fill(1), Fill(1), Fill(1)]); let [_, select_area, _, start_area, _] = layers.areas(area); - let select_layer = Layout::vertical([Fill(1), Fill(1), Fill(1), Fill(1)]); - let [_, select, _, _] = select_layer.areas(select_area); + let select_layer = Layout::vertical([Fill(1), Fill(1), Fill(5)]); + let [_, select, _] = select_layer.areas(select_area); - let start_layer = Layout::vertical([Fill(1), Fill(1), Fill(1), Fill(1)]); - let [_, start, _, _] = start_layer.areas(start_area); + let start_layer = Layout::vertical([Fill(1), Fill(1), Fill(5)]); + let [_, start, _] = start_layer.areas(start_area); select_button.render(select, buf); start_button.render(start, buf); } fn render_trigger_buttons(&self, area: Rect, buf: &mut Buffer) { - Block::bordered().render(area, buf); - - let r1_button = self.create_action_button_ui(gilrs::Button::RightTrigger); - let r2_button = self.create_action_button_ui(gilrs::Button::RightTrigger2); - let l1_button = self.create_action_button_ui(gilrs::Button::LeftTrigger); - let l2_button = self.create_action_button_ui(gilrs::Button::LeftTrigger2); + let r1_button = self.create_button_ui(gilrs::Button::RightTrigger, "R1"); + let r2_button = self.create_button_ui(gilrs::Button::RightTrigger2, "R2"); + let l1_button = self.create_button_ui(gilrs::Button::LeftTrigger, "L1"); + let l2_button = self.create_button_ui(gilrs::Button::LeftTrigger2, "L2"); let layers = Layout::horizontal([ Fill(2), @@ -214,16 +189,16 @@ impl App { ]); let [_, l2_area, _, l1_area, _, r1_area, _, r2_area, _] = layers.areas(area); - let r1_layer = Layout::vertical([Fill(2), Fill(1), Fill(2)]); + let r1_layer = Layout::vertical([Fill(1), Fill(1), Fill(2)]); let [_, r1, _] = r1_layer.areas(r1_area); - let r2_layer = Layout::vertical([Fill(1), Fill(3), Fill(1)]); + let r2_layer = Layout::vertical([Fill(1), Fill(4), Fill(2)]); let [_, r2, _] = r2_layer.areas(r2_area); - let l1_layer = Layout::vertical([Fill(2), Fill(1), Fill(2)]); + let l1_layer = Layout::vertical([Fill(1), Fill(1), Fill(2)]); let [_, l1, _] = l1_layer.areas(l1_area); - let l2_layer = Layout::vertical([Fill(1), Fill(3), Fill(1)]); + let l2_layer = Layout::vertical([Fill(1), Fill(4), Fill(2)]); let [_, l2, _] = l2_layer.areas(l2_area); r1_button.render(r1, buf); @@ -248,32 +223,103 @@ impl App { // .render(r2, buf); } - fn create_action_button_ui(&self, dir: gilrs::Button) -> Block { + fn render_joystick_areas(&self, area: Rect, buf: &mut Buffer) { + let layout = Layout::horizontal([Fill(4), Fill(4), Fill(2), Fill(4), Fill(4)]); + let [_, left, _, right, _] = layout.areas(area); + + self.create_joystick_ui(gilrs::Axis::LeftStickX, gilrs::Axis::LeftStickY) + .render(left, buf); + + self.create_joystick_ui(gilrs::Axis::RightStickX, gilrs::Axis::RightStickY) + .render(right, buf); + } + + fn create_button_ui(&self, btn: gilrs::Button, title: &'static str) -> Block { let color = match self.manager.active_gamepad() { - Ok(gamepad) => match gamepad.is_pressed(dir) { + Ok(gamepad) => match gamepad.is_pressed(btn) { true => Color::Green, false => Color::DarkGray, }, Err(_) => Color::DarkGray, }; - Block::new().style(Style::default().bg(color)) + Block::new() + .title_top(title) + .style(Style::default().bg(color)) + } + + fn create_joystick_ui(&self, axis_x: gilrs::Axis, axis_y: gilrs::Axis) -> impl Widget + '_ { + let (x, y) = match self.manager.active_gamepad() { + Ok(gp) => { + let left_axis = gp.axis_data(axis_x).map_or(0.0, |d| d.value()); // 160.0 + let right_axis = gp.axis_data(axis_y).map_or(0.0, |d| d.value()); // 140.0 + + (left_axis as f64, right_axis as f64) + } + Err(_) => (0.0_f64, 0.0_f64), + }; + + let color = match (x, y) { + (a, b) + if a < REST_DEADZONE + && b < REST_DEADZONE + && a > -REST_DEADZONE + && b > -REST_DEADZONE => + { + Color::DarkGray + } + (a, b) + if a > MAX_DEADZONE + || b > MAX_DEADZONE + || a < -MAX_DEADZONE + || b < -MAX_DEADZONE => + { + Color::Green + } + (a, b) + if a < MIN_DEADZONE + || b < MIN_DEADZONE + || a > -MIN_DEADZONE + || b > -MIN_DEADZONE => + { + Color::Red + } + (_, _) => Color::DarkGray, + }; + + let (x, y) = (x * 160.0, y * 140.0); + + let joystick = Rectangle { + x: x - 20.0, + y: y - 40.0, + width: 40.0, + height: 60.0, + color, + }; + + Canvas::default() + .block( + Block::bordered() + .border_type(BorderType::QuadrantInside) + .border_style(Color::DarkGray), + ) + .marker(Marker::HalfBlock) + .paint(move |ctx| ctx.draw(&joystick)) + .x_bounds([-180.0, 180.0]) + .y_bounds([-180.0, 180.0]) } } impl Widget for &App { fn render(self, area: Rect, buf: &mut Buffer) { - let layout = Layout::vertical([Percentage(100)]); - let [inner_area] = layout.areas(area); + let layout = Layout::vertical([Length(3), Percentage(100)]); + let [header_area, inner_area] = layout.areas(area); - let gamepad_layout = Layout::vertical([Fill(2), Fill(4), Fill(3)]); - let [triggers, buttons, joysticks] = gamepad_layout.areas(inner_area); + let gamepad_layout = Layout::vertical([Fill(8), Fill(10), Fill(2), Fill(10), Fill(2)]); + let [triggers, buttons, _, joysticks, _] = gamepad_layout.areas(inner_area); - let buttons_layout = Layout::horizontal([Fill(2), Fill(2), Fill(2)]); - let [left_buttons, mid_buttons, right_buttons] = buttons_layout.areas(buttons); - - // let details_layout = Layout::horizontal([Fill(2), Fill(2)]); - // let [right_details_area, left_details_area] = details_layout.areas(details_area); + let buttons_layout = Layout::horizontal([Fill(1), Fill(10), Fill(10), Fill(10), Fill(1)]); + let [_, left_buttons, mid_buttons, right_buttons, _] = buttons_layout.areas(buttons); let mut title_label = Block::bordered(); title_label = match self.manager.active_gamepad() { @@ -282,39 +328,12 @@ impl Widget for &App { .title_style(Color::Rgb(255, 165, 35)), Err(_) => title_label.title("No active device".to_string()), }; - title_label.render(inner_area, buf); - - // Block::bordered() - // .style(Style::default().bg(tailwind::GREEN.c900)) - // .render(right_details_area, buf); - - // Block::new() - // .style(Style::default().bg(tailwind::YELLOW.c300)) - // .render(details_area, buf); + title_label.render(header_area, buf); self.render_dpad_buttons(left_buttons, buf); - self.render_setting_buttons(mid_buttons, buf); + self.render_menu_buttons(mid_buttons, buf); self.render_action_buttons(right_buttons, buf); self.render_trigger_buttons(triggers, buf); - - // Block::bordered() - // .style(Style::default().bg(tailwind::TEAL.c300)) - // .render(left_details_area, buf); - - // let gauge_progress: u16 = self - // .tick - // .wrapping_div(100) - // .clamp(0, 100) - // .try_into() - // .unwrap(); - // Gauge::default() - // .gauge_style(tailwind::BLUE.c900) - // .percent(gauge_progress) - // .render(right_details_area, buf); - - // self.render_title(title_area, buf); - // self.render_tabs(tabs_area, buf); - // self.selected_tab.render(inner_area, buf); - // render_footer(footer_area, buf); + self.render_joystick_areas(joysticks, buf); } }