Feat: collapsible help
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
use crate::model::categories;
|
||||
use crate::model::categories::{self, CatEntry, CATEGORIES};
|
||||
use crate::state::{DictFocus, UiState};
|
||||
|
||||
use CatEntry::{Category, Section};
|
||||
|
||||
pub fn toggle_focus(ui: &mut UiState) {
|
||||
ui.dict_focus = match ui.dict_focus {
|
||||
DictFocus::Categories => DictFocus::Words,
|
||||
@@ -11,17 +13,86 @@ pub fn toggle_focus(ui: &mut UiState) {
|
||||
pub fn select_category(ui: &mut UiState, index: usize) {
|
||||
if index < categories::category_count() {
|
||||
ui.dict_category = index;
|
||||
ui.dict_on_section = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum VisibleItem {
|
||||
Category(usize),
|
||||
Section(usize),
|
||||
}
|
||||
|
||||
fn visible_items(collapsed: &[bool]) -> Vec<VisibleItem> {
|
||||
let mut result = Vec::new();
|
||||
let mut section_idx = 0usize;
|
||||
let mut cat_idx = 0usize;
|
||||
let mut skipping = false;
|
||||
for entry in CATEGORIES.iter() {
|
||||
match entry {
|
||||
Section(_) => {
|
||||
let is_collapsed = collapsed.get(section_idx).copied().unwrap_or(false);
|
||||
if is_collapsed {
|
||||
result.push(VisibleItem::Section(section_idx));
|
||||
}
|
||||
skipping = is_collapsed;
|
||||
section_idx += 1;
|
||||
}
|
||||
Category(_) => {
|
||||
if !skipping {
|
||||
result.push(VisibleItem::Category(cat_idx));
|
||||
}
|
||||
cat_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn find_current(items: &[VisibleItem], ui: &UiState) -> usize {
|
||||
if let Some(s) = ui.dict_on_section {
|
||||
items
|
||||
.iter()
|
||||
.position(|v| matches!(v, VisibleItem::Section(idx) if *idx == s))
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
items
|
||||
.iter()
|
||||
.position(|v| matches!(v, VisibleItem::Category(idx) if *idx == ui.dict_category))
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_selection(ui: &mut UiState, item: VisibleItem) {
|
||||
match item {
|
||||
VisibleItem::Category(idx) => {
|
||||
ui.dict_category = idx;
|
||||
ui.dict_on_section = None;
|
||||
}
|
||||
VisibleItem::Section(idx) => {
|
||||
ui.dict_on_section = Some(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_category(ui: &mut UiState) {
|
||||
let count = categories::category_count();
|
||||
ui.dict_category = (ui.dict_category + 1) % count;
|
||||
let items = visible_items(&ui.dict_collapsed);
|
||||
if items.is_empty() {
|
||||
return;
|
||||
}
|
||||
let cur = find_current(&items, ui);
|
||||
let next = (cur + 1) % items.len();
|
||||
apply_selection(ui, items[next]);
|
||||
}
|
||||
|
||||
pub fn prev_category(ui: &mut UiState) {
|
||||
let count = categories::category_count();
|
||||
ui.dict_category = (ui.dict_category + count - 1) % count;
|
||||
let items = visible_items(&ui.dict_collapsed);
|
||||
if items.is_empty() {
|
||||
return;
|
||||
}
|
||||
let cur = find_current(&items, ui);
|
||||
let next = (cur + items.len() - 1) % items.len();
|
||||
apply_selection(ui, items[next]);
|
||||
}
|
||||
|
||||
pub fn scroll_down(ui: &mut UiState, n: usize) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::model::docs;
|
||||
use crate::model::docs::{self, DocEntry, DOCS};
|
||||
use crate::state::{HelpFocus, UiState};
|
||||
|
||||
use DocEntry::{Section, Topic};
|
||||
|
||||
pub fn toggle_focus(ui: &mut UiState) {
|
||||
ui.help_focus = match ui.help_focus {
|
||||
HelpFocus::Topics => HelpFocus::Content,
|
||||
@@ -11,18 +13,87 @@ pub fn toggle_focus(ui: &mut UiState) {
|
||||
pub fn select_topic(ui: &mut UiState, index: usize) {
|
||||
if index < docs::topic_count() {
|
||||
ui.help_topic = index;
|
||||
ui.help_on_section = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum VisibleItem {
|
||||
Topic(usize),
|
||||
Section(usize),
|
||||
}
|
||||
|
||||
fn visible_items(collapsed: &[bool]) -> Vec<VisibleItem> {
|
||||
let mut result = Vec::new();
|
||||
let mut section_idx = 0usize;
|
||||
let mut topic_idx = 0usize;
|
||||
let mut skipping = false;
|
||||
for entry in DOCS.iter() {
|
||||
match entry {
|
||||
Section(_) => {
|
||||
let is_collapsed = collapsed.get(section_idx).copied().unwrap_or(false);
|
||||
if is_collapsed {
|
||||
result.push(VisibleItem::Section(section_idx));
|
||||
}
|
||||
skipping = is_collapsed;
|
||||
section_idx += 1;
|
||||
}
|
||||
Topic(_, _) => {
|
||||
if !skipping {
|
||||
result.push(VisibleItem::Topic(topic_idx));
|
||||
}
|
||||
topic_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn find_current(items: &[VisibleItem], ui: &UiState) -> usize {
|
||||
if let Some(s) = ui.help_on_section {
|
||||
items
|
||||
.iter()
|
||||
.position(|v| matches!(v, VisibleItem::Section(idx) if *idx == s))
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
items
|
||||
.iter()
|
||||
.position(|v| matches!(v, VisibleItem::Topic(idx) if *idx == ui.help_topic))
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_selection(ui: &mut UiState, item: VisibleItem) {
|
||||
match item {
|
||||
VisibleItem::Topic(idx) => {
|
||||
ui.help_topic = idx;
|
||||
ui.help_on_section = None;
|
||||
}
|
||||
VisibleItem::Section(idx) => {
|
||||
ui.help_on_section = Some(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_topic(ui: &mut UiState, n: usize) {
|
||||
let count = docs::topic_count();
|
||||
ui.help_topic = (ui.help_topic + n) % count;
|
||||
let items = visible_items(&ui.help_collapsed);
|
||||
if items.is_empty() {
|
||||
return;
|
||||
}
|
||||
let cur = find_current(&items, ui);
|
||||
let next = (cur + n) % items.len();
|
||||
apply_selection(ui, items[next]);
|
||||
ui.help_focused_block = None;
|
||||
}
|
||||
|
||||
pub fn prev_topic(ui: &mut UiState, n: usize) {
|
||||
let count = docs::topic_count();
|
||||
ui.help_topic = (ui.help_topic + count - (n % count)) % count;
|
||||
let items = visible_items(&ui.help_collapsed);
|
||||
if items.is_empty() {
|
||||
return;
|
||||
}
|
||||
let cur = find_current(&items, ui);
|
||||
let next = (cur + items.len() - (n % items.len())) % items.len();
|
||||
apply_selection(ui, items[next]);
|
||||
ui.help_focused_block = None;
|
||||
}
|
||||
|
||||
@@ -49,6 +120,7 @@ pub fn search_input(ui: &mut UiState, c: char) {
|
||||
ui.help_search_query.push(c);
|
||||
if let Some((topic, line)) = docs::find_match(&ui.help_search_query) {
|
||||
ui.help_topic = topic;
|
||||
ui.help_on_section = None;
|
||||
ui.help_scrolls[topic] = line;
|
||||
}
|
||||
}
|
||||
@@ -60,6 +132,7 @@ pub fn search_backspace(ui: &mut UiState) {
|
||||
}
|
||||
if let Some((topic, line)) = docs::find_match(&ui.help_search_query) {
|
||||
ui.help_topic = topic;
|
||||
ui.help_on_section = None;
|
||||
ui.help_scrolls[topic] = line;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user