forked from mirrors/rmenu
feat: paginated results
This commit is contained in:
parent
7c3c3cf486
commit
3f4fd61aa8
@ -73,6 +73,8 @@ struct IndexTemplate<'a> {
|
|||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "results.html")]
|
#[template(path = "results.html")]
|
||||||
struct ResultsTemplate<'a> {
|
struct ResultsTemplate<'a> {
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
results: &'a Vec<&'a Entry>,
|
results: &'a Vec<&'a Entry>,
|
||||||
config: &'a Config,
|
config: &'a Config,
|
||||||
}
|
}
|
||||||
@ -81,6 +83,7 @@ struct ResultsTemplate<'a> {
|
|||||||
struct AppState<'a> {
|
struct AppState<'a> {
|
||||||
pos: usize,
|
pos: usize,
|
||||||
subpos: usize,
|
subpos: usize,
|
||||||
|
page: usize,
|
||||||
search: String,
|
search: String,
|
||||||
results: Vec<&'a Entry>,
|
results: Vec<&'a Entry>,
|
||||||
data: &'a AppData,
|
data: &'a AppData,
|
||||||
@ -97,27 +100,40 @@ impl<'a> AppState<'a> {
|
|||||||
Self {
|
Self {
|
||||||
pos: 0,
|
pos: 0,
|
||||||
subpos: 0,
|
subpos: 0,
|
||||||
|
page: 0,
|
||||||
search: "".to_owned(),
|
search: "".to_owned(),
|
||||||
results: vec![],
|
results: vec![],
|
||||||
data,
|
data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update AppState w/ new Search and Render HTML Results
|
/// Render Current Page of Results
|
||||||
fn search(&mut self, search: String) -> String {
|
fn render_results_page(&self) -> String {
|
||||||
// update search and calculate matching results
|
let size = self.data.config.page_size;
|
||||||
let sfn = build_searchfn(&self.data.config, &search);
|
let start = self.page * size;
|
||||||
self.pos = 0;
|
let max = (self.page + 1) * size;
|
||||||
self.search = search;
|
let nresults = std::cmp::max(self.results.len(), 1);
|
||||||
self.results = self.data.entries.iter().filter(|e| sfn(e)).collect();
|
let end = std::cmp::min(max, nresults - 1);
|
||||||
// generate results html from template
|
// generate results html from template
|
||||||
let template = ResultsTemplate {
|
let template = ResultsTemplate {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
config: &self.data.config,
|
config: &self.data.config,
|
||||||
results: &self.results,
|
results: &self.results,
|
||||||
};
|
};
|
||||||
template.render().unwrap()
|
template.render().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update AppState w/ new Search and Render HTML Results
|
||||||
|
fn search(&mut self, search: String) -> String {
|
||||||
|
let sfn = build_searchfn(&self.data.config, &search);
|
||||||
|
self.pos = 0;
|
||||||
|
self.page = 0;
|
||||||
|
self.search = search;
|
||||||
|
self.results = self.data.entries.iter().filter(|e| sfn(e)).collect();
|
||||||
|
self.render_results_page()
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute Action associated w/ Current Position/Subposition
|
/// Execute Action associated w/ Current Position/Subposition
|
||||||
fn execute(&self) {
|
fn execute(&self) {
|
||||||
log::debug!("execute {} {}", self.pos, self.subpos);
|
log::debug!("execute {} {}", self.pos, self.subpos);
|
||||||
@ -132,14 +148,36 @@ impl<'a> AppState<'a> {
|
|||||||
execute(action, self.data.config.terminal.clone());
|
execute(action, self.data.config.terminal.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Return Additional Page Results to Load (when nessesary)
|
||||||
fn move_up(&mut self, up: usize) {
|
fn append_results(&mut self, smooth: bool) -> Option<String> {
|
||||||
self.pos = std::cmp::max(self.pos, up) - up;
|
let pos = self.pos as f64;
|
||||||
|
let size = self.data.config.page_size as f64;
|
||||||
|
let pages = pos / size;
|
||||||
|
let ratio = (pos % size) / size;
|
||||||
|
if pages > self.page as f64 && ratio > self.data.config.page_load {
|
||||||
|
println!("loading next page!");
|
||||||
|
self.page += 1;
|
||||||
|
let results = self.render_results_page();
|
||||||
|
return Some(format!("append({}, {results:?}, {smooth})", self.pos));
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn move_down(&mut self, down: usize) {
|
fn move_up(&mut self, up: usize) -> Option<String> {
|
||||||
self.pos = std::cmp::min(self.pos + down, self.results.len() - 1);
|
self.pos = std::cmp::max(self.pos, up) - up;
|
||||||
|
Some(format!("setpos({})", self.pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn move_down(&mut self, down: usize) -> Option<String> {
|
||||||
|
let max = (self.page + 1) * self.data.config.page_size;
|
||||||
|
let end = std::cmp::min(max, self.results.len()) - 1;
|
||||||
|
self.pos = std::cmp::min(self.pos + down, end);
|
||||||
|
match self.append_results(false) {
|
||||||
|
Some(operation) => Some(operation),
|
||||||
|
None => Some(format!("setpos({})", self.pos)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle Search Event sent by UI
|
/// Handle Search Event sent by UI
|
||||||
@ -148,8 +186,6 @@ impl<'a> AppState<'a> {
|
|||||||
Some(format!("update({results:?})"))
|
Some(format!("update({results:?})"))
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: need to increase page-size as cursor moves down
|
|
||||||
//TODO: add loading on scroll as well
|
|
||||||
//TODO: add submenu access and selection
|
//TODO: add submenu access and selection
|
||||||
//TODO: put back main to reference actual config
|
//TODO: put back main to reference actual config
|
||||||
//TODO: update sway config to make borderless
|
//TODO: update sway config to make borderless
|
||||||
@ -165,11 +201,9 @@ impl<'a> AppState<'a> {
|
|||||||
} else if matches(&keybinds.exit, &mods, &code) {
|
} else if matches(&keybinds.exit, &mods, &code) {
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
} else if matches(&keybinds.move_next, &mods, &code) {
|
} else if matches(&keybinds.move_next, &mods, &code) {
|
||||||
self.move_down(1);
|
self.move_down(1)
|
||||||
Some(format!("setpos({})", self.pos))
|
|
||||||
} else if matches(&keybinds.move_prev, &mods, &code) {
|
} else if matches(&keybinds.move_prev, &mods, &code) {
|
||||||
self.move_up(1);
|
self.move_up(1)
|
||||||
Some(format!("setpos({})", self.pos))
|
|
||||||
} else if matches(&keybinds.open_menu, &mods, &code) {
|
} else if matches(&keybinds.open_menu, &mods, &code) {
|
||||||
// k_updater.set_event(KeyEvent::OpenMenu);
|
// k_updater.set_event(KeyEvent::OpenMenu);
|
||||||
None
|
None
|
||||||
@ -177,11 +211,9 @@ impl<'a> AppState<'a> {
|
|||||||
// k_updater.set_event(KeyEvent::CloseMenu);
|
// k_updater.set_event(KeyEvent::CloseMenu);
|
||||||
None
|
None
|
||||||
} else if matches(&keybinds.jump_next, &mods, &code) {
|
} else if matches(&keybinds.jump_next, &mods, &code) {
|
||||||
self.move_down(self.data.config.jump_dist);
|
self.move_down(self.data.config.jump_dist)
|
||||||
Some(format!("setpos({})", self.pos))
|
|
||||||
} else if matches(&keybinds.jump_prev, &mods, &code) {
|
} else if matches(&keybinds.jump_prev, &mods, &code) {
|
||||||
self.move_up(self.data.config.jump_dist);
|
self.move_up(self.data.config.jump_dist)
|
||||||
Some(format!("setpos({})", self.pos))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -206,7 +238,10 @@ impl<'a> AppState<'a> {
|
|||||||
if let Some((pos, subpos)) = self.parse_id_pos(&id) {
|
if let Some((pos, subpos)) = self.parse_id_pos(&id) {
|
||||||
self.pos = pos;
|
self.pos = pos;
|
||||||
self.subpos = subpos;
|
self.subpos = subpos;
|
||||||
return Some(format!("setpos({pos}, true)"));
|
return match self.append_results(true) {
|
||||||
|
Some(op) => Some(op),
|
||||||
|
None => Some(format!("setpos({pos}, true)")),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClickEvent::Double { id } => {
|
ClickEvent::Double { id } => {
|
||||||
@ -222,7 +257,13 @@ impl<'a> AppState<'a> {
|
|||||||
|
|
||||||
/// Handle Scrolling Events sent by UI
|
/// Handle Scrolling Events sent by UI
|
||||||
fn scroll_event(&mut self, event: ScrollEvent) -> Option<String> {
|
fn scroll_event(&mut self, event: ScrollEvent) -> Option<String> {
|
||||||
println!("scroll: {event:?}");
|
// load additonal results when scrolled near bottom
|
||||||
|
let ratio = event.y as f64 / event.maxy as f64;
|
||||||
|
if ratio >= self.data.config.page_load {
|
||||||
|
self.page += 1;
|
||||||
|
let results = self.render_results_page();
|
||||||
|
return Some(format!("append(null, {results:?})"));
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{%- for (i, entry) in results.iter().enumerate() %}
|
{%- for i in start..end %}
|
||||||
|
{% let entry = results[i] %}
|
||||||
<div class="result-entry">
|
<div class="result-entry">
|
||||||
<div
|
<div
|
||||||
id="result-{{ i }}"
|
id="result-{{ i }}"
|
||||||
|
@ -70,6 +70,14 @@ function update(html) {
|
|||||||
setpos(0);
|
setpos(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append Results HTML
|
||||||
|
function append(pos, html, smooth = false) {
|
||||||
|
results.innerHTML += html;
|
||||||
|
if (pos != null && pos != undefined) {
|
||||||
|
setpos(pos, smooth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Init */
|
/* Init */
|
||||||
|
|
||||||
// start position at zero
|
// start position at zero
|
||||||
|
Loading…
Reference in New Issue
Block a user