Skip to content

Commit

Permalink
feat(tui): add filter tab (#13)
Browse files Browse the repository at this point in the history
* feat(core): add task filter

* refactor: rename tab components

* feat(app): send only key event actions if a component is in editing mode

* feat(explorer): if not focused, handle only tab events

* wip: filter tab can filter tasks

* chore: improve test vault

* feat(filter): non case sensitive search and able to search by task state

* feat(tui): add hint on keybindings in footer

* fix(filter): accept if task's tags contain searched tags as substring

* feat(filter_tab): add a list of tags found in the vault, which updates during search

* chore: fix clippy warnings
  • Loading branch information
louis-thevenet committed Sep 29, 2024
1 parent c2abfef commit 825523a
Show file tree
Hide file tree
Showing 15 changed files with 724 additions and 108 deletions.
4 changes: 3 additions & 1 deletion .config/config.json5
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"<Ctrl-Left>":"TabLeft",
"<Ctrl-h>":"TabLeft",
},
"Tags": {
"Filter": {
"<q>": "Quit",
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
Expand All @@ -27,6 +27,8 @@
"<Ctrl-l>":"TabRight",
"<Ctrl-Left>":"TabLeft",
"<Ctrl-h>":"TabLeft",

"<Enter>": "Enter",
},
"Explorer": {
"<q>": "Quit",
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ chrono = "0.4.38"
toml = "0.8.19"
winnow = "0.6.18"
tui-widget-list = "0.12.1"
tui-input = "0.10.1"

[build-dependencies]
anyhow = "1.0.86"
Expand Down
4 changes: 3 additions & 1 deletion src/action.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crossterm::event::KeyEvent;
use serde::{Deserialize, Serialize};
use strum::Display;

Expand All @@ -12,6 +13,7 @@ pub enum Action {
ClearScreen,
Error(String),
Help,
Key(KeyEvent),
Up,
Down,
Left,
Expand All @@ -21,5 +23,5 @@ pub enum Action {
TabRight,
TabLeft,
FocusExplorer,
FocusTags,
FocusFilter,
}
18 changes: 13 additions & 5 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use tracing::{debug, info};

use crate::{
action::Action,
components::{explorer::Explorer, fps::FpsCounter, home::Home, tags::Tags, Component},
components::{
explorer_tab::ExplorerTab, filter_tab::FilterTab, fps::FpsCounter, home::Home, Component,
},
config::Config,
tui::{Event, Tui},
};
Expand All @@ -30,7 +32,7 @@ pub enum Mode {
#[default]
Home,
Explorer,
Tags,
Filter,
}

impl App {
Expand All @@ -42,8 +44,8 @@ impl App {
components: vec![
Box::new(Home::new()),
Box::<FpsCounter>::default(),
Box::new(Explorer::new()),
Box::new(Tags::new()),
Box::new(ExplorerTab::new()),
Box::new(FilterTab::new()),
],
should_quit: false,
should_suspend: false,
Expand Down Expand Up @@ -117,6 +119,12 @@ impl App {
let Some(keymap) = self.config.keybindings.get(&self.mode) else {
return Ok(());
};

if self.components.iter().any(|c| c.editing_mode()) {
action_tx.send(Action::Key(key))?;
return Ok(());
}

if let Some(action) = keymap.get(&vec![key]) {
action_tx.send(action.clone())?;
} else {
Expand All @@ -140,7 +148,7 @@ impl App {
}
match action {
Action::FocusExplorer => self.mode = Mode::Explorer,
Action::FocusTags => self.mode = Mode::Tags,
Action::FocusFilter => self.mode = Mode::Filter,
Action::Tick => {
self.last_tick_key_events.drain(..);
}
Expand Down
12 changes: 10 additions & 2 deletions src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use tokio::sync::mpsc::UnboundedSender;

use crate::{action::Action, config::Config, tui::Event};

pub mod explorer;
pub mod explorer_tab;
pub mod filter_tab;
pub mod fps;
pub mod home;
pub mod tags;

/// `Component` is a trait that represents a visual and interactive element of the user interface.
///
Expand Down Expand Up @@ -124,4 +124,12 @@ pub trait Component {
///
/// * `Result<()>` - An Ok result or an error.
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()>;
/// Whether the app should send Actions or `Action::Key`
///
/// # Returns
///
/// * `bool` - Whether the component is in editing mode or not.
fn editing_mode(&self) -> bool {
false
}
}
48 changes: 30 additions & 18 deletions src/components/explorer.rs → src/components/explorer_tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::widgets::task_list::TaskList;
use crate::{action::Action, config::Config};

#[derive(Default)]
pub struct Explorer {
pub struct ExplorerTab {
command_tx: Option<UnboundedSender<Action>>,
config: Config,
focused: bool,
Expand All @@ -27,7 +27,7 @@ pub struct Explorer {
entries_right_view: Vec<VaultData>,
}

impl Explorer {
impl ExplorerTab {
pub fn new() -> Self {
Self::default()
}
Expand Down Expand Up @@ -155,9 +155,14 @@ impl Explorer {
.map(|item| format!("{} {}", item.0, item.1))
.collect()
}
pub fn render_footer(area: Rect, frame: &mut Frame) {
Line::raw("Press hjkl|◄▼▲▶ to move")
.centered()
.render(area, frame.buffer_mut());
}
}

impl Component for Explorer {
impl Component for ExplorerTab {
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
self.command_tx = Some(tx);
Ok(())
Expand All @@ -172,21 +177,24 @@ impl Component for Explorer {
}

fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::FocusExplorer => self.focused = true,
Action::FocusTags => self.focused = false,
Action::Up => {
self.state_center_view.previous();
self.update_preview();
}
Action::Down => {
self.state_center_view.next();
self.update_preview();
if self.focused {
match action {
Action::FocusFilter => self.focused = false,
Action::Up => {
self.state_center_view.previous();
self.update_preview();
}
Action::Down => {
self.state_center_view.next();
self.update_preview();
}
Action::Right | Action::Enter => self.enter_selected_entry()?,
Action::Left | Action::Cancel => self.leave_selected_entry()?,
Action::Help => todo!(),
_ => (),
}
Action::Right | Action::Enter => self.enter_selected_entry()?,
Action::Left | Action::Cancel => self.leave_selected_entry()?,
Action::Help => todo!(),
_ => (),
} else if action == Action::FocusExplorer {
self.focused = true;
}
Ok(None)
}
Expand All @@ -205,8 +213,12 @@ impl Component for Explorer {
Constraint::Length(1),
Constraint::Min(0),
Constraint::Length(1),
Constraint::Length(1),
]);
let [_header_area, inner_area, _footer_areaa] = vertical.areas(frame.area());
let [_header_area, inner_area, footer_area, _tab_footer_areaa] =
vertical.areas(frame.area());

Self::render_footer(footer_area, frame);

// Outer Layout : path on top, main layout on bottom
let outer_layout = Layout::default()
Expand Down
Loading

0 comments on commit 825523a

Please sign in to comment.