Added some better headers, layouts and joystick canvases

master
Wynd 2024-10-19 19:59:11 +03:00
parent fe162b60ff
commit c653bad935
1 changed files with 115 additions and 96 deletions

View File

@ -9,7 +9,8 @@ use ratatui::{
style::{palette::tailwind, Color, Style, Stylize}, style::{palette::tailwind, Color, Style, Stylize},
symbols::Marker, symbols::Marker,
widgets::{ widgets::{
canvas::{Canvas, Circle, Shape}, block::Position,
canvas::{Canvas, Circle, Rectangle, Shape},
Block, BorderType, Gauge, Padding, Paragraph, Widget, Block, BorderType, Gauge, Padding, Paragraph, Widget,
}, },
DefaultTerminal, DefaultTerminal,
@ -18,6 +19,10 @@ use Constraint::{Fill, Length, Min, Percentage};
use crate::gamepad_manager::GamepadManager; 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 struct App {
pub manager: GamepadManager, pub manager: GamepadManager,
@ -96,39 +101,13 @@ impl App {
self.state = AppState::Quitting; 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) { fn render_action_buttons(&self, area: Rect, buf: &mut Buffer) {
Block::bordered().render(area, buf); Block::bordered().render(area, buf);
let north_button = self.create_action_button_ui(gilrs::Button::North); let north_button = self.create_button_ui(gilrs::Button::North, "Y");
let east_button = self.create_action_button_ui(gilrs::Button::East); let east_button = self.create_button_ui(gilrs::Button::East, "B");
let south_button = self.create_action_button_ui(gilrs::Button::South); let south_button = self.create_button_ui(gilrs::Button::South, "A");
let west_button = self.create_action_button_ui(gilrs::Button::West); let west_button = self.create_button_ui(gilrs::Button::West, "X");
let layers = Layout::vertical([Fill(1), Fill(1), Fill(1)]); let layers = Layout::vertical([Fill(1), Fill(1), Fill(1)]);
let [top, mid, bot] = layers.areas(area); let [top, mid, bot] = layers.areas(area);
@ -151,10 +130,10 @@ impl App {
fn render_dpad_buttons(&self, area: Rect, buf: &mut Buffer) { fn render_dpad_buttons(&self, area: Rect, buf: &mut Buffer) {
Block::bordered().render(area, buf); Block::bordered().render(area, buf);
let up_button = self.create_action_button_ui(gilrs::Button::DPadUp); let up_button = self.create_button_ui(gilrs::Button::DPadUp, "Up");
let right_button = self.create_action_button_ui(gilrs::Button::DPadRight); let right_button = self.create_button_ui(gilrs::Button::DPadRight, "Right");
let down_button = self.create_action_button_ui(gilrs::Button::DPadDown); let down_button = self.create_button_ui(gilrs::Button::DPadDown, "Down");
let left_button = self.create_action_button_ui(gilrs::Button::DPadLeft); let left_button = self.create_button_ui(gilrs::Button::DPadLeft, "Left");
let layers = Layout::vertical([Fill(1), Fill(1), Fill(1)]); let layers = Layout::vertical([Fill(1), Fill(1), Fill(1)]);
let [top, mid, bot] = layers.areas(area); let [top, mid, bot] = layers.areas(area);
@ -174,32 +153,28 @@ impl App {
left_button.render(left, buf); left_button.render(left, buf);
} }
fn render_setting_buttons(&self, area: Rect, buf: &mut Buffer) { fn render_menu_buttons(&self, area: Rect, buf: &mut Buffer) {
Block::bordered().render(area, buf); let select_button = self.create_button_ui(gilrs::Button::Select, "Select");
let start_button = self.create_button_ui(gilrs::Button::Start, "Start");
let select_button = self.create_action_button_ui(gilrs::Button::Select);
let start_button = self.create_action_button_ui(gilrs::Button::Start);
let layers = Layout::horizontal([Fill(1), Fill(1), Fill(1), Fill(1), Fill(1)]); let layers = Layout::horizontal([Fill(1), Fill(1), Fill(1), Fill(1), Fill(1)]);
let [_, select_area, _, start_area, _] = layers.areas(area); let [_, select_area, _, start_area, _] = layers.areas(area);
let select_layer = Layout::vertical([Fill(1), Fill(1), Fill(1), Fill(1)]); let select_layer = Layout::vertical([Fill(1), Fill(1), Fill(5)]);
let [_, select, _, _] = select_layer.areas(select_area); let [_, select, _] = select_layer.areas(select_area);
let start_layer = Layout::vertical([Fill(1), Fill(1), Fill(1), Fill(1)]); let start_layer = Layout::vertical([Fill(1), Fill(1), Fill(5)]);
let [_, start, _, _] = start_layer.areas(start_area); let [_, start, _] = start_layer.areas(start_area);
select_button.render(select, buf); select_button.render(select, buf);
start_button.render(start, buf); start_button.render(start, buf);
} }
fn render_trigger_buttons(&self, area: Rect, buf: &mut Buffer) { fn render_trigger_buttons(&self, area: Rect, buf: &mut Buffer) {
Block::bordered().render(area, buf); let r1_button = self.create_button_ui(gilrs::Button::RightTrigger, "R1");
let r2_button = self.create_button_ui(gilrs::Button::RightTrigger2, "R2");
let r1_button = self.create_action_button_ui(gilrs::Button::RightTrigger); let l1_button = self.create_button_ui(gilrs::Button::LeftTrigger, "L1");
let r2_button = self.create_action_button_ui(gilrs::Button::RightTrigger2); let l2_button = self.create_button_ui(gilrs::Button::LeftTrigger2, "L2");
let l1_button = self.create_action_button_ui(gilrs::Button::LeftTrigger);
let l2_button = self.create_action_button_ui(gilrs::Button::LeftTrigger2);
let layers = Layout::horizontal([ let layers = Layout::horizontal([
Fill(2), Fill(2),
@ -214,16 +189,16 @@ impl App {
]); ]);
let [_, l2_area, _, l1_area, _, r1_area, _, r2_area, _] = layers.areas(area); 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 [_, 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 [_, 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 [_, 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); let [_, l2, _] = l2_layer.areas(l2_area);
r1_button.render(r1, buf); r1_button.render(r1, buf);
@ -248,32 +223,103 @@ impl App {
// .render(r2, buf); // .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() { let color = match self.manager.active_gamepad() {
Ok(gamepad) => match gamepad.is_pressed(dir) { Ok(gamepad) => match gamepad.is_pressed(btn) {
true => Color::Green, true => Color::Green,
false => Color::DarkGray, false => Color::DarkGray,
}, },
Err(_) => 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 { impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let layout = Layout::vertical([Percentage(100)]); let layout = Layout::vertical([Length(3), Percentage(100)]);
let [inner_area] = layout.areas(area); let [header_area, inner_area] = layout.areas(area);
let gamepad_layout = Layout::vertical([Fill(2), Fill(4), Fill(3)]); 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 [triggers, buttons, _, joysticks, _] = gamepad_layout.areas(inner_area);
let buttons_layout = Layout::horizontal([Fill(2), Fill(2), Fill(2)]); 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 [_, 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 mut title_label = Block::bordered(); let mut title_label = Block::bordered();
title_label = match self.manager.active_gamepad() { title_label = match self.manager.active_gamepad() {
@ -282,39 +328,12 @@ impl Widget for &App {
.title_style(Color::Rgb(255, 165, 35)), .title_style(Color::Rgb(255, 165, 35)),
Err(_) => title_label.title("No active device".to_string()), Err(_) => title_label.title("No active device".to_string()),
}; };
title_label.render(inner_area, buf); title_label.render(header_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);
self.render_dpad_buttons(left_buttons, 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_action_buttons(right_buttons, buf);
self.render_trigger_buttons(triggers, buf); self.render_trigger_buttons(triggers, buf);
self.render_joystick_areas(joysticks, 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);
} }
} }