Progressbar (#25)

* Progressbar
* header
* label
* border
This commit is contained in:
Jens K. 2024-12-25 12:21:36 +01:00 committed by GitHub
parent acb627b1d7
commit ae1a48e5e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 96 additions and 8 deletions

View File

@ -8,6 +8,7 @@ use crate::{
clock::{self, Clock, ClockArgs, Style}, clock::{self, Clock, ClockArgs, Style},
countdown::{Countdown, CountdownWidget}, countdown::{Countdown, CountdownWidget},
footer::Footer, footer::Footer,
header::Header,
pomodoro::{Mode as PomodoroMode, Pomodoro, PomodoroArgs, PomodoroWidget}, pomodoro::{Mode as PomodoroMode, Pomodoro, PomodoroArgs, PomodoroWidget},
timer::{Timer, TimerWidget}, timer::{Timer, TimerWidget},
}, },
@ -173,14 +174,22 @@ impl App {
} }
} }
fn clock_is_running(&mut self) -> bool { fn clock_is_running(&self) -> bool {
match self.content { match self.content {
Content::Countdown => self.countdown.get_clock().clone().is_running(), Content::Countdown => self.countdown.get_clock().is_running(),
Content::Timer => self.timer.get_clock().clone().is_running(), Content::Timer => self.timer.get_clock().is_running(),
Content::Pomodoro => self.pomodoro.get_clock().is_running(), Content::Pomodoro => self.pomodoro.get_clock().is_running(),
} }
} }
fn get_percentage_done(&self) -> Option<u16> {
match self.content {
Content::Countdown => Some(self.countdown.get_clock().get_percentage_done()),
Content::Timer => None,
Content::Pomodoro => Some(self.pomodoro.get_clock().get_percentage_done()),
}
}
fn handle_key_event(&mut self, key: KeyEvent) { fn handle_key_event(&mut self, key: KeyEvent) {
debug!("Received key {:?}", key.code); debug!("Received key {:?}", key.code);
match key.code { match key.code {
@ -249,14 +258,20 @@ impl AppWidget {
impl StatefulWidget for AppWidget { impl StatefulWidget for AppWidget {
type State = App; type State = App;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let [v0, v1] = Layout::vertical([ let [v0, v1, v2] = Layout::vertical([
Constraint::Length(1),
Constraint::Percentage(100), Constraint::Percentage(100),
Constraint::Length(if state.show_menu { 4 } else { 1 }), Constraint::Length(if state.show_menu { 4 } else { 1 }),
]) ])
.areas(area); .areas(area);
// header
Header {
percentage: state.get_percentage_done(),
}
.render(v0, buf);
// content // content
self.render_content(v0, buf, state); self.render_content(v1, buf, state);
// footer // footer
Footer { Footer {
show_menu: state.show_menu, show_menu: state.show_menu,
@ -264,6 +279,6 @@ impl StatefulWidget for AppWidget {
selected_content: state.content, selected_content: state.content,
edit_mode: state.is_edit_mode(), edit_mode: state.is_edit_mode(),
} }
.render(v1, buf); .render(v2, buf);
} }
} }

View File

@ -1,5 +1,7 @@
pub mod clock; pub mod clock;
pub mod countdown; pub mod countdown;
pub mod footer; pub mod footer;
pub mod header;
pub mod pomodoro; pub mod pomodoro;
pub mod progressbar;
pub mod timer; pub mod timer;

View File

@ -393,12 +393,22 @@ impl Clock<Countdown> {
} }
} }
pub fn set_done(&mut self) { fn set_done(&mut self) {
if self.current_value.is_zero() { if self.current_value.is_zero() {
self.mode = Mode::Done; self.mode = Mode::Done;
} }
} }
pub fn get_percentage_done(&self) -> u16 {
let initial = self.initial_value.as_millis();
let elapsed = self
.initial_value
.saturating_sub(self.current_value)
.as_millis();
(elapsed * 100 / initial) as u16
}
pub fn edit_next(&mut self) { pub fn edit_next(&mut self) {
self.edit_mode_next(); self.edit_mode_next();
} }

View File

@ -36,7 +36,7 @@ impl Widget for Footer {
.title( .title(
format! {"[m]enu {:} ", if self.show_menu {scrollbar::VERTICAL.end} else {scrollbar::VERTICAL.begin}}, format! {"[m]enu {:} ", if self.show_menu {scrollbar::VERTICAL.end} else {scrollbar::VERTICAL.begin}},
) )
.border_set(border::DOUBLE) .border_set(border::PLAIN)
.render(border_area, buf); .render(border_area, buf);
// show menu // show menu
if self.show_menu { if self.show_menu {

22
src/widgets/header.rs Normal file
View File

@ -0,0 +1,22 @@
use ratatui::{
buffer::Buffer,
layout::Rect,
widgets::{Block, Borders, Widget},
};
use crate::widgets::progressbar::Progressbar;
#[derive(Debug, Clone)]
pub struct Header {
pub percentage: Option<u16>,
}
impl Widget for Header {
fn render(self, area: Rect, buf: &mut Buffer) {
if let Some(percentage) = self.percentage {
Progressbar::new(percentage).render(area, buf);
} else {
Block::new().borders(Borders::TOP).render(area, buf);
}
}
}

View File

@ -0,0 +1,39 @@
use ratatui::{
buffer::Buffer,
layout::{Constraint, Layout, Rect},
symbols::line,
text::Span,
widgets::Widget,
};
#[derive(Debug, Clone)]
pub struct Progressbar {
pub percentage: u16,
}
impl Progressbar {
pub fn new(percentage: u16) -> Self {
Self { percentage }
}
}
impl Widget for Progressbar {
fn render(self, area: Rect, buf: &mut Buffer) {
let label = Span::raw(format!(" {}% ", self.percentage));
let [h0, area] = Layout::horizontal([
Constraint::Length(label.width() as u16),
Constraint::Percentage(100),
])
.areas(area);
let [h1, h2] =
Layout::horizontal([Constraint::Percentage(self.percentage), Constraint::Fill(0)])
.areas(area);
// label
label.render(h0, buf);
// done
Span::from(line::THICK_HORIZONTAL.repeat(h1.width as usize)).render(h1, buf);
// rest
Span::from(line::HORIZONTAL.repeat(h2.width as usize)).render(h2, buf);
}
}