This commit is contained in:
RainBus
2024-10-16 20:32:02 +08:00
parent ef6f088fde
commit c8eb942b13
13 changed files with 269 additions and 63 deletions

11
Cargo.lock generated
View File

@@ -309,7 +309,7 @@ dependencies = [
"time",
"unicode-segmentation",
"unicode-truncate",
"unicode-width",
"unicode-width 0.1.14",
]
[[package]]
@@ -327,6 +327,7 @@ version = "0.1.0"
dependencies = [
"crossterm",
"ratatui",
"unicode-width 0.2.0",
]
[[package]]
@@ -496,7 +497,7 @@ checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
dependencies = [
"itertools",
"unicode-segmentation",
"unicode-width",
"unicode-width 0.1.14",
]
[[package]]
@@ -505,6 +506,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"

View File

@@ -6,3 +6,4 @@ edition = "2021"
[dependencies]
crossterm = { version = "0.28.1", features = ["event-stream"] }
ratatui = { version = "0.28.1", features = ["all-widgets"] }
unicode-width = "0.2.0"

View File

@@ -3,7 +3,7 @@ use std::io;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{DefaultTerminal, Frame};
use crate::view::home::{self, Home};
use crate::view::{home::Home, View};
pub struct App {
running: bool,
@@ -39,7 +39,7 @@ impl App {
code: KeyCode::Esc,
..
}) => self.running = false,
event => self.view.handle_event(event)?,
event => self.view.handle_event(&event)?,
}
Ok(())
}

View File

@@ -1,7 +1,29 @@
use component::Component;
use std::io;
use crossterm::event::Event;
use ratatui::{
layout::{Flex, Layout, Rect},
Frame,
};
pub mod component;
pub mod home;
pub mod setting;
pub trait View {
fn draw(&self, frame: &mut Frame);
fn handle_event(&mut self, event: &Event) -> io::Result<()>;
}
pub trait PopupView {
fn draw(&self, frame: &mut Frame);
fn handle_event(&mut self, event: &Event);
}
fn center_rect(area: Rect, width: u16, height: u16) -> Rect {
let horizontal = Layout::horizontal([width]).flex(Flex::Center);
let vertical = Layout::vertical([height]).flex(Flex::Center);
let [area] = vertical.areas(area);
let [area] = horizontal.areas(area);
area
}

View File

@@ -4,10 +4,11 @@ use crossterm::event::Event;
use ratatui::widgets::Widget;
pub mod block;
pub mod help;
pub mod input;
pub mod table;
pub mod list;
pub trait Component {
fn widget(&self) -> impl Widget;
fn event_handler(&mut self, event: Event) -> io::Result<()>;
fn event_handler(&mut self, event: &Event) -> io::Result<()>;
}

View File

@@ -3,12 +3,20 @@ use std::marker::PhantomData;
use ratatui::widgets::{Block, Borders};
#[derive(Default)]
pub struct BlockWidget<'a> {
pub struct BlockComponent<'a> {
_phantom: PhantomData<&'a ()>,
title: String,
}
impl<'a> BlockWidget<'a> {
impl<'a> BlockComponent<'a> {
pub fn widget(&self) -> Block<'a> {
Block::default().borders(Borders::ALL)
Block::default()
.borders(Borders::ALL)
.title(self.title.clone())
}
pub fn title(mut self, text: String) -> Self {
self.title = text;
self
}
}

View File

@@ -0,0 +1,20 @@
use std::io;
use crossterm::event::Event;
use ratatui::widgets::{Paragraph, Widget};
use super::{block::BlockComponent, Component};
#[derive(Default)]
pub struct HelpComponent {}
impl Component for HelpComponent {
fn widget(&self) -> impl Widget {
let block = BlockComponent::default().title("Help".to_string()).widget();
let help = Paragraph::new("Temp").block(block);
help
}
fn event_handler(&mut self, _event: &Event) -> io::Result<()> {
Ok(())
}
}

View File

@@ -1,33 +1,39 @@
use std::{io, marker::PhantomData, u32};
use std::io;
use super::{block::BlockWidget, Component};
use super::{block::BlockComponent, Component};
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::{
prelude::Rect,
widgets::{Block, Paragraph, Widget},
layout::{Position, Rect},
widgets::{Paragraph, Widget},
Frame,
};
#[derive(Default)]
pub struct InputWidget {
pub struct InputComponent {
name: String,
value: String,
cursor_idx: usize,
cursor: usize,
}
impl InputWidget {
pub fn new() -> Self {
InputWidget::default()
impl InputComponent {
pub fn name(mut self, name: String) -> Self {
self.name = name;
self
}
pub fn show_cursor(&self, frame: &mut Frame, area: Rect) {
let position = Position::new(area.x + 1 + self.cursor as u16, area.y + 1);
frame.set_cursor_position(position);
}
}
impl Component for InputWidget {
impl Component for InputComponent {
fn widget(&self) -> impl Widget {
let block = BlockWidget::default().widget();
let block = BlockComponent::default().title(self.name.clone()).widget();
let input = Paragraph::new(self.value.clone()).block(block);
input
}
fn event_handler(&mut self, event: Event) -> io::Result<()> {
fn event_handler(&mut self, event: &Event) -> io::Result<()> {
match event {
Event::Key(KeyEvent {
kind: KeyEventKind::Press,
@@ -37,12 +43,18 @@ impl Component for InputWidget {
}) => {
use KeyCode::*;
match (code, modifiers) {
(Char(c), KeyModifiers::SHIFT | KeyModifiers::NONE) => {
self.value.push(c);
(Char(c), &KeyModifiers::SHIFT | &KeyModifiers::NONE) => {
self.value.push(*c);
self.cursor += 1;
}
(Backspace, &KeyModifiers::NONE) => {
if self.value.pop().is_some() {
self.cursor -= 1;
};
}
_ => {}
}
},
}
_ => {}
}
Ok(())

View File

@@ -0,0 +1,97 @@
use super::{block::BlockComponent, Component};
use crossterm::{
event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
style::Color,
};
use ratatui::{
style::Stylize,
widgets::{List, ListItem, Widget},
};
// #[derive(Default)]
pub struct ListComponent {
name: String,
item_keys: Vec<String>,
cursor: usize,
selectable: bool,
}
impl ListComponent {
pub fn add_item(&mut self, _item_keys: Vec<String>) {
self.item_keys.extend(vec!["S".to_string()]);
}
pub fn name(mut self, name: String) -> Self {
self.name = name;
self
}
pub fn selectable(mut self) -> Self {
self.selectable = true;
self
}
}
impl Default for ListComponent {
fn default() -> Self {
ListComponent {
name: String::default(),
item_keys: vec!["1".to_string(), "2".to_string(), "3".to_string()],
cursor: 0,
selectable: false,
}
}
}
impl Component for ListComponent {
fn widget(&self) -> impl Widget {
let block = BlockComponent::default().title(self.name.clone()).widget();
let list_items: Vec<ListItem> = self
.item_keys
.iter()
.enumerate()
.map(|(idx, key)| {
if self.selectable && idx == self.cursor {
ListItem::new(key.clone()).bg(Color::Grey).fg(Color::Black)
} else {
ListItem::new(key.clone())
}
})
.collect();
let list = List::default().items(list_items).block(block);
list
}
fn event_handler(&mut self, event: &Event) -> std::io::Result<()> {
if !self.selectable {
return Ok(());
}
match event {
Event::Key(KeyEvent {
code,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
..
}) => {
use KeyCode::*;
match code {
Up => {
self.cursor = if self.cursor == 0 {
self.item_keys.len() - 1
} else {
self.cursor - 1
}
}
Down => {
self.cursor = if self.cursor == self.item_keys.len() - 1 {
0
} else {
self.cursor + 1
}
}
_ => {}
}
}
_ => {}
}
Ok(())
}
}

View File

@@ -1,29 +0,0 @@
use std::marker::PhantomData;
use super::{block::BlockWidget, Component};
use crossterm::event::{self, Event};
use ratatui::{
prelude::Rect,
widgets::{Block, Paragraph, Widget},
Frame,
};
#[derive(Default)]
pub struct TableWidget {}
impl TableWidget {
pub fn new() -> Self {
TableWidget::default()
}
}
impl Component for TableWidget {
fn widget(&self) -> impl Widget {
let block = BlockWidget::default().widget();
let input = Paragraph::new("sss").block(block);
input
}
fn event_handler(&mut self, event: Event) -> std::io::Result<()> {
Ok(())
}
}

View File

@@ -1,22 +1,48 @@
use std::io;
use crossterm::event::{ Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
widgets::Paragraph,
style::{Color, Style},
widgets::{Block, BorderType, Borders},
Frame,
};
use super::component::{input::InputWidget, table::TableWidget, Component};
use super::{
center_rect,
component::{
help::HelpComponent,
input::{self, InputComponent},
list::ListComponent,
Component,
},
setting::SettingView,
PopupView, View,
};
#[derive(Default)]
// #[derive(Default)]
pub struct Home {
input: InputWidget,
table: TableWidget,
input: InputComponent,
table: ListComponent,
help: HelpComponent,
setting: SettingView,
}
impl Home {
pub fn draw(&self, frame: &mut Frame) {
impl Default for Home {
fn default() -> Self {
Home {
input: InputComponent::default().name("Name".to_string()),
table: ListComponent::default()
.selectable()
.name("Connections".to_string()),
help: HelpComponent::default(),
setting: SettingView::default(),
}
}
}
impl View for Home {
fn draw(&self, frame: &mut Frame) {
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![
@@ -25,12 +51,16 @@ impl Home {
Constraint::Length(3),
])
.split(frame.area());
self.input.show_cursor(frame, layout[0]);
frame.render_widget(self.input.widget(), layout[0]);
frame.render_widget(self.table.widget(), layout[1]);
frame.render_widget(self.help.widget(), layout[2]);
self.setting.draw(frame);
}
pub fn handle_event(&mut self, event: Event) -> io::Result<()> {
fn handle_event(&mut self, event: &Event) -> io::Result<()> {
self.input.event_handler(event)?;
self.table.event_handler(event)?;
Ok(())
}
}

35
src/view/setting.rs Normal file
View File

@@ -0,0 +1,35 @@
use ratatui::layout::{Constraint, Layout, Rect};
use super::{
center_rect,
component::{list::ListComponent, Component},
PopupView,
};
pub struct SettingView {
menu: ListComponent,
area: Rect,
}
impl SettingView {}
impl Default for SettingView {
fn default() -> Self {
SettingView {
area: Rect::new(0, 0, 10, 5),
menu: ListComponent::default().selectable(),
}
}
}
impl PopupView for SettingView {
fn draw(&self, frame: &mut ratatui::Frame) {
let area = center_rect(frame.area(), self.area.width, self.area.height);
let layout = Layout::default()
.constraints([Constraint::default()])
.split(area);
frame.render_widget(self.menu.widget(), layout[0]);
}
fn handle_event(&mut self, _event: &crossterm::event::Event) {}
}

2
src/view/util.rs Normal file
View File

@@ -0,0 +1,2 @@