Added some better headers, layouts and joystick canvases
parent
fe162b60ff
commit
c653bad935
211
src/app.rs
211
src/app.rs
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue