horizontal scroll

pull/16/head^2
Takayuki Maeda 3 years ago
parent 2f1edc8384
commit 922b1d96e2

@ -1,4 +1,4 @@
use super::{utils::scroll_vertical::VerticalScroll, Component, DrawableComponent};
use super::{utils::vertical_scroll::VerticalScroll, Component, DrawableComponent};
use crate::event::Key;
use crate::ui::common_nav;
use crate::ui::scrolllist::draw_list_block;

@ -1,5 +1,5 @@
use super::{utils::scroll_vertical::VerticalScroll, Component, DrawableComponent};
use crate::event::Key;
use super::{utils::vertical_scroll::VerticalScroll, Component, DrawableComponent};
use crate::{components::utils::horizontal_scroll::HorizontalScroll, event::Key};
use anyhow::Result;
use std::convert::From;
use tui::{
@ -16,7 +16,8 @@ pub struct TableComponent {
pub rows: Vec<Vec<String>>,
pub column_index: usize,
pub column_page: usize,
pub scroll: VerticalScroll,
pub vertical_scroll: VerticalScroll,
pub horizontal_scroll: HorizontalScroll,
pub select_entire_row: bool,
}
@ -28,7 +29,8 @@ impl Default for TableComponent {
rows: vec![],
column_page: 0,
column_index: 0,
scroll: VerticalScroll::new(),
vertical_scroll: VerticalScroll::new(),
horizontal_scroll: HorizontalScroll::new(),
select_entire_row: false,
}
}
@ -95,7 +97,7 @@ impl TableComponent {
if self.rows.is_empty() {
return;
}
if self.column_index == self.headers.len() - 1 {
if self.column_index == self.headers.len() {
return;
}
if self.column_index == 9 {
@ -163,10 +165,10 @@ impl DrawableComponent for TableComponent {
fn draw<B: Backend>(&mut self, f: &mut Frame<B>, area: Rect, focused: bool) -> Result<()> {
self.state.selected().map_or_else(
|| {
self.scroll.reset();
self.vertical_scroll.reset();
},
|selection| {
self.scroll.update(
self.vertical_scroll.update(
selection,
self.rows.len(),
area.height.saturating_sub(2) as usize,
@ -174,6 +176,19 @@ impl DrawableComponent for TableComponent {
},
);
let column_width = area.width.saturating_pow(10);
self.horizontal_scroll.update(
if self.headers.is_empty() {
0
} else {
column_width
.saturating_mul(self.headers[..self.column_index].len() as u16)
.saturating_add(1) as usize
},
column_width.saturating_mul(self.headers.len() as u16) as usize,
area.width.saturating_sub(2) as usize,
);
let headers = self.headers();
let header_cells = headers
.iter()
@ -224,7 +239,8 @@ impl DrawableComponent for TableComponent {
.widths(&widths);
f.render_stateful_widget(t, area, &mut self.state);
self.scroll.draw(f, area);
self.vertical_scroll.draw(f, area);
self.horizontal_scroll.draw(f, area);
Ok(())
}
}

@ -0,0 +1,92 @@
use crate::{components::ScrollType, ui::scrollbar::draw_scrollbar};
use std::{cell::Cell, thread::panicking};
use tui::{backend::Backend, layout::Rect, Frame};
pub struct HorizontalScroll {
right: Cell<usize>,
max_right: Cell<usize>,
}
impl HorizontalScroll {
pub const fn new() -> Self {
Self {
right: Cell::new(0),
max_right: Cell::new(0),
}
}
pub fn get_right(&self) -> usize {
self.right.get()
}
pub fn reset(&self) {
self.right.set(0);
}
pub fn _move_right(&self, move_type: ScrollType) -> bool {
let old = self.right.get();
let max = self.max_right.get();
let new_scroll_right = match move_type {
ScrollType::Down => old.saturating_add(1),
ScrollType::Up => old.saturating_sub(1),
ScrollType::Home => 0,
ScrollType::End => max,
_ => old,
};
let new_scroll_right = new_scroll_right.clamp(0, max);
if new_scroll_right == old {
return false;
}
self.right.set(new_scroll_right);
true
}
pub fn update(&self, selection: usize, selection_max: usize, visual_width: usize) -> usize {
let new_right = calc_scroll_right(self.get_right(), visual_width, selection, selection_max);
self.right.set(new_right);
if visual_width == 0 {
self.max_right.set(0);
} else {
let new_max = selection_max.saturating_sub(visual_width);
self.max_right.set(new_max);
}
new_right
}
pub fn _update_no_selection(&self, line_count: usize, visual_width: usize) -> usize {
self.update(self.get_right(), line_count, visual_width)
}
pub fn draw<B: Backend>(&self, f: &mut Frame<B>, r: Rect) {
draw_scrollbar(f, r, self.max_right.get(), self.right.get(), false);
}
}
fn calc_scroll_right(
current_right: usize,
width_in_lines: usize,
selection: usize,
selection_max: usize,
) -> usize {
if width_in_lines == 0 {
return 0;
}
if selection_max <= width_in_lines {
return 0;
}
if current_right + width_in_lines <= selection {
selection.saturating_sub(width_in_lines) + 1
} else if current_right > selection {
selection
} else {
current_right
}
}

@ -1 +1,2 @@
pub mod scroll_vertical;
pub mod horizontal_scroll;
pub mod vertical_scroll;

@ -1,9 +1,7 @@
use crate::{components::ScrollType, ui::scrollbar::draw_scrollbar};
use std::cell::Cell;
use tui::{backend::Backend, layout::Rect, Frame};
use crate::{components::ScrollType, ui::scrollbar::draw_scrollbar};
pub struct VerticalScroll {
top: Cell<usize>,
max_top: Cell<usize>,
@ -67,7 +65,7 @@ impl VerticalScroll {
}
pub fn draw<B: Backend>(&self, f: &mut Frame<B>, r: Rect) {
draw_scrollbar(f, r, self.max_top.get(), self.top.get());
draw_scrollbar(f, r, self.max_top.get(), self.top.get(), true);
}
}

@ -33,7 +33,6 @@ async fn main() -> anyhow::Result<()> {
let stdout = stdout();
setup_terminal()?;
set_panic_handlers()?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
@ -70,13 +69,13 @@ fn setup_terminal() -> Result<()> {
Ok(())
}
fn set_panic_handlers() -> Result<()> {
panic::set_hook(Box::new(|e| {
eprintln!("panic: {:?}", e);
shutdown_terminal();
}));
Ok(())
}
// fn set_panic_handlers() -> Result<()> {
// panic::set_hook(Box::new(|e| {
// println!("panic: {:?}", e);
// shutdown_terminal();
// }));
// Ok(())
// }
fn shutdown_terminal() {
let leave_screen = io::stdout().execute(LeaveAlternateScreen).map(|_f| ());

@ -5,7 +5,10 @@ use tui::{
buffer::Buffer,
layout::{Margin, Rect},
style::Style,
symbols::{block::FULL, line::DOUBLE_VERTICAL},
symbols::{
block::FULL,
line::{DOUBLE_HORIZONTAL, DOUBLE_VERTICAL},
},
widgets::Widget,
Frame,
};
@ -16,22 +19,28 @@ struct Scrollbar {
pos: u16,
style_bar: Style,
style_pos: Style,
vertical: bool,
}
impl Scrollbar {
fn new(max: usize, pos: usize) -> Self {
fn new(max: usize, pos: usize, vertical: 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(),
vertical,
}
}
}
impl Widget for Scrollbar {
fn render(self, area: Rect, buf: &mut Buffer) {
if area.height <= 2 {
if self.vertical && area.height <= 2 {
return;
}
if !self.vertical && area.width <= 2 {
return;
}
@ -44,17 +53,39 @@ impl Widget for Scrollbar {
return;
};
let bottom = area.bottom().saturating_sub(1);
if bottom <= area.top() {
return;
};
let (bar_top, bar_height) = {
let scrollbar_area = area.inner(&Margin {
horizontal: 0,
vertical: 1,
});
let scrollbar_area = if self.vertical {
area.inner(&Margin {
horizontal: 0,
vertical: 1,
})
} else {
area.inner(&Margin {
horizontal: 1,
vertical: 0,
})
};
(scrollbar_area.top(), scrollbar_area.height)
if self.vertical {
(scrollbar_area.top(), scrollbar_area.height)
} else {
(scrollbar_area.left(), scrollbar_area.width)
}
};
for y in bar_top..(bar_top + bar_height) {
buf.set_string(right, y, DOUBLE_VERTICAL, self.style_bar);
if self.vertical {
for y in bar_top..(bar_top + bar_height) {
buf.set_string(right, y, DOUBLE_VERTICAL, self.style_bar)
}
} else {
for x in bar_top..(bar_top + bar_height) {
buf.set_string(x, bottom, DOUBLE_HORIZONTAL, self.style_bar)
}
}
let progress = f32::from(self.pos) / f32::from(self.max);
@ -64,12 +95,22 @@ impl Widget for Scrollbar {
let pos: u16 = pos.cast_nearest();
let pos = pos.saturating_sub(1);
buf.set_string(right, bar_top + pos, FULL, self.style_pos);
if self.vertical {
buf.set_string(right, bar_top + pos, FULL, self.style_pos);
} else {
buf.set_string(bar_top + pos, bottom, "▆", self.style_pos);
}
}
}
pub fn draw_scrollbar<B: Backend>(f: &mut Frame<B>, r: Rect, max: usize, pos: usize) {
let mut widget = Scrollbar::new(max, pos);
pub fn draw_scrollbar<B: Backend>(
f: &mut Frame<B>,
r: Rect,
max: usize,
pos: usize,
vertical: bool,
) {
let mut widget = Scrollbar::new(max, pos, vertical);
widget.style_pos = Style::default();
f.render_widget(widget, r);
}

Loading…
Cancel
Save