Implemented roster scrolling.
This commit is contained in:
parent
a79cd5d566
commit
b10886508c
2 changed files with 94 additions and 50 deletions
133
src/alarm.rs
133
src/alarm.rs
|
@ -37,10 +37,6 @@ impl Countdown {
|
||||||
self.value = value;
|
self.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_position(&mut self, position: Position) {
|
|
||||||
self.position = Some(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_position(&self) -> bool {
|
pub fn has_position(&self) -> bool {
|
||||||
self.position.is_some()
|
self.position.is_some()
|
||||||
}
|
}
|
||||||
|
@ -74,6 +70,40 @@ impl Countdown {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn place(
|
||||||
|
&mut self,
|
||||||
|
layout: &Layout,
|
||||||
|
alarm: &Alarm,
|
||||||
|
offset: usize,
|
||||||
|
index: usize,
|
||||||
|
) {
|
||||||
|
// Compute position.
|
||||||
|
let mut col =
|
||||||
|
layout.roster.col
|
||||||
|
+ 3
|
||||||
|
+ UnicodeWidthStr::width(alarm.label.as_str()) as u16;
|
||||||
|
let mut line = layout.roster.line + index as u16;
|
||||||
|
|
||||||
|
// Compensate for "hidden" items in the alarm roster.
|
||||||
|
if offset > 0 {
|
||||||
|
if index <= offset {
|
||||||
|
// Draw next to upper placeholder.
|
||||||
|
line = layout.roster.line;
|
||||||
|
col = layout.roster.col + 6;
|
||||||
|
} else {
|
||||||
|
// Should be no problem as index > offset and
|
||||||
|
// line is x + index.
|
||||||
|
line -= offset as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if line > layout.roster_height.saturating_add(2) {
|
||||||
|
// Draw next to lower placeholder.
|
||||||
|
line = layout.roster.line + layout.roster_height;
|
||||||
|
col = layout.roster.col + 6;
|
||||||
|
}
|
||||||
|
self.position = Some(Position { col, line });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Alarm {
|
pub struct Alarm {
|
||||||
|
@ -91,12 +121,14 @@ impl Alarm {
|
||||||
|
|
||||||
pub struct AlarmRoster {
|
pub struct AlarmRoster {
|
||||||
list: Vec<Alarm>,
|
list: Vec<Alarm>,
|
||||||
|
offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlarmRoster {
|
impl AlarmRoster {
|
||||||
pub fn new() -> AlarmRoster {
|
pub fn new() -> AlarmRoster {
|
||||||
AlarmRoster {
|
AlarmRoster {
|
||||||
list: Vec::new(),
|
list: Vec::new(),
|
||||||
|
offset: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,8 +202,13 @@ impl AlarmRoster {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove last alarm.
|
// Remove last alarm.
|
||||||
pub fn drop_last(&mut self) -> bool {
|
pub fn pop(&mut self) -> Option<Alarm> {
|
||||||
self.list.pop().is_some()
|
self.list.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset ceiling according to layout information.
|
||||||
|
fn adjust_offset(&mut self, layout: &Layout) {
|
||||||
|
self.offset = self.offset.min(self.list.len().saturating_sub(layout.roster_height as usize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for active alarms.
|
// Check for active alarms.
|
||||||
|
@ -179,6 +216,16 @@ impl AlarmRoster {
|
||||||
!self.list.iter().any(|a| !a.exceeded)
|
!self.list.iter().any(|a| !a.exceeded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scroll_up(&mut self, layout: &Layout) {
|
||||||
|
let excess = self.list.len().saturating_sub(layout.roster_height as usize);
|
||||||
|
self.offset = excess.min(self.offset.saturating_sub(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scroll_down(&mut self, layout: &Layout) {
|
||||||
|
let excess = self.list.len().saturating_sub(layout.roster_height as usize);
|
||||||
|
self.offset = excess.min(self.offset.saturating_add(1));
|
||||||
|
}
|
||||||
|
|
||||||
// Find and process exceeded alarms.
|
// Find and process exceeded alarms.
|
||||||
pub fn check(&mut self,
|
pub fn check(&mut self,
|
||||||
clock: &mut Clock,
|
clock: &mut Clock,
|
||||||
|
@ -188,7 +235,6 @@ impl AlarmRoster {
|
||||||
) -> Option<&Alarm>
|
) -> Option<&Alarm>
|
||||||
{
|
{
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
let size = self.list.len() as u16;
|
|
||||||
|
|
||||||
for (index, alarm) in self.list.iter_mut()
|
for (index, alarm) in self.list.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -207,26 +253,7 @@ impl AlarmRoster {
|
||||||
// Reached the alarm to exceed next. Update countdown accordingly.
|
// Reached the alarm to exceed next. Update countdown accordingly.
|
||||||
countdown.set(alarm.time - clock.elapsed);
|
countdown.set(alarm.time - clock.elapsed);
|
||||||
if !countdown.has_position() || force_redraw {
|
if !countdown.has_position() || force_redraw {
|
||||||
// Compute position.
|
countdown.place(&layout, &alarm, self.offset, index);
|
||||||
let mut col =
|
|
||||||
layout.roster.col
|
|
||||||
+ 3
|
|
||||||
+ UnicodeWidthStr::width(alarm.label.as_str()) as u16;
|
|
||||||
let mut line = layout.roster.line + index as u16;
|
|
||||||
|
|
||||||
// Compensate for "hidden" items in the alarm roster.
|
|
||||||
// TODO: Make this more elegant and robust.
|
|
||||||
if let Some(offset) = size.checked_sub(layout.roster_height + 1) {
|
|
||||||
if index as u16 <= offset{
|
|
||||||
// Draw next to placeholder ("[...]").
|
|
||||||
line = layout.roster.line;
|
|
||||||
col = layout.roster.col + 6;
|
|
||||||
} else {
|
|
||||||
line = line.checked_sub(offset)
|
|
||||||
.unwrap_or(layout.roster.line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
countdown.set_position(Position { col, line });
|
|
||||||
}
|
}
|
||||||
// Ignore other alarms.
|
// Ignore other alarms.
|
||||||
break;
|
break;
|
||||||
|
@ -236,35 +263,43 @@ impl AlarmRoster {
|
||||||
|
|
||||||
// Draw alarm roster according to layout.
|
// Draw alarm roster according to layout.
|
||||||
pub fn draw<W: Write>(
|
pub fn draw<W: Write>(
|
||||||
&self,
|
&mut self,
|
||||||
stdout: &mut RawTerminal<W>,
|
stdout: &mut RawTerminal<W>,
|
||||||
layout: &mut Layout,
|
layout: &mut Layout,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> Result<(), std::io::Error>
|
) -> Result<(), std::io::Error>
|
||||||
{
|
{
|
||||||
// Find first item to print in case we lack the space to print them
|
// Match offset to layout.
|
||||||
// all. Final '-1' to take account for the input buffer.
|
self.adjust_offset(&layout);
|
||||||
let mut offset = 0;
|
|
||||||
|
|
||||||
if self.list.len() > layout.roster_height as usize {
|
for (i, alarm) in self.list.iter()
|
||||||
// Actually -1 (zero indexing) +1 (first line containing "[...]").
|
.skip(self.offset)
|
||||||
offset = self.list.len() - layout.roster_height as usize;
|
.enumerate()
|
||||||
|
{
|
||||||
|
// Add 1 to compensate for the line "[...]".
|
||||||
|
let line = layout.roster.line + i as u16;
|
||||||
|
|
||||||
write!(stdout,
|
if self.offset > 0 && i == 0 {
|
||||||
"{}{}[...]{}",
|
// Indicate hidden items at top.
|
||||||
cursor::Goto(layout.roster.col, layout.roster.line),
|
write!(stdout,
|
||||||
style::Faint,
|
"{}{}{}{}",
|
||||||
style::Reset,
|
cursor::Goto(layout.roster.col, line),
|
||||||
)?;
|
style::Faint,
|
||||||
}
|
if config.fancy { "╶╴▲╶╴" } else { "[ ^ ]" },
|
||||||
|
style::Reset,
|
||||||
for (i, alarm) in self.list.iter().skip(offset).enumerate() {
|
)?;
|
||||||
let line = if offset > 0 {
|
continue;
|
||||||
// Add offset of one for "[...]".
|
} else if i == layout.roster_height as usize {
|
||||||
layout.roster.line + i as u16 + 1
|
// Indicate hidden items at bottom.
|
||||||
} else {
|
write!(stdout,
|
||||||
layout.roster.line + i as u16
|
"{}{}{}{}",
|
||||||
};
|
cursor::Goto(layout.roster.col, line),
|
||||||
|
style::Faint,
|
||||||
|
if config.fancy { "╶╴▼╶╴" } else { "[ v ]" },
|
||||||
|
style::Reset,
|
||||||
|
)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
match alarm.exceeded {
|
match alarm.exceeded {
|
||||||
true if config.fancy => {
|
true if config.fancy => {
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -274,6 +274,15 @@ pub fn run(
|
||||||
layout.schedule_recalc();
|
layout.schedule_recalc();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
},
|
||||||
|
// Scroll alarm roster.
|
||||||
|
Key::PageUp => {
|
||||||
|
alarm_roster.scroll_up(&layout);
|
||||||
|
force_redraw = true;
|
||||||
|
},
|
||||||
|
Key::PageDown => {
|
||||||
|
alarm_roster.scroll_down(&layout);
|
||||||
|
force_redraw = true;
|
||||||
|
},
|
||||||
// Forward every char if in insert mode.
|
// Forward every char if in insert mode.
|
||||||
Key::Char(c) if buffer.visible => {
|
Key::Char(c) if buffer.visible => {
|
||||||
buffer.push(c);
|
buffer.push(c);
|
||||||
|
@ -297,7 +306,7 @@ pub fn run(
|
||||||
},
|
},
|
||||||
// Delete last alarm on 'd'.
|
// Delete last alarm on 'd'.
|
||||||
Key::Char('d') => {
|
Key::Char('d') => {
|
||||||
if alarm_roster.drop_last() {
|
if alarm_roster.pop().is_some() {
|
||||||
// If we remove the last alarm we have to reset "countdown"
|
// If we remove the last alarm we have to reset "countdown"
|
||||||
// manually. It is safe to do it anyway.
|
// manually. It is safe to do it anyway.
|
||||||
layout.set_roster_width(alarm_roster.width());
|
layout.set_roster_width(alarm_roster.width());
|
||||||
|
|
Loading…
Reference in a new issue