mirror of
https://github.com/pythops/bluetui.git
synced 2026-05-06 21:42:28 +08:00
remove help popup and use help banner instead
This commit is contained in:
553
Cargo.lock
generated
553
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Readme.md
10
Readme.md
@@ -36,6 +36,7 @@ pacman -S bluetui
|
||||
### 🐧 Gentoo
|
||||
|
||||
You can install `bluetui` from the [lamdness Gentoo Overlay](https://gpo.zugaina.org/net-wireless/bluetui):
|
||||
|
||||
```sh
|
||||
sudo eselect repository enable lamdness
|
||||
sudo emaint -r lamdness sync
|
||||
@@ -50,7 +51,6 @@ If you are a user of [x-cmd](https://x-cmd.com), you can run:
|
||||
x install bluetui
|
||||
```
|
||||
|
||||
|
||||
### ⚒️ Build from source
|
||||
|
||||
Run the following command:
|
||||
@@ -93,7 +93,7 @@ This will produce an executable file at `target/release/bluetui` that you can co
|
||||
|
||||
`u`: Unpair the device.
|
||||
|
||||
`Space`: Connect/Disconnect the device.
|
||||
`Space or Enter`: Connect/Disconnect the device.
|
||||
|
||||
`t`: Trust/Untrust the device.
|
||||
|
||||
@@ -101,7 +101,7 @@ This will produce an executable file at `target/release/bluetui` that you can co
|
||||
|
||||
### New devices
|
||||
|
||||
`p`: Pair the device.
|
||||
`Space or Enter`: Pair the device.
|
||||
|
||||
## Custom keybindings
|
||||
|
||||
@@ -117,12 +117,8 @@ toggle_discovery = "d"
|
||||
|
||||
[paired_device]
|
||||
unpair = "u"
|
||||
toggle_connect = " "
|
||||
toggle_trust = "t"
|
||||
rename = "e"
|
||||
|
||||
[new_device]
|
||||
pair = "p"
|
||||
```
|
||||
|
||||
## ⚖️ License
|
||||
|
||||
86
src/app.rs
86
src/app.rs
@@ -19,7 +19,6 @@ use crate::{
|
||||
bluetooth::{Controller, request_confirmation},
|
||||
config::Config,
|
||||
confirmation::PairingConfirmation,
|
||||
help::Help,
|
||||
notification::Notification,
|
||||
spinner::Spinner,
|
||||
};
|
||||
@@ -35,7 +34,6 @@ pub enum FocusedBlock {
|
||||
Adapter,
|
||||
PairedDevices,
|
||||
NewDevices,
|
||||
Help,
|
||||
PassKeyConfirmation,
|
||||
SetDeviceAliasBox,
|
||||
}
|
||||
@@ -51,7 +49,6 @@ pub struct App {
|
||||
pub running: bool,
|
||||
pub session: Arc<Session>,
|
||||
pub agent: AgentHandle,
|
||||
pub help: Help,
|
||||
pub spinner: Spinner,
|
||||
pub notifications: Vec<Notification>,
|
||||
pub controllers: Vec<Controller>,
|
||||
@@ -62,6 +59,7 @@ pub struct App {
|
||||
pub pairing_confirmation: PairingConfirmation,
|
||||
pub color_mode: ColorMode,
|
||||
pub new_alias: Input,
|
||||
pub config: Arc<Config>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -110,7 +108,6 @@ impl App {
|
||||
running: true,
|
||||
session,
|
||||
agent: handle,
|
||||
help: Help::new(config),
|
||||
spinner: Spinner::default(),
|
||||
notifications: Vec::new(),
|
||||
controllers,
|
||||
@@ -121,6 +118,7 @@ impl App {
|
||||
pairing_confirmation,
|
||||
color_mode,
|
||||
new_alias: Input::default(),
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -257,7 +255,7 @@ impl App {
|
||||
|
||||
let paired_devices_block_height = selected_controller.paired_devices.len() as u16 + 4;
|
||||
|
||||
let (paired_devices_block, new_devices_block, controller_block) = {
|
||||
let (paired_devices_block, new_devices_block, controller_block, help_block) = {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(if render_new_devices {
|
||||
@@ -265,17 +263,19 @@ impl App {
|
||||
Constraint::Length(paired_devices_block_height),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(adapter_block_height),
|
||||
Constraint::Length(1),
|
||||
]
|
||||
} else {
|
||||
[
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(0),
|
||||
Constraint::Length(adapter_block_height),
|
||||
Constraint::Length(1),
|
||||
]
|
||||
})
|
||||
.margin(1)
|
||||
.split(frame.area());
|
||||
(chunks[0], chunks[1], chunks[2])
|
||||
(chunks[0], chunks[1], chunks[2], chunks[3])
|
||||
};
|
||||
|
||||
//Adapters
|
||||
@@ -718,11 +718,85 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
// Help
|
||||
let help = match self.focused_block {
|
||||
FocusedBlock::PairedDevices => Line::from(vec![
|
||||
Span::from("k,").bold(),
|
||||
Span::from(" Up"),
|
||||
Span::from(" | "),
|
||||
Span::from("j,").bold(),
|
||||
Span::from(" Down"),
|
||||
Span::from(" | "),
|
||||
Span::from("s").bold(),
|
||||
Span::from(" Scan on/off"),
|
||||
Span::from(" | "),
|
||||
Span::from(self.config.paired_device.unpair.to_string()).bold(),
|
||||
Span::from(" Unpair"),
|
||||
Span::from(" | "),
|
||||
Span::from(" or ↵ ").bold(),
|
||||
Span::from(" Dis/Connect"),
|
||||
Span::from(" | "),
|
||||
Span::from(self.config.paired_device.toggle_trust.to_string()).bold(),
|
||||
Span::from(" Un/Trust"),
|
||||
Span::from(" | "),
|
||||
Span::from(self.config.paired_device.rename.to_string()).bold(),
|
||||
Span::from(" Rename"),
|
||||
Span::from(" | "),
|
||||
Span::from("⇄").bold(),
|
||||
Span::from(" Nav"),
|
||||
]),
|
||||
FocusedBlock::NewDevices => Line::from(vec![
|
||||
Span::from("k,").bold(),
|
||||
Span::from(" Up"),
|
||||
Span::from(" | "),
|
||||
Span::from("j,").bold(),
|
||||
Span::from(" Down"),
|
||||
Span::from(" | "),
|
||||
Span::from(" or ↵ ").bold(),
|
||||
Span::from(" Pair"),
|
||||
Span::from(" | "),
|
||||
Span::from("s").bold(),
|
||||
Span::from(" Scan on/off"),
|
||||
Span::from(" | "),
|
||||
Span::from("⇄").bold(),
|
||||
Span::from(" Nav"),
|
||||
]),
|
||||
FocusedBlock::Adapter => Line::from(vec![
|
||||
Span::from("s").bold(),
|
||||
Span::from(" Scan on/off"),
|
||||
Span::from(" | "),
|
||||
Span::from(self.config.adapter.toggle_pairing.to_string()).bold(),
|
||||
Span::from(" Pairing on/off"),
|
||||
Span::from(" | "),
|
||||
Span::from(self.config.adapter.toggle_power.to_string()).bold(),
|
||||
Span::from(" Power on/off"),
|
||||
Span::from(" | "),
|
||||
Span::from(self.config.adapter.toggle_discovery.to_string()).bold(),
|
||||
Span::from(" Discory on/off"),
|
||||
Span::from(" | "),
|
||||
Span::from("⇄").bold(),
|
||||
Span::from(" Nav"),
|
||||
]),
|
||||
FocusedBlock::SetDeviceAliasBox => {
|
||||
Line::from(vec![Span::from(" ").bold(), Span::from(" Discard")])
|
||||
}
|
||||
FocusedBlock::PassKeyConfirmation => {
|
||||
Line::from(vec![Span::from(" ").bold(), Span::from(" Discard")])
|
||||
}
|
||||
};
|
||||
frame.render_widget(help.centered().blue(), help_block);
|
||||
|
||||
// Pairing confirmation
|
||||
|
||||
if self.pairing_confirmation.display.load(Ordering::Relaxed) {
|
||||
self.focused_block = FocusedBlock::PassKeyConfirmation;
|
||||
self.pairing_confirmation.render(frame);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set alias popup
|
||||
if self.focused_block == FocusedBlock::SetDeviceAliasBox {
|
||||
self.render_set_alias(frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ pub struct Config {
|
||||
|
||||
#[serde(default)]
|
||||
pub paired_device: PairedDevice,
|
||||
|
||||
#[serde(default)]
|
||||
pub new_device: NewDevice,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@@ -45,9 +42,6 @@ pub struct PairedDevice {
|
||||
#[serde(default = "default_unpair_device")]
|
||||
pub unpair: char,
|
||||
|
||||
#[serde(default = "default_toggle_device_connection")]
|
||||
pub toggle_connect: char,
|
||||
|
||||
#[serde(default = "default_toggle_device_trust")]
|
||||
pub toggle_trust: char,
|
||||
|
||||
@@ -59,7 +53,6 @@ impl Default for PairedDevice {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unpair: 'u',
|
||||
toggle_connect: ' ',
|
||||
toggle_trust: 't',
|
||||
rename: 'e',
|
||||
}
|
||||
@@ -70,18 +63,6 @@ fn default_set_new_name() -> char {
|
||||
'e'
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct NewDevice {
|
||||
#[serde(default = "default_pair_new_device")]
|
||||
pub pair: char,
|
||||
}
|
||||
|
||||
impl Default for NewDevice {
|
||||
fn default() -> Self {
|
||||
Self { pair: 'p' }
|
||||
}
|
||||
}
|
||||
|
||||
fn default_toggle_scanning() -> char {
|
||||
's'
|
||||
}
|
||||
@@ -102,18 +83,10 @@ fn default_unpair_device() -> char {
|
||||
'u'
|
||||
}
|
||||
|
||||
fn default_toggle_device_connection() -> char {
|
||||
' '
|
||||
}
|
||||
|
||||
fn default_toggle_device_trust() -> char {
|
||||
't'
|
||||
}
|
||||
|
||||
fn default_pair_new_device() -> char {
|
||||
'p'
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Self {
|
||||
let conf_path = dirs::config_dir()
|
||||
|
||||
264
src/handler.rs
264
src/handler.rs
@@ -12,6 +12,121 @@ use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use tui_input::backend::crossterm::EventHandler;
|
||||
|
||||
async fn toggle_connect(app: &mut App, sender: UnboundedSender<Event>) {
|
||||
if let Some(selected_controller) = app.controller_state.selected() {
|
||||
let controller = &app.controllers[selected_controller];
|
||||
if let Some(index) = app.paired_devices_state.selected() {
|
||||
let addr = controller.paired_devices[index].addr;
|
||||
match controller.adapter.device(addr) {
|
||||
Ok(device) => {
|
||||
tokio::spawn(async move {
|
||||
match device.is_connected().await {
|
||||
Ok(is_connected) => {
|
||||
if is_connected {
|
||||
match device.disconnect().await {
|
||||
Ok(_) => {
|
||||
let _ = Notification::send(
|
||||
"Device disconnected".to_string(),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match device.connect().await {
|
||||
Ok(_) => {
|
||||
let _ = Notification::send(
|
||||
"Device connected".to_string(),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
let _ =
|
||||
Notification::send(e.to_string(), NotificationLevel::Error, sender.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn pair(app: &mut App, sender: UnboundedSender<Event>) {
|
||||
if let Some(selected_controller) = app.controller_state.selected() {
|
||||
let controller = &app.controllers[selected_controller];
|
||||
if let Some(index) = app.new_devices_state.selected() {
|
||||
let addr = controller.new_devices[index].addr;
|
||||
match controller.adapter.device(addr) {
|
||||
Ok(device) => match device.alias().await {
|
||||
Ok(device_name) => {
|
||||
let _ = Notification::send(
|
||||
format!("Start pairing with the device\n `{device_name}`",),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
|
||||
tokio::spawn(async move {
|
||||
match device.pair().await {
|
||||
Ok(_) => {
|
||||
let _ = Notification::send(
|
||||
"Device paired".to_string(),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let _ =
|
||||
Notification::send(e.to_string(), NotificationLevel::Error, sender.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_key_events(
|
||||
key_event: KeyEvent,
|
||||
app: &mut App,
|
||||
@@ -67,18 +182,6 @@ pub async fn handle_key_events(
|
||||
app.quit();
|
||||
}
|
||||
|
||||
// Show help
|
||||
KeyCode::Char('?') => {
|
||||
app.focused_block = FocusedBlock::Help;
|
||||
}
|
||||
|
||||
// Discard help popup
|
||||
KeyCode::Esc => {
|
||||
if app.focused_block == FocusedBlock::Help {
|
||||
app.focused_block = FocusedBlock::Adapter;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch focus
|
||||
KeyCode::Tab => match app.focused_block {
|
||||
FocusedBlock::Adapter => {
|
||||
@@ -221,9 +324,6 @@ pub async fn handle_key_events(
|
||||
}
|
||||
}
|
||||
|
||||
FocusedBlock::Help => {
|
||||
app.help.scroll_down();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
@@ -265,9 +365,6 @@ pub async fn handle_key_events(
|
||||
}
|
||||
}
|
||||
}
|
||||
FocusedBlock::Help => {
|
||||
app.help.scroll_up();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
@@ -356,78 +453,8 @@ pub async fn handle_key_events(
|
||||
}
|
||||
|
||||
// Connect / Disconnect
|
||||
KeyCode::Char(c) if c == config.paired_device.toggle_connect => {
|
||||
if let Some(selected_controller) =
|
||||
app.controller_state.selected()
|
||||
{
|
||||
let controller = &app.controllers[selected_controller];
|
||||
if let Some(index) = app.paired_devices_state.selected() {
|
||||
let addr = controller.paired_devices[index].addr;
|
||||
match controller.adapter.device(addr) {
|
||||
Ok(device) => {
|
||||
tokio::spawn(async move {
|
||||
match device.is_connected().await {
|
||||
Ok(is_connected) => {
|
||||
if is_connected {
|
||||
match device.disconnect().await
|
||||
{
|
||||
Ok(_) => {
|
||||
let _ = Notification::send(
|
||||
"Device disconnected"
|
||||
.to_string(),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match device.connect().await {
|
||||
Ok(_) => {
|
||||
let _ = Notification::send(
|
||||
"Device connected"
|
||||
.to_string(),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Enter => toggle_connect(app, sender).await,
|
||||
KeyCode::Char(' ') => toggle_connect(app, sender).await,
|
||||
|
||||
// Trust / Untrust
|
||||
KeyCode::Char(c) if c == config.paired_device.toggle_trust => {
|
||||
@@ -718,59 +745,10 @@ pub async fn handle_key_events(
|
||||
|
||||
FocusedBlock::NewDevices => {
|
||||
// Pair new device
|
||||
if KeyCode::Char(config.new_device.pair) == key_event.code
|
||||
&& let Some(selected_controller) = app.controller_state.selected()
|
||||
{
|
||||
let controller = &app.controllers[selected_controller];
|
||||
if let Some(index) = app.new_devices_state.selected() {
|
||||
let addr = controller.new_devices[index].addr;
|
||||
match controller.adapter.device(addr) {
|
||||
Ok(device) => match device.alias().await {
|
||||
Ok(device_name) => {
|
||||
let _ = Notification::send(
|
||||
format!(
|
||||
"Start pairing with the device\n `{device_name}`",
|
||||
),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
|
||||
tokio::spawn(async move {
|
||||
match device.pair().await {
|
||||
Ok(_) => {
|
||||
let _ = Notification::send(
|
||||
"Device paired".to_string(),
|
||||
NotificationLevel::Info,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let _ = Notification::send(
|
||||
e.to_string(),
|
||||
NotificationLevel::Error,
|
||||
sender.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
match key_event.code {
|
||||
KeyCode::Enter => pair(app, sender).await,
|
||||
KeyCode::Char(' ') => pair(app, sender).await,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
194
src/help.rs
194
src/help.rs
@@ -1,194 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ratatui::{
|
||||
Frame,
|
||||
layout::{Alignment, Constraint, Direction, Layout, Margin},
|
||||
style::{Color, Style, Stylize},
|
||||
widgets::{
|
||||
Block, BorderType, Borders, Cell, Clear, Padding, Row, Scrollbar, ScrollbarOrientation,
|
||||
ScrollbarState, Table, TableState,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{app::ColorMode, config::Config};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Help {
|
||||
block_height: usize,
|
||||
state: TableState,
|
||||
keys: Vec<(Cell<'static>, &'static str)>,
|
||||
}
|
||||
|
||||
impl Help {
|
||||
pub fn new(config: Arc<Config>) -> Self {
|
||||
let mut state = TableState::new().with_offset(0);
|
||||
state.select(Some(0));
|
||||
|
||||
Self {
|
||||
block_height: 0,
|
||||
state,
|
||||
keys: vec![
|
||||
(
|
||||
Cell::from("## Global").style(Style::new().bold().fg(Color::Yellow)),
|
||||
"",
|
||||
),
|
||||
(Cell::from("Esc").bold(), "Dismiss different pop-ups"),
|
||||
(
|
||||
Cell::from("Tab or h/l").bold(),
|
||||
"Switch between different sections",
|
||||
),
|
||||
(Cell::from("j or Down").bold(), "Scroll down"),
|
||||
(Cell::from("k or Up").bold(), "Scroll up"),
|
||||
(
|
||||
Cell::from(config.toggle_scanning.to_string()).bold(),
|
||||
"Start/Stop scanning",
|
||||
),
|
||||
(Cell::from("?").bold(), "Show help"),
|
||||
(Cell::from("ctrl+c or q").bold(), "Quit"),
|
||||
(Cell::from(""), ""),
|
||||
(
|
||||
Cell::from("## Adapters").style(Style::new().bold().fg(Color::Yellow)),
|
||||
"",
|
||||
),
|
||||
(
|
||||
Cell::from(config.adapter.toggle_pairing.to_string()).bold(),
|
||||
"Enable/Disable the pairing",
|
||||
),
|
||||
(
|
||||
Cell::from(config.adapter.toggle_power.to_string()).bold(),
|
||||
"Power on/off the adapter",
|
||||
),
|
||||
(
|
||||
Cell::from(config.adapter.toggle_discovery.to_string()).bold(),
|
||||
"Enable/Disable the discovery",
|
||||
),
|
||||
(Cell::from(""), ""),
|
||||
(
|
||||
Cell::from("## Paired devices").style(Style::new().bold().fg(Color::Yellow)),
|
||||
"",
|
||||
),
|
||||
(
|
||||
Cell::from(config.paired_device.unpair.to_string()).bold(),
|
||||
"Unpair the device",
|
||||
),
|
||||
(
|
||||
Cell::from({
|
||||
if config.paired_device.toggle_connect == ' ' {
|
||||
"Space".to_string()
|
||||
} else {
|
||||
config.paired_device.toggle_connect.to_string()
|
||||
}
|
||||
})
|
||||
.bold(),
|
||||
"Connect/Disconnect the device",
|
||||
),
|
||||
(
|
||||
Cell::from(config.paired_device.toggle_trust.to_string()).bold(),
|
||||
"Trust/Untrust the device",
|
||||
),
|
||||
(
|
||||
Cell::from(config.paired_device.rename.to_string()).bold(),
|
||||
"Rename the device",
|
||||
),
|
||||
(Cell::from(""), ""),
|
||||
(
|
||||
Cell::from("## New devices").style(Style::default().bold().fg(Color::Yellow)),
|
||||
"",
|
||||
),
|
||||
(
|
||||
Cell::from(config.new_device.pair.to_string()).bold(),
|
||||
"Pair the device",
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_down(&mut self) {
|
||||
let i = match self.state.selected() {
|
||||
Some(i) => {
|
||||
if i >= self.keys.len().saturating_sub(self.block_height - 6) {
|
||||
i
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
None => 1,
|
||||
};
|
||||
*self.state.offset_mut() = i;
|
||||
self.state.select(Some(i));
|
||||
}
|
||||
pub fn scroll_up(&mut self) {
|
||||
let i = match self.state.selected() {
|
||||
Some(i) => i.saturating_sub(1),
|
||||
None => 1,
|
||||
};
|
||||
*self.state.offset_mut() = i;
|
||||
self.state.select(Some(i));
|
||||
}
|
||||
|
||||
pub fn render(&mut self, frame: &mut Frame, color_mode: ColorMode) {
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(28),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.flex(ratatui::layout::Flex::SpaceBetween)
|
||||
.split(frame.area());
|
||||
|
||||
let block = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(70),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.flex(ratatui::layout::Flex::SpaceBetween)
|
||||
.split(layout[1])[1];
|
||||
|
||||
self.block_height = block.height as usize;
|
||||
|
||||
let widths = [Constraint::Length(20), Constraint::Max(40)];
|
||||
let rows: Vec<Row> = self
|
||||
.keys
|
||||
.iter()
|
||||
.map(|key| {
|
||||
Row::new(vec![key.0.to_owned(), key.1.into()]).style(match color_mode {
|
||||
ColorMode::Dark => Style::default().fg(Color::White),
|
||||
ColorMode::Light => Style::default().fg(Color::Black),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let rows_len = self.keys.len().saturating_sub(self.block_height - 6);
|
||||
|
||||
let table = Table::new(rows, widths).block(
|
||||
Block::default()
|
||||
.padding(Padding::uniform(2))
|
||||
.title(" Help ")
|
||||
.title_style(Style::default().bold().fg(Color::Green))
|
||||
.title_alignment(Alignment::Center)
|
||||
.borders(Borders::ALL)
|
||||
.style(Style::default())
|
||||
.border_type(BorderType::Thick)
|
||||
.border_style(Style::default().fg(Color::Green)),
|
||||
);
|
||||
|
||||
frame.render_widget(Clear, block);
|
||||
frame.render_stateful_widget(table, block, &mut self.state);
|
||||
|
||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
|
||||
.begin_symbol(Some("↑"))
|
||||
.end_symbol(Some("↓"));
|
||||
let mut scrollbar_state =
|
||||
ScrollbarState::new(rows_len).position(self.state.selected().unwrap_or_default());
|
||||
frame.render_stateful_widget(
|
||||
scrollbar,
|
||||
block.inner(Margin {
|
||||
vertical: 1,
|
||||
horizontal: 0,
|
||||
}),
|
||||
&mut scrollbar_state,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,6 @@ pub mod notification;
|
||||
|
||||
pub mod spinner;
|
||||
|
||||
pub mod help;
|
||||
|
||||
pub mod config;
|
||||
|
||||
pub mod rfkill;
|
||||
|
||||
10
src/ui.rs
10
src/ui.rs
@@ -1,18 +1,10 @@
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::{App, FocusedBlock};
|
||||
use crate::app::App;
|
||||
|
||||
pub fn render(app: &mut App, frame: &mut Frame) {
|
||||
// App
|
||||
app.render(frame);
|
||||
|
||||
match app.focused_block {
|
||||
FocusedBlock::Help => app.help.render(frame, app.color_mode),
|
||||
FocusedBlock::SetDeviceAliasBox => app.render_set_alias(frame),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Notifications
|
||||
for (index, notification) in app.notifications.iter().enumerate() {
|
||||
notification.render(index, frame);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user