use crate::core::buffer::Buffer;
use crate::core::color::Color;
use crate::core::rect::Rect;
use crate::widgets::Widget;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScrollBarOrientation {
Vertical,
Horizontal,
}
#[derive(Debug, Clone)]
pub struct ScrollBar {
pub position: usize,
pub total: usize,
pub viewport: usize,
pub orientation: ScrollBarOrientation,
pub track_color: Color,
pub thumb_color: Color,
pub arrow_color: Color,
}
impl ScrollBar {
pub fn new(orientation: ScrollBarOrientation) -> Self {
Self {
position: 0,
total: 100,
viewport: 10,
orientation,
track_color: Color::rgb(48, 54, 61),
thumb_color: Color::rgb(88, 166, 255),
arrow_color: Color::rgb(139, 148, 158),
}
}
pub fn with_position(mut self, pos: usize) -> Self {
self.position = pos;
self
}
pub fn with_total(mut self, total: usize) -> Self {
self.total = total;
self
}
pub fn with_viewport(mut self, vp: usize) -> Self {
self.viewport = vp;
self
}
pub fn thumb_position(&self) -> usize {
if self.total <= self.viewport {
return 0;
}
let track_len = if self.orientation == ScrollBarOrientation::Vertical {
10
} else {
20
};
let thumb_len = ((self.viewport as f64 / self.total as f64) * track_len as f64) as usize;
let max_pos = self.total.saturating_sub(self.viewport);
if max_pos == 0 {
0
} else {
let pos =
(self.position as f64 / max_pos as f64 * (track_len - thumb_len) as f64) as usize;
pos.min(track_len.saturating_sub(thumb_len))
}
}
}
impl Widget for ScrollBar {
fn render(&self, buffer: &mut Buffer, area: Rect) {
match self.orientation {
ScrollBarOrientation::Vertical => self.render_vertical(buffer, area),
ScrollBarOrientation::Horizontal => self.render_horizontal(buffer, area),
}
}
}
impl ScrollBar {
fn render_vertical(&self, buffer: &mut Buffer, area: Rect) {
let h = area.height as usize;
let thumb_len = ((self.viewport as f64 / self.total as f64) * h as f64).max(1.0) as usize;
let thumb_pos = self.thumb_position();
for i in 0..h {
let x = area.x as usize;
let y = area.y as usize + i;
let is_thumb = i >= thumb_pos && i < thumb_pos + thumb_len;
let ch = if is_thumb { '█' } else { '░' };
let fg = if is_thumb {
self.thumb_color
} else {
self.track_color
};
buffer.set(
x,
y,
crate::core::buffer::Cell {
ch,
fg,
bg: None,
bold: false,
italic: false,
underlined: false,
},
);
}
}
fn render_horizontal(&self, buffer: &mut Buffer, area: Rect) {
let w = area.width as usize;
let thumb_len = ((self.viewport as f64 / self.total as f64) * w as f64).max(1.0) as usize;
let thumb_pos = self.thumb_position();
for i in 0..w {
let x = area.x as usize + i;
let y = area.y as usize;
let is_thumb = i >= thumb_pos && i < thumb_pos + thumb_len;
let ch = if is_thumb { '█' } else { '░' };
let fg = if is_thumb {
self.thumb_color
} else {
self.track_color
};
buffer.set(
x,
y,
crate::core::buffer::Cell {
ch,
fg,
bg: None,
bold: false,
italic: false,
underlined: false,
},
);
}
}
}