horizontal scroll

pull/16/head
Takayuki Maeda 3 years ago
parent da06d0b557
commit a86183916a

@ -1,5 +1,5 @@
use super::{
compute_character_width, utils::scroll_vertical::VerticalScroll, Component, DrawableComponent,
compute_character_width, utils::vertical_scroll::VerticalScroll, Component, DrawableComponent,
EventState,
};
use crate::components::RecordTableComponent;

@ -1,6 +1,6 @@
use super::{
utils::scroll_vertical::VerticalScroll, Component, DrawableComponent, EventState,
TableValueComponent,
utils::{horizontal_scroll::HorizontalScroll, vertical_scroll::VerticalScroll},
Component, DrawableComponent, EventState, TableValueComponent,
};
use crate::event::Key;
use anyhow::Result;
@ -22,6 +22,8 @@ pub struct TableComponent {
pub column_page: usize,
pub column_page_start: std::cell::Cell<usize>,
pub scroll: VerticalScroll,
pub vertical_scroll: VerticalScroll,
pub horizontal_scroll: HorizontalScroll,
pub select_entire_row: bool,
pub eod: bool,
}
@ -36,6 +38,8 @@ impl Default for TableComponent {
column_index: 0,
column_page_start: std::cell::Cell::new(0),
scroll: VerticalScroll::new(),
vertical_scroll: VerticalScroll::new(),
horizontal_scroll: HorizontalScroll::new(),
select_entire_row: false,
eod: false,
}
@ -287,10 +291,10 @@ impl DrawableComponent for TableComponent {
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(),
layout[1].height.saturating_sub(2) as usize,
@ -311,6 +315,18 @@ impl DrawableComponent for TableComponent {
Style::default()
})
});
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 header = Row::new(header_cells).height(1).bottom_margin(1);
let rows = rows.iter().enumerate().map(|(row_index, item)| {
let height = item
@ -347,7 +363,8 @@ impl DrawableComponent for TableComponent {
.widths(&constraints);
f.render_stateful_widget(table, layout[1], &mut self.state);
self.scroll.draw(f, layout[1]);
self.vertical_scroll.draw(f, layout[1]);
self.horizontal_scroll.draw(f, layout[1]);
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;

@ -65,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);
}
}

@ -67,13 +67,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| ());

@ -4,8 +4,11 @@ use tui::{
backend::Backend,
buffer::Buffer,
layout::{Margin, Rect},
style::{Color, Style},
symbols::{block::FULL, line::DOUBLE_VERTICAL},
style::Style,
symbols::{
block::FULL,
line::{DOUBLE_HORIZONTAL, DOUBLE_VERTICAL},
},
widgets::Widget,
Frame,
};
@ -15,22 +18,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;
}
@ -43,17 +52,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);
@ -63,12 +94,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);
widget.style_pos = Style::default().fg(Color::Blue);
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