Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ pub enum SearchAction {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PageAction {
SwitchRenderer(Renderer),
ToggleContents,
ToggleTocFocus,
ToggleTocVisibility,

SelectFirstLink,
SelectLastLink,
Expand Down
9 changes: 8 additions & 1 deletion src/components/help_popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,14 @@ impl HelpPopupComponent {
"toggle page language"
),
convert_binding!(config.bindings.page.toggle_zen_mode, "toggle zen mode"),
convert_binding!(config.bindings.page.toggle_toc, "toggle table of contents"),
convert_binding!(
config.bindings.page.toggle_toc_visibility,
"toggle table of contents visibility"
),
convert_binding!(
config.bindings.page.toggle_toc_focus,
"toggle table of contents focus"
),
]
.into();

Expand Down
120 changes: 85 additions & 35 deletions src/components/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,13 @@ pub struct PageComponent {
theme: Arc<Theme>,

is_contents: bool,
is_toc_visible: bool,
is_zen_mode: bool,
#[serde(skip)]
contents_state: PageContentsState,

#[serde(skip)]
pending_anchor: Option<String>,
}

impl PageComponent {
Expand All @@ -111,9 +115,12 @@ impl PageComponent {
selected: (0, 0),

is_contents: false,
is_toc_visible: true,
is_zen_mode: config.page.default_zen,
contents_state,

pending_anchor: None,

config,
theme,
}
Expand Down Expand Up @@ -300,6 +307,16 @@ impl PageComponent {
self.page.content.nth(self.selected.0)
}

fn top_node_index(&self) -> Option<usize> {
let page = self.rendered_page(self.viewport.width)?;
let line_idx = self.viewport.y as usize;
page.lines
.get(line_idx)?
.iter()
.find(|w| w.index != usize::MAX)
.map(|w| w.index)
}

fn select_first(&mut self) {
if self.page.content.nth(0).is_none() {
return;
Expand Down Expand Up @@ -591,35 +608,29 @@ impl PageComponent {
}

fn render_toc(&mut self, f: &mut Frame<'_>, area: Rect) -> Rect {
if self.config.page.toc.enabled {
let mut constraints = [
Constraint::Percentage(
100_u16.saturating_sub(self.config.page.toc.width_percentage),
),
Constraint::Percentage(self.config.page.toc.width_percentage),
];

if self.config.page.toc.position == TocConfigPosition::Left {
constraints.reverse();
}
let mut constraints = [
Constraint::Percentage(100_u16.saturating_sub(self.config.page.toc.width_percentage)),
Constraint::Percentage(self.config.page.toc.width_percentage),
];

let splits = Layout::default()
.direction(Direction::Horizontal)
.constraints(constraints)
.split(area);

match self.config.page.toc.position {
TocConfigPosition::Left => {
self.render_contents(f, splits[0]);
splits[1]
}
TocConfigPosition::Right => {
self.render_contents(f, splits[1]);
splits[0]
}
if self.config.page.toc.position == TocConfigPosition::Left {
constraints.reverse();
}

let splits = Layout::default()
.direction(Direction::Horizontal)
.constraints(constraints)
.split(area);

match self.config.page.toc.position {
TocConfigPosition::Left => {
self.render_contents(f, splits[0]);
splits[1]
}
TocConfigPosition::Right => {
self.render_contents(f, splits[1]);
splits[0]
}
} else {
area
}
}

Expand Down Expand Up @@ -651,7 +662,20 @@ impl Component for PageComponent {
}
};
}
matches_binding!(toggle_toc, Action::Page(PageAction::ToggleContents));
matches_binding!(toggle_toc_visibility, {
self.is_toc_visible = !self.is_toc_visible;
if !self.is_toc_visible {
self.is_contents = false;
}
ActionResult::consumed()
});

matches_binding!(toggle_toc_focus, {
if self.is_toc_visible {
self.is_contents = !self.is_contents;
}
ActionResult::consumed()
});

if self.is_contents {
matches_binding!(jump_to_header, {
Expand All @@ -660,10 +684,8 @@ impl Component for PageComponent {
info!("no header selected");
return ActionResult::Ignored;
}
ActionPacket::single(Action::Page(PageAction::GoToHeader(
header.unwrap().anchor.to_string(),
)))
.action(Action::Page(PageAction::ToggleContents))

Action::Page(PageAction::GoToHeader(header.unwrap().anchor.to_string()))
});
return ActionResult::Ignored;
}
Expand Down Expand Up @@ -692,7 +714,17 @@ impl Component for PageComponent {
match action {
Action::Page(page_action) => match page_action {
PageAction::SwitchRenderer(renderer) => self.switch_renderer(renderer),
PageAction::ToggleContents => self.is_contents = !self.is_contents,
PageAction::ToggleTocFocus => {
if self.is_toc_visible {
self.is_contents = !self.is_contents;
}
}
PageAction::ToggleTocVisibility => {
self.is_toc_visible = !self.is_toc_visible;
if !self.is_toc_visible {
self.is_contents = false;
}
}

PageAction::SelectFirstLink => self.select_first(),
PageAction::SelectLastLink => self.select_last(),
Expand All @@ -702,7 +734,10 @@ impl Component for PageComponent {
PageAction::SelectPrevLink => self.select_prev(),
PageAction::SelectNextLink => self.select_next(),

PageAction::GoToHeader(anchor) => self.select_header(anchor),
PageAction::GoToHeader(anchor) => {
self.is_contents = false;
self.pending_anchor = Some(anchor);
}
},
Action::ScrollUp(amount) => self.scroll_up(amount),
Action::ScrollDown(amount) => self.scroll_down(amount),
Expand All @@ -722,6 +757,13 @@ impl Component for PageComponent {
fn render(&mut self, f: &mut Frame, mut area: Rect) {
let zen_mode = self.config.page.zen_mode.clone();

let old_width = self.viewport.width;
let top_node = if old_width > 0 {
self.top_node_index()
} else {
None
};

// when in zen mode, use the constraints for the zen mode, otherwise the regular padding
area = if self.is_zen_mode() {
let [area] = Layout::horizontal([self.config.page.zen_horizontal])
Expand All @@ -738,7 +780,7 @@ impl Component for PageComponent {
area = self.render_status_bar(f, area);
}

if !self.is_zen_mode || zen_mode.contains(ZenModeComponents::TOC) {
if (!self.is_zen_mode || zen_mode.contains(ZenModeComponents::TOC)) && self.is_toc_visible {
area = self.render_toc(f, area);
}

Expand All @@ -754,6 +796,14 @@ impl Component for PageComponent {
self.viewport.width = page_area.width;
self.viewport.height = page_area.height;

if let Some(anchor) = self.pending_anchor.take() {
self.select_header(anchor);
} else if old_width != self.viewport.width && old_width > 0 {
if let Some(idx) = top_node {
self.scroll_to_node(idx);
}
}

let rendered_page = rendered_page!(self, page_area.width);
let mut lines: Vec<Line> = rendered_page
.lines
Expand Down
14 changes: 9 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ fn override_bindings_config(config: &mut Keybindings, user_config: UserKeybindin
open_link,
toggle_page_language_selection,
toggle_zen_mode,
toggle_toc
toggle_toc_focus,
toggle_toc_visibility
});
}
}
Expand Down Expand Up @@ -505,7 +506,8 @@ pub struct PageKeybindings {

pub toggle_page_language_selection: Keybinding,
pub toggle_zen_mode: Keybinding,
pub toggle_toc: Keybinding,
pub toggle_toc_focus: Keybinding,
pub toggle_toc_visibility: Keybinding,
}

pub struct Keybindings {
Expand Down Expand Up @@ -552,7 +554,7 @@ impl Config {
toc: TocConfig {
enabled: true,
width_percentage: 20,
position: TocConfigPosition::Right,
position: TocConfigPosition::Left,
title: TocConfigTitle::Default,
item_format: "{NUMBER} {TEXT}".to_string(),

Expand Down Expand Up @@ -608,7 +610,8 @@ impl Config {
open_link: keybinding!([KeyCode::Enter;]),
toggle_page_language_selection: keybinding!([KeyCode::F(3);]),
toggle_zen_mode: keybinding!([KeyCode::F(4);]),
toggle_toc: keybinding!([KeyCode::Tab;, KeyCode::BackTab;]),
toggle_toc_focus: keybinding!([KeyCode::Tab;, KeyCode::BackTab;]),
toggle_toc_visibility: keybinding!([KeyCode::Char('e');]),
},
},
api: ApiConfig {
Expand Down Expand Up @@ -877,7 +880,8 @@ user_keybindings!(
open_link,
toggle_page_language_selection,
toggle_zen_mode,
toggle_toc
toggle_toc_focus,
toggle_toc_visibility
);

#[derive(Deserialize)]
Expand Down