Add scrolling context to StatusBar
- Whenever a scrolling context is entered/exited, send a ScrollUpdate event. - StatusBar maintains a stack of scrolling contexts and displays the last one, if it exists. Each context is associated with a ComponentId. - To handle dangling contexts after their Components aren't visible anymore, send a VisibilityChange event in situations where that scenario is possible.lazy_fetch
parent
5eb4342af8
commit
6d63429ad3
|
@ -66,6 +66,22 @@ pub enum PageMovement {
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct ScrollContext {
|
||||||
|
shown_lines: usize,
|
||||||
|
total_lines: usize,
|
||||||
|
has_more_lines: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ScrollUpdate {
|
||||||
|
End(ComponentId),
|
||||||
|
Update {
|
||||||
|
id: ComponentId,
|
||||||
|
context: ScrollContext,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// Types implementing this Trait can draw on the terminal and receive events.
|
/// Types implementing this Trait can draw on the terminal and receive events.
|
||||||
/// If a type wants to skip drawing if it has not changed anything, it can hold some flag in its
|
/// If a type wants to skip drawing if it has not changed anything, it can hold some flag in its
|
||||||
/// fields (eg self.dirty = false) and act upon that in their `draw` implementation.
|
/// fields (eg self.dirty = false) and act upon that in their `draw` implementation.
|
||||||
|
|
|
@ -414,6 +414,27 @@ impl ContactList {
|
||||||
|
|
||||||
let top_idx = page_no * rows;
|
let top_idx = page_no * rows;
|
||||||
|
|
||||||
|
if self.length >= rows {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::Update {
|
||||||
|
id: self.id,
|
||||||
|
context: ScrollContext {
|
||||||
|
shown_lines: top_idx + rows,
|
||||||
|
total_lines: self.length,
|
||||||
|
has_more_lines: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
/* If cursor position has changed, remove the highlight from the previous position and
|
/* If cursor position has changed, remove the highlight from the previous position and
|
||||||
* apply it in the new one. */
|
* apply it in the new one. */
|
||||||
if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
|
if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
|
||||||
|
@ -621,6 +642,11 @@ impl Component for ContactList {
|
||||||
|
|
||||||
self.mode = ViewMode::View(manager.id());
|
self.mode = ViewMode::View(manager.id());
|
||||||
self.view = Some(manager);
|
self.view = Some(manager);
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -639,6 +665,11 @@ impl Component for ContactList {
|
||||||
|
|
||||||
self.mode = ViewMode::View(manager.id());
|
self.mode = ViewMode::View(manager.id());
|
||||||
self.view = Some(manager);
|
self.view = Some(manager);
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -925,6 +925,9 @@ impl Component for Composer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_event(&mut self, mut event: &mut UIEvent, context: &mut Context) -> bool {
|
fn process_event(&mut self, mut event: &mut UIEvent, context: &mut Context) -> bool {
|
||||||
|
if let UIEvent::VisibilityChange(_) = event {
|
||||||
|
self.pager.process_event(event, context);
|
||||||
|
}
|
||||||
let shortcuts = self.get_shortcuts(context);
|
let shortcuts = self.get_shortcuts(context);
|
||||||
match (&mut self.mode, &mut event) {
|
match (&mut self.mode, &mut event) {
|
||||||
(ViewMode::Edit, _) => {
|
(ViewMode::Edit, _) => {
|
||||||
|
|
|
@ -703,6 +703,8 @@ impl Component for Listing {
|
||||||
fallback = *cur;
|
fallback = *cur;
|
||||||
}
|
}
|
||||||
if self.component.coordinates() == (*account_hash, *mailbox_hash) {
|
if self.component.coordinates() == (*account_hash, *mailbox_hash) {
|
||||||
|
self.component
|
||||||
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.component.set_coordinates((
|
self.component.set_coordinates((
|
||||||
self.accounts[self.cursor_pos.0].hash,
|
self.accounts[self.cursor_pos.0].hash,
|
||||||
self.accounts[self.cursor_pos.0].entries[fallback].3,
|
self.accounts[self.cursor_pos.0].entries[fallback].3,
|
||||||
|
@ -730,6 +732,8 @@ impl Component for Listing {
|
||||||
let account_hash = self.accounts[self.cursor_pos.0].hash;
|
let account_hash = self.accounts[self.cursor_pos.0].hash;
|
||||||
self.cursor_pos.1 = MenuEntryCursor::Mailbox(*idx);
|
self.cursor_pos.1 = MenuEntryCursor::Mailbox(*idx);
|
||||||
self.status = None;
|
self.status = None;
|
||||||
|
self.component
|
||||||
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.component
|
self.component
|
||||||
.set_coordinates((account_hash, *mailbox_hash));
|
.set_coordinates((account_hash, *mailbox_hash));
|
||||||
self.menu_content.empty();
|
self.menu_content.empty();
|
||||||
|
@ -1148,6 +1152,11 @@ impl Component for Listing {
|
||||||
match *event {
|
match *event {
|
||||||
UIEvent::Input(Key::Right) => {
|
UIEvent::Input(Key::Right) => {
|
||||||
self.focus = ListingFocus::Mailbox;
|
self.focus = ListingFocus::Mailbox;
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
self.ratio = 90;
|
self.ratio = 90;
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1161,6 +1170,11 @@ impl Component for Listing {
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
self.focus = ListingFocus::Mailbox;
|
self.focus = ListingFocus::Mailbox;
|
||||||
self.ratio = 90;
|
self.ratio = 90;
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(ref k)
|
UIEvent::Input(ref k)
|
||||||
|
@ -1171,6 +1185,11 @@ impl Component for Listing {
|
||||||
self.focus = ListingFocus::Mailbox;
|
self.focus = ListingFocus::Mailbox;
|
||||||
self.ratio = 90;
|
self.ratio = 90;
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
context
|
context
|
||||||
.replies
|
.replies
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||||
|
@ -1650,6 +1669,20 @@ impl Listing {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if self.show_menu_scrollbar == ShowMenuScrollbar::True && total_height > rows {
|
if self.show_menu_scrollbar == ShowMenuScrollbar::True && total_height > rows {
|
||||||
|
if self.focus == ListingFocus::Menu {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::Update {
|
||||||
|
id: self.id,
|
||||||
|
context: ScrollContext {
|
||||||
|
shown_lines: skip_offset + rows,
|
||||||
|
total_lines: total_height,
|
||||||
|
has_more_lines: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
}
|
||||||
ScrollBar::default().set_show_arrows(true).draw(
|
ScrollBar::default().set_show_arrows(true).draw(
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
|
@ -1664,6 +1697,12 @@ impl Listing {
|
||||||
/* length */
|
/* length */
|
||||||
total_height,
|
total_height,
|
||||||
);
|
);
|
||||||
|
} else if total_height < rows {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
|
@ -1964,6 +2003,8 @@ impl Listing {
|
||||||
if let Some((_, _, _, mailbox_hash)) =
|
if let Some((_, _, _, mailbox_hash)) =
|
||||||
self.accounts[self.cursor_pos.0].entries.get(idx)
|
self.accounts[self.cursor_pos.0].entries.get(idx)
|
||||||
{
|
{
|
||||||
|
self.component
|
||||||
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.component
|
self.component
|
||||||
.set_coordinates((account_hash, *mailbox_hash));
|
.set_coordinates((account_hash, *mailbox_hash));
|
||||||
/* Check if per-mailbox configuration overrides general configuration */
|
/* Check if per-mailbox configuration overrides general configuration */
|
||||||
|
|
|
@ -1627,6 +1627,8 @@ impl Component for CompactListing {
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
self.unfocused = false;
|
self.unfocused = false;
|
||||||
|
self.view
|
||||||
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
||||||
* will be performed but the list will not be drawn. So force a draw in any case.
|
* will be performed but the list will not be drawn. So force a draw in any case.
|
||||||
|
|
|
@ -1491,6 +1491,8 @@ impl Component for ConversationsListing {
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
self.unfocused = false;
|
self.unfocused = false;
|
||||||
|
self.view
|
||||||
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
||||||
* will be performed but the list will not be drawn. So force a draw in any case.
|
* will be performed but the list will not be drawn. So force a draw in any case.
|
||||||
|
|
|
@ -1146,6 +1146,8 @@ impl Component for PlainListing {
|
||||||
&& shortcut!(k == shortcuts[PlainListing::DESCRIPTION]["exit_thread"]) =>
|
&& shortcut!(k == shortcuts[PlainListing::DESCRIPTION]["exit_thread"]) =>
|
||||||
{
|
{
|
||||||
self.unfocused = false;
|
self.unfocused = false;
|
||||||
|
self.view
|
||||||
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
||||||
* will be performed but the list will not be drawn. So force a draw in any case.
|
* will be performed but the list will not be drawn. So force a draw in any case.
|
||||||
|
|
|
@ -1224,6 +1224,9 @@ impl Component for ThreadListing {
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Char('i')) if self.unfocused => {
|
UIEvent::Input(Key::Char('i')) if self.unfocused => {
|
||||||
self.unfocused = false;
|
self.unfocused = false;
|
||||||
|
if let Some(ref mut s) = self.view {
|
||||||
|
s.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
|
}
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.view = None;
|
self.view = None;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -64,6 +64,7 @@ pub struct StatusBar {
|
||||||
progress_spinner: ProgressSpinner,
|
progress_spinner: ProgressSpinner,
|
||||||
in_progress_jobs: HashSet<JobId>,
|
in_progress_jobs: HashSet<JobId>,
|
||||||
done_jobs: HashSet<JobId>,
|
done_jobs: HashSet<JobId>,
|
||||||
|
scroll_contexts: IndexMap<ComponentId, ScrollContext>,
|
||||||
|
|
||||||
auto_complete: AutoComplete,
|
auto_complete: AutoComplete,
|
||||||
cmd_history: Vec<String>,
|
cmd_history: Vec<String>,
|
||||||
|
@ -104,6 +105,7 @@ impl StatusBar {
|
||||||
progress_spinner,
|
progress_spinner,
|
||||||
in_progress_jobs: HashSet::default(),
|
in_progress_jobs: HashSet::default(),
|
||||||
done_jobs: HashSet::default(),
|
done_jobs: HashSet::default(),
|
||||||
|
scroll_contexts: IndexMap::default(),
|
||||||
cmd_history: crate::command::history::old_cmd_history(),
|
cmd_history: crate::command::history::old_cmd_history(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,6 +142,33 @@ impl StatusBar {
|
||||||
grid[(x, y)].set_attrs(attribute.attrs | Attr::BOLD);
|
grid[(x, y)].set_attrs(attribute.attrs | Attr::BOLD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some((
|
||||||
|
_,
|
||||||
|
ScrollContext {
|
||||||
|
shown_lines,
|
||||||
|
total_lines,
|
||||||
|
has_more_lines,
|
||||||
|
},
|
||||||
|
)) = self.scroll_contexts.last()
|
||||||
|
{
|
||||||
|
let s = format!(
|
||||||
|
"| {shown_percentage}% {line_desc}{shown_lines}/{total_lines}{has_more_lines}",
|
||||||
|
line_desc = if grid.ascii_drawing { "lines:" } else { "☰ " },
|
||||||
|
shown_percentage = (*shown_lines as f32 / (*total_lines as f32) * 100.0) as usize,
|
||||||
|
shown_lines = *shown_lines,
|
||||||
|
total_lines = *total_lines,
|
||||||
|
has_more_lines = if *has_more_lines { "(+)" } else { "" }
|
||||||
|
);
|
||||||
|
write_string_to_grid(
|
||||||
|
&s,
|
||||||
|
grid,
|
||||||
|
attribute.fg,
|
||||||
|
attribute.bg,
|
||||||
|
attribute.attrs,
|
||||||
|
((x + 1, y), bottom_right!(area)),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let (mut x, y) = bottom_right!(area);
|
let (mut x, y) = bottom_right!(area);
|
||||||
if self.progress_spinner.is_active() {
|
if self.progress_spinner.is_active() {
|
||||||
|
@ -706,6 +735,21 @@ impl Component for StatusBar {
|
||||||
self.progress_spinner.set_dirty(true);
|
self.progress_spinner.set_dirty(true);
|
||||||
self.in_progress_jobs.insert(*job_id);
|
self.in_progress_jobs.insert(*job_id);
|
||||||
}
|
}
|
||||||
|
UIEvent::StatusEvent(StatusEvent::ScrollUpdate(ScrollUpdate::End(component_id))) => {
|
||||||
|
if self.scroll_contexts.remove(component_id).is_some() {
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::StatusEvent(StatusEvent::ScrollUpdate(ScrollUpdate::Update {
|
||||||
|
id,
|
||||||
|
context,
|
||||||
|
})) => {
|
||||||
|
if self.scroll_contexts.insert(*id, *context) != Some(*context) {
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
UIEvent::Timer(_) => {
|
UIEvent::Timer(_) => {
|
||||||
if self.progress_spinner.process_event(event, context) {
|
if self.progress_spinner.process_event(event, context) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -975,6 +1019,21 @@ impl Component for Tabbed {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if height.wrapping_div(rows + 1) > 0 || width.wrapping_div(cols + 1) > 0 {
|
if height.wrapping_div(rows + 1) > 0 || width.wrapping_div(cols + 1) > 0 {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::Update {
|
||||||
|
id: self.id,
|
||||||
|
context: ScrollContext {
|
||||||
|
shown_lines: std::cmp::min(
|
||||||
|
(height).saturating_sub(rows + 1),
|
||||||
|
self.help_screen_cursor.1,
|
||||||
|
) + rows,
|
||||||
|
total_lines: height,
|
||||||
|
has_more_lines: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)));
|
||||||
ScrollBar::default().set_show_arrows(true).draw(
|
ScrollBar::default().set_show_arrows(true).draw(
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
|
@ -989,6 +1048,12 @@ impl Component for Tabbed {
|
||||||
/* length */
|
/* length */
|
||||||
height,
|
height,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
return;
|
return;
|
||||||
|
@ -1192,6 +1257,21 @@ impl Component for Tabbed {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if height.wrapping_div(rows + 1) > 0 || width.wrapping_div(cols + 1) > 0 {
|
if height.wrapping_div(rows + 1) > 0 || width.wrapping_div(cols + 1) > 0 {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::Update {
|
||||||
|
id: self.id,
|
||||||
|
context: ScrollContext {
|
||||||
|
shown_lines: std::cmp::min(
|
||||||
|
(height).saturating_sub(rows),
|
||||||
|
self.help_screen_cursor.1,
|
||||||
|
) + rows,
|
||||||
|
total_lines: height,
|
||||||
|
has_more_lines: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)));
|
||||||
ScrollBar::default().set_show_arrows(true).draw(
|
ScrollBar::default().set_show_arrows(true).draw(
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
|
@ -1206,6 +1286,12 @@ impl Component for Tabbed {
|
||||||
/* length */
|
/* length */
|
||||||
height,
|
height,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
|
@ -1250,6 +1336,11 @@ impl Component for Tabbed {
|
||||||
if self.show_shortcuts {
|
if self.show_shortcuts {
|
||||||
/* children below the shortcut overlay must be redrawn */
|
/* children below the shortcut overlay must be redrawn */
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
self.show_shortcuts = !self.show_shortcuts;
|
self.show_shortcuts = !self.show_shortcuts;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -1281,6 +1372,8 @@ impl Component for Tabbed {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if let Some(c_idx) = self.children.iter().position(|x| x.id() == *id) {
|
if let Some(c_idx) = self.children.iter().position(|x| x.id() == *id) {
|
||||||
|
self.children[c_idx]
|
||||||
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.children.remove(c_idx);
|
self.children.remove(c_idx);
|
||||||
self.cursor_pos = 0;
|
self.cursor_pos = 0;
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
|
|
|
@ -533,28 +533,30 @@ impl Component for Pager {
|
||||||
}
|
}
|
||||||
if (rows < height) || self.search.is_some() {
|
if (rows < height) || self.search.is_some() {
|
||||||
const RESULTS_STR: &str = "Results for ";
|
const RESULTS_STR: &str = "Results for ";
|
||||||
let shown_percentage =
|
|
||||||
((self.cursor.1 + rows) as f32 / (height as f32) * 100.0) as usize;
|
|
||||||
let shown_lines = self.cursor.1 + rows;
|
let shown_lines = self.cursor.1 + rows;
|
||||||
let total_lines = height;
|
let total_lines = height;
|
||||||
let scrolling = if rows < height {
|
if rows < height {
|
||||||
format!(
|
context
|
||||||
"{shown_percentage}% {line_desc}{shown_lines}/{total_lines}{has_more_lines}",
|
.replies
|
||||||
line_desc = if grid.ascii_drawing { "lines:" } else { "☰ " },
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
shown_percentage = shown_percentage,
|
ScrollUpdate::Update {
|
||||||
shown_lines = shown_lines,
|
id: self.id,
|
||||||
total_lines = total_lines,
|
context: ScrollContext {
|
||||||
has_more_lines = if self.line_breaker.is_finished() {
|
shown_lines,
|
||||||
""
|
total_lines,
|
||||||
} else {
|
has_more_lines: !self.line_breaker.is_finished(),
|
||||||
"(+)"
|
},
|
||||||
}
|
},
|
||||||
)
|
)));
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
};
|
};
|
||||||
let search_results = if let Some(ref search) = self.search {
|
if let Some(ref search) = self.search {
|
||||||
format!(
|
let status_message = format!(
|
||||||
"{results_str}{search_pattern}: {current_pos}/{total_results}{has_more_lines}",
|
"{results_str}{search_pattern}: {current_pos}/{total_results}{has_more_lines}",
|
||||||
results_str = RESULTS_STR,
|
results_str = RESULTS_STR,
|
||||||
search_pattern = &search.pattern,
|
search_pattern = &search.pattern,
|
||||||
|
@ -569,39 +571,29 @@ impl Component for Pager {
|
||||||
} else {
|
} else {
|
||||||
"(+)"
|
"(+)"
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
} else {
|
let mut attribute = crate::conf::value(context, "status.bar");
|
||||||
String::new()
|
if !context.settings.terminal.use_color() {
|
||||||
};
|
attribute.attrs |= Attr::REVERSE;
|
||||||
let status_message = format!(
|
}
|
||||||
"{search_results}{divider}{scrolling}",
|
let (_, y) = write_string_to_grid(
|
||||||
search_results = search_results,
|
&status_message,
|
||||||
divider = if self.search.is_some() { " " } else { "" },
|
grid,
|
||||||
scrolling = scrolling,
|
attribute.fg,
|
||||||
);
|
attribute.bg,
|
||||||
let mut attribute = crate::conf::value(context, "status.bar");
|
attribute.attrs,
|
||||||
if !context.settings.terminal.use_color() {
|
(
|
||||||
attribute.attrs |= Attr::REVERSE;
|
set_y(upper_left!(area), get_y(bottom_right!(area))),
|
||||||
}
|
bottom_right!(area),
|
||||||
let (_, y) = write_string_to_grid(
|
),
|
||||||
&status_message,
|
None,
|
||||||
grid,
|
);
|
||||||
attribute.fg,
|
/* set search pattern to italics */
|
||||||
attribute.bg,
|
|
||||||
attribute.attrs,
|
|
||||||
(
|
|
||||||
set_y(upper_left!(area), get_y(bottom_right!(area))),
|
|
||||||
bottom_right!(area),
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
/* set search pattern to italics */
|
|
||||||
if let Some(ref search) = self.search {
|
|
||||||
let start_x = get_x(upper_left!(area)) + RESULTS_STR.len();
|
let start_x = get_x(upper_left!(area)) + RESULTS_STR.len();
|
||||||
for c in grid.row_iter(start_x..(start_x + search.pattern.grapheme_width()), y) {
|
for c in grid.row_iter(start_x..(start_x + search.pattern.grapheme_width()), y) {
|
||||||
grid[c].set_attrs(attribute.attrs | Attr::ITALICS);
|
grid[c].set_attrs(attribute.attrs | Attr::ITALICS);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
|
@ -746,6 +738,13 @@ impl Component for Pager {
|
||||||
self.initialised = false;
|
self.initialised = false;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
UIEvent::VisibilityChange(false) => {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub use self::helpers::*;
|
||||||
use super::command::Action;
|
use super::command::Action;
|
||||||
use super::jobs::{JobExecutor, JobId};
|
use super::jobs::{JobExecutor, JobId};
|
||||||
use super::terminal::*;
|
use super::terminal::*;
|
||||||
use crate::components::{Component, ComponentId};
|
use crate::components::{Component, ComponentId, ScrollUpdate};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use melib::backends::{AccountHash, BackendEvent, MailboxHash};
|
use melib::backends::{AccountHash, BackendEvent, MailboxHash};
|
||||||
|
@ -57,6 +57,7 @@ pub enum StatusEvent {
|
||||||
JobFinished(JobId),
|
JobFinished(JobId),
|
||||||
JobCanceled(JobId),
|
JobCanceled(JobId),
|
||||||
SetMouse(bool),
|
SetMouse(bool),
|
||||||
|
ScrollUpdate(ScrollUpdate),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads
|
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads
|
||||||
|
@ -149,6 +150,7 @@ pub enum UIEvent {
|
||||||
ConfigReload {
|
ConfigReload {
|
||||||
old_settings: crate::conf::Settings,
|
old_settings: crate::conf::Settings,
|
||||||
},
|
},
|
||||||
|
VisibilityChange(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) -> () + Send + 'static>);
|
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) -> () + Send + 'static>);
|
||||||
|
|
Loading…
Reference in New Issue