Skip to content

Commit

Permalink
feat: better file hover state (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
sxyazi authored Oct 14, 2023
1 parent 0b09018 commit b840dcc
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 113 deletions.
10 changes: 4 additions & 6 deletions app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,10 @@ impl App {
manager.refresh();
}
Event::Files(op) => {
let calc = matches!(op, FilesOp::Full(..) | FilesOp::Part(..));
let calc = !matches!(op, FilesOp::Size(..) | FilesOp::IOErr(_));
let b = match op {
FilesOp::Full(..) => manager.update_read(op),
FilesOp::Part(..) => manager.update_read(op),
FilesOp::Size(..) => manager.update_read(op),
FilesOp::IOErr(..) => manager.update_ioerr(op),
_ => manager.update_read(op),
};
if b {
emit!(Render);
Expand All @@ -156,8 +154,8 @@ impl App {
emit!(Peek);
}
}
Event::Hover(file) => {
if manager.update_hover(file) {
Event::Hover(url) => {
if manager.current_mut().repos(url) {
emit!(Render);
}
emit!(Peek);
Expand Down
4 changes: 2 additions & 2 deletions core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crossterm::event::KeyEvent;
use shared::{InputError, RoCell, Url};
use tokio::sync::{mpsc::{self, UnboundedSender}, oneshot};

use super::{files::{File, FilesOp}, input::InputOpt, select::SelectOpt};
use super::{files::FilesOp, input::InputOpt, select::SelectOpt};
use crate::{manager::PreviewLock, tasks::TasksProgress};

static TX: RoCell<UnboundedSender<Event>> = RoCell::new();
Expand All @@ -26,7 +26,7 @@ pub enum Event {
Files(FilesOp),
Pages(usize),
Mimetype(BTreeMap<Url, String>),
Hover(Option<File>),
Hover(Option<Url>),
Peek(Option<(usize, Url)>),
Preview(PreviewLock),

Expand Down
9 changes: 8 additions & 1 deletion core/src/files/file.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{borrow::Cow, ffi::OsStr, fs::Metadata};
use std::{borrow::Cow, collections::BTreeMap, ffi::OsStr, fs::Metadata};

use anyhow::Result;
use shared::Url;
Expand Down Expand Up @@ -34,6 +34,13 @@ impl File {
let is_hidden = url.file_name().map(|s| s.to_string_lossy().starts_with('.')).unwrap_or(false);
Self { url, meta, length, link_to, is_link, is_hidden }
}

#[inline]
pub fn into_map(self) -> BTreeMap<Url, File> {
let mut map = BTreeMap::new();
map.insert(self.url.clone(), self);
map
}
}

impl File {
Expand Down
78 changes: 78 additions & 0 deletions core/src/files/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,84 @@ impl Files {
}
true
}

pub fn update_creating(&mut self, mut todo: BTreeMap<Url, File>) -> bool {
if !self.show_hidden {
todo.retain(|_, f| !f.is_hidden);
}

let b = self.update_replacing(&mut todo);
if todo.is_empty() {
return b;
}

self.items.extend(todo.into_values());
self.sorter.sort(&mut self.items, &self.sizes);
self.version += 1;
true
}

pub fn update_deleting(&mut self, mut todo: BTreeSet<Url>) -> bool {
let mut removed = Vec::with_capacity(todo.len());
macro_rules! go {
($name:expr) => {
removed.clear();
for i in 0..$name.len() {
if todo.remove(&$name[i].url) {
removed.push(i);
if todo.is_empty() {
break;
}
}
}
for i in (0..removed.len()).rev() {
$name.remove(removed[i]);
}
};
}

let mut b = false;
if !todo.is_empty() {
go!(self.items);
b |= !removed.is_empty();
}

if !todo.is_empty() {
go!(self.hidden);
b |= !removed.is_empty();
}
b
}

pub fn update_replacing(&mut self, todo: &mut BTreeMap<Url, File>) -> bool {
if todo.is_empty() {
return false;
}

macro_rules! go {
($name:expr) => {
for i in 0..$name.len() {
if let Some(f) = todo.remove(&$name[i].url) {
$name[i] = f;
if todo.is_empty() {
self.version += 1;
return true;
}
}
}
};
}

let old = todo.len();
go!(self.items);
go!(self.hidden);

if old != todo.len() {
self.version += 1;
return true;
}
false
}
}

impl Files {
Expand Down
10 changes: 9 additions & 1 deletion core/src/files/op.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::BTreeMap, sync::atomic::{AtomicU64, Ordering}};
use std::{collections::{BTreeMap, BTreeSet}, sync::atomic::{AtomicU64, Ordering}};

use shared::Url;

Expand All @@ -13,6 +13,10 @@ pub enum FilesOp {
Part(Url, u64, Vec<File>),
Size(Url, BTreeMap<Url, u64>),
IOErr(Url),

Creating(Url, BTreeMap<Url, File>),
Deleting(Url, BTreeSet<Url>),
Replacing(Url, BTreeMap<Url, File>),
}

impl FilesOp {
Expand All @@ -23,6 +27,10 @@ impl FilesOp {
Self::Part(url, ..) => url,
Self::Size(url, _) => url,
Self::IOErr(url) => url,

Self::Creating(url, _) => url,
Self::Deleting(url, _) => url,
Self::Replacing(url, _) => url,
}
}

Expand Down
80 changes: 35 additions & 45 deletions core/src/manager/folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ pub struct Folder {
pub cwd: Url,
pub files: Files,

offset: usize,
cursor: usize,
pub offset: usize,
pub cursor: usize,

pub page: usize,
pub hovered: Option<File>,
pub page: usize,
}

impl From<Url> for Folder {
Expand All @@ -30,19 +29,23 @@ impl Folder {
FilesOp::Full(_, items) => self.files.update_full(items),
FilesOp::Part(_, ticket, items) => self.files.update_part(ticket, items),
FilesOp::Size(_, items) => self.files.update_size(items),

FilesOp::Creating(_, items) => self.files.update_creating(items),
FilesOp::Deleting(_, items) => self.files.update_deleting(items),
FilesOp::Replacing(_, mut items) => self.files.update_replacing(&mut items),
_ => unreachable!(),
};
if !b {
return false;
}

let max = self.files.len().saturating_sub(1);
self.offset = self.offset.min(max);
self.cursor = self.cursor.min(max);
self.set_page(true);
let old = self.page;
self.prev(Default::default());

if self.page == old {
emit!(Pages(self.page)); // Force update
}

self.hover_repos();
self.hovered = self.files.duplicate(self.cursor);
true
}

Expand All @@ -59,35 +62,34 @@ impl Folder {
}

pub fn next(&mut self, step: Step) -> bool {
let old = (self.cursor, self.offset);
let len = self.files.len();
if len == 0 {
return false;
}

let old = self.cursor;
let limit = MANAGER.layout.folder_height();
self.cursor = step.add(self.cursor, || limit).min(len - 1);
self.hovered = self.files.duplicate(self.cursor);
self.set_page(false);

if self.cursor >= (self.offset + limit).min(len).saturating_sub(5) {
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
}
self.cursor = step.add(self.cursor, || limit).min(len.saturating_sub(1));
self.offset = if self.cursor >= (self.offset + limit).min(len).saturating_sub(5) {
len.saturating_sub(limit).min(self.offset + self.cursor - old.0)
} else {
self.offset.min(len.saturating_sub(1))
};

old != self.cursor
self.set_page(false);
old != (self.cursor, self.offset)
}

pub fn prev(&mut self, step: Step) -> bool {
let old = self.cursor;
self.cursor = step.add(self.cursor, || MANAGER.layout.folder_height());
self.hovered = self.files.duplicate(self.cursor);
self.set_page(false);
let old = (self.cursor, self.offset);
let max = self.files.len().saturating_sub(1);

if self.cursor < self.offset + 5 {
self.offset = self.offset.saturating_sub(old - self.cursor);
}
self.cursor = step.add(self.cursor, || MANAGER.layout.folder_height()).min(max);
self.offset = if self.cursor < self.offset + 5 {
self.offset.saturating_sub(old.0 - self.cursor)
} else {
self.offset.min(max)
};

old != self.cursor
self.set_page(false);
old != (self.cursor, self.offset)
}

pub fn hover(&mut self, url: &Url) -> bool {
Expand All @@ -100,26 +102,14 @@ impl Folder {
}

#[inline]
pub fn hover_repos(&mut self) -> bool {
self.hover(&self.hovered.as_ref().map(|h| h.url_owned()).unwrap_or_default())
}

pub fn hover_force(&mut self, file: File) -> bool {
if self.hover(file.url()) {
return true;
}

self.hovered = Some(file);
false
pub fn repos(&mut self, url: Option<impl AsRef<Url>>) -> bool {
if let Some(u) = url { self.hover(u.as_ref()) } else { self.prev(Default::default()) }
}
}

impl Folder {
#[inline]
pub fn offset(&self) -> usize { self.offset }

#[inline]
pub fn cursor(&self) -> usize { self.cursor }
pub fn hovered(&self) -> Option<&File> { self.files.get(self.cursor) }

pub fn paginate(&self) -> &[File] {
let len = self.files.len();
Expand Down
Loading

0 comments on commit b840dcc

Please sign in to comment.