-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #49 from NJUPT-SAST/feature/magic&cli
Feature/magic&cli
- Loading branch information
Showing
12 changed files
with
286 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
fn main() { | ||
// println!("Hello, world!"); | ||
// let mut terminal = ratatui::init(); | ||
// terminal.clear()?; | ||
// // let app_result = run(terminal); | ||
// ratatui::restore(); | ||
// app_result | ||
use std::io::Result; | ||
|
||
use cli::utils::tui::{tui::Tui, tui_render::TuiRender}; | ||
|
||
fn main() -> Result<()> { | ||
let mut terminal = ratatui::init(); | ||
let app_result = Tui::new().run(&mut terminal); | ||
ratatui::restore(); | ||
app_result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
use ratatui::{ | ||
style::{ | ||
palette::{material::GREEN, tailwind::SLATE}, | ||
Color, | ||
}, | ||
text::Line, | ||
widgets::{ListItem, ListState}, | ||
}; | ||
|
||
const TEXT_FG_COLOR: Color = SLATE.c200; | ||
const COMPLETED_TEXT_FG_COLOR: Color = GREEN.c500; | ||
|
||
#[derive(Debug, Default)] | ||
pub struct ComponentList { | ||
pub components: Vec<ComponentItem>, | ||
pub state: ListState, | ||
} | ||
|
||
impl ComponentList { | ||
pub fn new() -> Self { | ||
ComponentList::from_iter([ | ||
(Status::UnSelect, "Button", "A button with magic animation"), | ||
( | ||
Status::Select, | ||
"Card", | ||
"A card with a shooting star background", | ||
), | ||
]) | ||
} | ||
} | ||
|
||
impl FromIterator<(Status, &'static str, &'static str)> for ComponentList { | ||
fn from_iter<I: IntoIterator<Item = (Status, &'static str, &'static str)>>(iter: I) -> Self { | ||
let components = iter | ||
.into_iter() | ||
.map(|(status, todo, info)| ComponentItem::new(status, todo, info)) | ||
.collect(); | ||
let state: ListState = ListState::default(); | ||
Self { components, state } | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct ComponentItem { | ||
pub title: String, | ||
pub info: String, | ||
pub status: Status, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum Status { | ||
Select, | ||
UnSelect, | ||
} | ||
|
||
impl ComponentItem { | ||
fn new(status: Status, title: &str, info: &str) -> Self { | ||
Self { | ||
title: title.to_string(), | ||
info: info.to_string(), | ||
status, | ||
} | ||
} | ||
} | ||
|
||
impl From<&ComponentItem> for ListItem<'_> { | ||
fn from(value: &ComponentItem) -> Self { | ||
let line = match value.status { | ||
Status::UnSelect => Line::styled(format!(" ☐ {}", value.title), TEXT_FG_COLOR), | ||
Status::Select => Line::styled(format!(" ✓ {}", value.title), COMPLETED_TEXT_FG_COLOR), | ||
}; | ||
|
||
ListItem::new(line) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
pub mod tui_base; | ||
pub mod wrapper; | ||
pub mod component_list; | ||
pub mod split_area; | ||
pub mod tui; | ||
pub mod tui_render; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
use std::rc::Rc; | ||
|
||
use ratatui::layout::{Constraint, Direction, Layout, Rect}; | ||
|
||
pub fn split_area(area: Rect) -> (Rc<[Rect]>, Rc<[Rect]>, Rc<[Rect]>) { | ||
let comment = Layout::default() | ||
.direction(Direction::Vertical) | ||
.constraints(vec![Constraint::Fill(1), Constraint::Length(1)]) | ||
.split(area); | ||
|
||
let outer = Layout::default() | ||
.direction(Direction::Horizontal) | ||
.constraints(vec![Constraint::Percentage(30), Constraint::Percentage(70)]) | ||
.split(comment[0]); | ||
|
||
let inner = Layout::default() | ||
.direction(Direction::Vertical) | ||
.constraints(vec![Constraint::Percentage(35), Constraint::Percentage(65)]) | ||
.split(outer[1]); | ||
|
||
(comment, outer, inner) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
use crossterm::event::{KeyCode, KeyEvent}; | ||
use ratatui::{ | ||
buffer::Buffer, | ||
layout::Rect, | ||
style::{palette::tailwind::SLATE, Color, Modifier, Style}, | ||
symbols::border, | ||
widgets::{Block, List, ListItem, Paragraph, StatefulWidget, Widget, Wrap}, | ||
}; | ||
|
||
use super::{ | ||
component_list::{ComponentList, Status}, | ||
split_area::split_area, | ||
tui_render::TuiRender, | ||
}; | ||
|
||
const SELECTED_STYLE: Style = Style::new().bg(SLATE.c800).add_modifier(Modifier::BOLD); | ||
|
||
#[derive(Debug, Default)] | ||
pub struct Tui { | ||
exit: bool, | ||
component_list: ComponentList, | ||
} | ||
|
||
impl Tui { | ||
pub fn new() -> Self { | ||
Self { | ||
exit: false, | ||
component_list: ComponentList::new(), | ||
} | ||
} | ||
fn exit(&mut self) { | ||
self.exit = true; | ||
} | ||
|
||
fn select_next(&mut self) { | ||
self.component_list.state.select_next(); | ||
} | ||
|
||
fn select_previous(&mut self) { | ||
self.component_list.state.select_previous(); | ||
} | ||
|
||
fn select_first(&mut self) { | ||
self.component_list.state.select_first(); | ||
} | ||
|
||
fn select_last(&mut self) { | ||
self.component_list.state.select_last(); | ||
} | ||
|
||
fn toggle_status(&mut self) { | ||
if let Some(i) = self.component_list.state.selected() { | ||
self.component_list.components[i].status = | ||
match self.component_list.components[i].status { | ||
Status::Select => Status::UnSelect, | ||
Status::UnSelect => Status::Select, | ||
}; | ||
} | ||
} | ||
|
||
fn render_list(&mut self, area: Rect, buf: &mut Buffer) { | ||
let block = Block::bordered() | ||
.title(" Select Components ") | ||
.border_set(border::THICK); | ||
|
||
let items: Vec<ListItem> = self | ||
.component_list | ||
.components | ||
.iter() | ||
.enumerate() | ||
.map(|(_, component_item)| ListItem::from(component_item)) | ||
.collect(); | ||
|
||
let list = List::new(items) | ||
.block(block) | ||
.highlight_style(SELECTED_STYLE) | ||
.highlight_symbol(">") | ||
.highlight_spacing(ratatui::widgets::HighlightSpacing::Always); | ||
|
||
StatefulWidget::render(list, area, buf, &mut self.component_list.state); | ||
} | ||
|
||
fn render_selected_item(&self, area: Rect, buf: &mut Buffer) { | ||
let info = if let Some(select_index) = self.component_list.state.selected() { | ||
self.component_list.components[select_index].info.clone() | ||
} else { | ||
"Nothing selected...".to_string() | ||
}; | ||
|
||
let block = Block::bordered().title(" Info ").border_set(border::THICK); | ||
|
||
Paragraph::new(info) | ||
.block(block) | ||
.wrap(Wrap { trim: false }) | ||
.render(area, buf); | ||
} | ||
} | ||
|
||
impl Widget for &mut Tui { | ||
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) { | ||
let (comment, outer, inner) = split_area(area); | ||
|
||
Paragraph::new("Use 'j/k' to move, 'g/G' to go top/bottom, Space to toggle.") | ||
.style(Style::default().fg(Color::Red)) | ||
.centered() | ||
.render(comment[1], buf); | ||
|
||
let block = Block::bordered().title(" State ").border_set(border::THICK); | ||
Paragraph::new("") | ||
.centered() | ||
.block(block) | ||
.render(inner[1], buf); | ||
|
||
self.render_list(outer[0], buf); | ||
|
||
self.render_selected_item(inner[0], buf); | ||
} | ||
} | ||
|
||
impl TuiRender for Tui { | ||
fn draw(&mut self, frame: &mut ratatui::Frame) { | ||
frame.render_widget(self, frame.area()); | ||
} | ||
|
||
fn handle_key_event(&mut self, key_event: KeyEvent) { | ||
match key_event.code { | ||
KeyCode::Char('q') | KeyCode::Esc => self.exit(), | ||
KeyCode::Char('j') | KeyCode::Down => self.select_next(), | ||
KeyCode::Char('k') | KeyCode::Up => self.select_previous(), | ||
KeyCode::Char('g') | KeyCode::Home => self.select_first(), | ||
KeyCode::Char('G') | KeyCode::End => self.select_last(), | ||
KeyCode::Char(' ') | KeyCode::Right => { | ||
self.toggle_status(); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
fn get_exit(&self) -> bool { | ||
self.exit | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use crossterm::event::{self, Event, KeyEvent, KeyEventKind}; | ||
use ratatui::{DefaultTerminal, Frame}; | ||
pub trait TuiRender { | ||
fn handle_events(&mut self) -> Result<(), std::io::Error> { | ||
match event::read()? { | ||
// it's important to check that the event is a key press event as | ||
// crossterm also emits key release and repeat events on Windows. | ||
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => { | ||
self.handle_key_event(key_event) | ||
} | ||
_ => {} | ||
}; | ||
Ok(()) | ||
} | ||
|
||
fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<(), std::io::Error> { | ||
while !self.get_exit() { | ||
terminal.draw(|frame| self.draw(frame))?; | ||
self.handle_events()?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn draw(&mut self, frame: &mut Frame); | ||
|
||
fn handle_key_event(&mut self, key_event: KeyEvent); | ||
|
||
fn get_exit(&self) -> bool; | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
d01712b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
sast-ui – ./
sast-ui-git-main-sast.vercel.app
sast-ui-sast.vercel.app