Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: better file hover state #269

Merged
merged 1 commit into from
Oct 14, 2023
Merged
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
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