diff --git a/sample.toml b/sample.toml index e33e232..f76968f 100644 --- a/sample.toml +++ b/sample.toml @@ -1,6 +1,12 @@ [[conn]] type = "mysql" -name = "sample" +user = "root" +host = "localhost" +database = "world" +port = 3306 + +[[conn]] +type = "mysql" user = "root" host = "localhost" port = 3306 diff --git a/src/app.rs b/src/app.rs index fd37b3a..b522e50 100644 --- a/src/app.rs +++ b/src/app.rs @@ -33,7 +33,6 @@ pub struct App { help: HelpComponent, databases: DatabasesComponent, connections: ConnectionsComponent, - table_status: TableStatusComponent, pool: Option>, pub config: Config, pub error: ErrorComponent, @@ -52,7 +51,6 @@ impl App { tab: TabComponent::new(config.key_config.clone()), help: HelpComponent::new(config.key_config.clone()), databases: DatabasesComponent::new(config.key_config.clone()), - table_status: TableStatusComponent::default(), error: ErrorComponent::new(config.key_config), focus: Focus::ConnectionList, pool: None, @@ -77,15 +75,10 @@ impl App { .direction(Direction::Horizontal) .constraints([Constraint::Percentage(15), Constraint::Percentage(85)]) .split(f.size()); - let left_chunks = Layout::default() - .constraints([Constraint::Min(8), Constraint::Length(7)].as_ref()) - .split(main_chunks[0]); self.databases - .draw(f, left_chunks[0], matches!(self.focus, Focus::DabataseList)) + .draw(f, main_chunks[0], matches!(self.focus, Focus::DabataseList)) .unwrap(); - self.table_status - .draw(f, left_chunks[1], matches!(self.focus, Focus::DabataseList))?; let right_chunks = Layout::default() .direction(Direction::Vertical) @@ -268,8 +261,6 @@ impl App { table.clone(), ); } - self.table_status - .update(self.record_table.len() as u64, table); } Ok(()) } diff --git a/src/components/databases.rs b/src/components/databases.rs index a11cf3e..e439f08 100644 --- a/src/components/databases.rs +++ b/src/components/databases.rs @@ -49,7 +49,7 @@ impl DatabasesComponent { Self { tree: DatabaseTree::default(), filterd_tree: None, - scroll: VerticalScroll::new(), + scroll: VerticalScroll::new(true, true), input: Vec::new(), input_idx: 0, input_cursor_position: 0, diff --git a/src/components/table.rs b/src/components/table.rs index 1237732..5e38f59 100644 --- a/src/components/table.rs +++ b/src/components/table.rs @@ -1,6 +1,6 @@ use super::{ utils::scroll_vertical::VerticalScroll, Component, DrawableComponent, EventState, - TableValueComponent, + TableStatusComponent, TableValueComponent, }; use crate::components::command::{self, CommandInfo}; use crate::config::KeyConfig; @@ -40,7 +40,7 @@ impl TableComponent { selected_column: 0, selection_area_corner: None, column_page_start: std::cell::Cell::new(0), - scroll: VerticalScroll::new(), + scroll: VerticalScroll::new(false, false), eod: false, key_config, } @@ -68,7 +68,7 @@ impl TableComponent { self.selected_column = 0; self.selection_area_corner = None; self.column_page_start = std::cell::Cell::new(0); - self.scroll = VerticalScroll::new(); + self.scroll = VerticalScroll::new(false, false); self.eod = false; self.table = Some((database, table)); } @@ -80,7 +80,7 @@ impl TableComponent { self.selected_column = 0; self.selection_area_corner = None; self.column_page_start = std::cell::Cell::new(0); - self.scroll = VerticalScroll::new(); + self.scroll = VerticalScroll::new(false, false); self.eod = false; self.table = None; } @@ -406,6 +406,25 @@ impl DrawableComponent for TableComponent { .constraints(vec![Constraint::Length(3), Constraint::Length(5)]) .split(area); + f.render_widget( + Block::default() + .title(self.title()) + .borders(Borders::ALL) + .style(if focused { + Style::default() + } else { + Style::default().fg(Color::DarkGray) + }), + layout[1], + ); + + let chunks = Layout::default() + .vertical_margin(1) + .horizontal_margin(1) + .direction(Direction::Vertical) + .constraints([Constraint::Min(1), Constraint::Length(2)].as_ref()) + .split(layout[1]); + self.selected_row.selected().map_or_else( || { self.scroll.reset(); @@ -422,9 +441,9 @@ impl DrawableComponent for TableComponent { TableValueComponent::new(self.selected_cells().unwrap_or_default()) .draw(f, layout[0], focused)?; - let block = Block::default().borders(Borders::ALL).title(self.title()); + let block = Block::default().borders(Borders::NONE); let (selected_column_index, headers, rows, constraints) = - self.calculate_cell_widths(block.inner(layout[1]).width); + self.calculate_cell_widths(block.inner(chunks[0]).width); let header_cells = headers.iter().enumerate().map(|(column_index, h)| { Cell::from(h.to_string()).style(if selected_column_index == column_index { Style::default().add_modifier(Modifier::BOLD) @@ -466,7 +485,7 @@ impl DrawableComponent for TableComponent { let mut state = self.selected_row.clone(); f.render_stateful_widget( table, - layout[1], + chunks[0], if let Some((_, y)) = self.selection_area_corner { state.select(Some(y)); &mut state @@ -475,7 +494,22 @@ impl DrawableComponent for TableComponent { }, ); - self.scroll.draw(f, layout[1]); + TableStatusComponent::new( + if self.rows.is_empty() { + None + } else { + Some(self.rows.len()) + }, + if self.headers.is_empty() { + None + } else { + Some(self.headers.len()) + }, + self.table.as_ref().map_or(None, |t| Some(t.1.clone())), + ) + .draw(f, chunks[1], focused)?; + + self.scroll.draw(f, chunks[0]); Ok(()) } } diff --git a/src/components/table_status.rs b/src/components/table_status.rs index 89998d5..7c0bee3 100644 --- a/src/components/table_status.rs +++ b/src/components/table_status.rs @@ -8,79 +8,65 @@ use tui::{ layout::Rect, style::{Color, Style}, text::{Span, Spans}, - widgets::{Block, Borders, List, ListItem}, + widgets::{Block, Borders, Paragraph, Wrap}, Frame, }; pub struct TableStatusComponent { - rows_count: u64, + column_count: Option, + row_count: Option, table: Option, } impl Default for TableStatusComponent { fn default() -> Self { Self { - rows_count: 0, + row_count: None, + column_count: None, table: None, } } } impl TableStatusComponent { - pub fn update(&mut self, count: u64, table: Table) { - self.rows_count = count; - self.table = Some(table); - } - - fn status_str(&self) -> Vec { - if let Some(table) = self.table.as_ref() { - return vec![ - format!( - "created: {}", - table - .create_time - .map(|time| time.to_string()) - .unwrap_or_default() - ), - format!( - "updated: {}", - table - .update_time - .map(|time| time.to_string()) - .unwrap_or_default() - ), - format!( - "engine: {}", - table - .engine - .as_ref() - .map(|engine| engine.to_string()) - .unwrap_or_default() - ), - format!("rows: {}", self.rows_count), - ]; + pub fn new( + row_count: Option, + column_count: Option, + table: Option
, + ) -> Self { + Self { + row_count, + column_count, + table, } - Vec::new() } } impl DrawableComponent for TableStatusComponent { fn draw(&mut self, f: &mut Frame, area: Rect, focused: bool) -> Result<()> { - let table_status: Vec = self - .status_str() - .iter() - .map(|i| { - ListItem::new(vec![Spans::from(Span::raw(i.to_string()))]).style(Style::default()) - }) - .collect(); - let tasks = List::new(table_status).block(Block::default().borders(Borders::ALL).style( - if focused { - Style::default() - } else { - Style::default().fg(Color::DarkGray) - }, - )); - f.render_widget(tasks, area); + let status = Paragraph::new(Spans::from(vec![ + Span::from("rows: "), + Span::from(format!( + "{}, ", + self.row_count.map_or("-".to_string(), |c| c.to_string()) + )), + Span::from("columns: "), + Span::from(format!( + "{}, ", + self.column_count.map_or("-".to_string(), |c| c.to_string()) + )), + Span::from("engine: "), + Span::from(self.table.as_ref().map_or("-".to_string(), |c| { + c.engine.as_ref().map_or("-".to_string(), |e| e.to_string()) + })), + ])) + .block(Block::default().borders(Borders::TOP).style(if focused { + Style::default() + } else { + Style::default().fg(Color::DarkGray) + })) + .wrap(Wrap { trim: true }); + f.render_widget(status, area); Ok(()) } } diff --git a/src/components/utils/scroll_vertical.rs b/src/components/utils/scroll_vertical.rs index d54cf5a..3d8f310 100644 --- a/src/components/utils/scroll_vertical.rs +++ b/src/components/utils/scroll_vertical.rs @@ -5,13 +5,17 @@ use tui::{backend::Backend, layout::Rect, Frame}; pub struct VerticalScroll { top: Cell, max_top: Cell, + inside: bool, + border: bool, } impl VerticalScroll { - pub const fn new() -> Self { + pub const fn new(border: bool, inside: bool) -> Self { Self { top: Cell::new(0), max_top: Cell::new(0), + border, + inside, } } @@ -38,7 +42,14 @@ impl VerticalScroll { } pub fn draw(&self, f: &mut Frame, r: Rect) { - draw_scrollbar(f, r, self.max_top.get(), self.top.get()); + draw_scrollbar( + f, + r, + self.max_top.get(), + self.top.get(), + self.border, + self.inside, + ); } } diff --git a/src/ui/scrollbar.rs b/src/ui/scrollbar.rs index 0b2344a..a58972e 100644 --- a/src/ui/scrollbar.rs +++ b/src/ui/scrollbar.rs @@ -15,15 +15,19 @@ struct Scrollbar { pos: u16, style_bar: Style, style_pos: Style, + inside: bool, + border: bool, } impl Scrollbar { - fn new(max: usize, pos: usize) -> Self { + fn new(max: usize, pos: usize, border: bool, inside: bool) -> Self { Self { max: u16::try_from(max).unwrap_or_default(), pos: u16::try_from(pos).unwrap_or_default(), style_pos: Style::default(), style_bar: Style::default(), + inside, + border, } } } @@ -38,7 +42,11 @@ impl Widget for Scrollbar { return; } - let right = area.right().saturating_sub(1); + let right = if self.inside { + area.right().saturating_sub(1) + } else { + area.right() + }; if right <= area.left() { return; }; @@ -46,7 +54,7 @@ impl Widget for Scrollbar { let (bar_top, bar_height) = { let scrollbar_area = area.inner(&Margin { horizontal: 0, - vertical: 1, + vertical: if self.border { 1 } else { 0 }, }); (scrollbar_area.top(), scrollbar_area.height) @@ -67,8 +75,15 @@ impl Widget for Scrollbar { } } -pub fn draw_scrollbar(f: &mut Frame, r: Rect, max: usize, pos: usize) { - let mut widget = Scrollbar::new(max, pos); +pub fn draw_scrollbar( + f: &mut Frame, + r: Rect, + max: usize, + pos: usize, + border: bool, + inside: bool, +) { + let mut widget = Scrollbar::new(max, pos, border, inside); widget.style_pos = Style::default().fg(Color::Blue); f.render_widget(widget, r); }