mirror of
https://github.com/bobwen-dev/hunter
synced 2025-04-12 00:55:41 +02:00
load metadata and file-count asynchronously
This commit is contained in:
parent
fd366a26dc
commit
3888f49aeb
71
src/dirty.rs
71
src/dirty.rs
@ -1,20 +1,36 @@
|
|||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||||
pub struct DirtyBit(bool);
|
pub struct DirtyBit(bool);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AsyncDirtyBit(pub Arc<RwLock<DirtyBit>>);
|
||||||
|
|
||||||
|
impl PartialEq for AsyncDirtyBit {
|
||||||
|
fn eq(&self, other: &AsyncDirtyBit) -> bool {
|
||||||
|
*self.0.read().unwrap() == *other.0.read().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for AsyncDirtyBit {}
|
||||||
|
|
||||||
|
impl Hash for AsyncDirtyBit {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.read().unwrap().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for AsyncDirtyBit {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
AsyncDirtyBit(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Dirtyable {
|
pub trait Dirtyable {
|
||||||
fn get_bit(&self) -> &DirtyBit;
|
fn is_dirty(&self) -> bool;
|
||||||
fn get_bit_mut(&mut self) -> &mut DirtyBit;
|
fn set_dirty(&mut self);
|
||||||
|
fn set_clean(&mut self);
|
||||||
fn is_dirty(&self) -> bool {
|
|
||||||
self.get_bit().0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_dirty(&mut self) {
|
|
||||||
self.get_bit_mut().0 = true;
|
|
||||||
}
|
|
||||||
fn set_clean(&mut self) {
|
|
||||||
self.get_bit_mut().0 = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -24,12 +40,33 @@ impl DirtyBit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsyncDirtyBit {
|
||||||
|
pub fn new() -> AsyncDirtyBit {
|
||||||
|
AsyncDirtyBit(Arc::new(RwLock::new(DirtyBit::new())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Dirtyable for DirtyBit {
|
impl Dirtyable for DirtyBit {
|
||||||
fn get_bit(&self) -> &DirtyBit {
|
fn is_dirty(&self) -> bool {
|
||||||
self
|
self.0
|
||||||
}
|
}
|
||||||
fn get_bit_mut(&mut self) -> &mut DirtyBit {
|
fn set_dirty(&mut self) {
|
||||||
self
|
self.0 = true;
|
||||||
|
}
|
||||||
|
fn set_clean(&mut self) {
|
||||||
|
self.0 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dirtyable for AsyncDirtyBit {
|
||||||
|
fn is_dirty(&self) -> bool {
|
||||||
|
self.0.read().unwrap().is_dirty()
|
||||||
|
}
|
||||||
|
fn set_dirty(&mut self) {
|
||||||
|
self.0.write().unwrap().set_dirty();
|
||||||
|
}
|
||||||
|
fn set_clean(&mut self) {
|
||||||
|
self.0.write().unwrap().set_clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
86
src/fail.rs
86
src/fail.rs
@ -11,42 +11,44 @@ use crate::foldview::LogEntry;
|
|||||||
|
|
||||||
pub type HResult<T> = Result<T, HError>;
|
pub type HResult<T> = Result<T, HError>;
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
#[derive(Fail, Debug, Clone)]
|
||||||
pub enum HError {
|
pub enum HError {
|
||||||
#[fail(display = "IO error: {} ", error)]
|
#[fail(display = "IO error: {} ", _0)]
|
||||||
IoError{#[cause] error: std::io::Error, backtrace: Backtrace},
|
IoError(String),
|
||||||
#[fail(display = "Mutex failed")]
|
#[fail(display = "Mutex failed")]
|
||||||
MutexError(Backtrace),
|
MutexError,
|
||||||
#[fail(display = "Can't lock!")]
|
#[fail(display = "Can't lock!")]
|
||||||
TryLockError(Backtrace),
|
TryLockError,
|
||||||
#[fail(display = "Channel failed: {}", error)]
|
#[fail(display = "Channel failed: {}", error)]
|
||||||
ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError},
|
ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError},
|
||||||
#[fail(display = "Channel failed: {}", error)]
|
#[fail(display = "Channel failed: {}", error)]
|
||||||
ChannelRecvError{#[cause] error: std::sync::mpsc::RecvError},
|
ChannelRecvError{#[cause] error: std::sync::mpsc::RecvError},
|
||||||
#[fail(display = "Channel failed")]
|
#[fail(display = "Channel failed")]
|
||||||
ChannelSendError(Backtrace),
|
ChannelSendError,
|
||||||
#[fail(display = "Previewer failed on file: {}", file)]
|
#[fail(display = "Previewer failed on file: {}", file)]
|
||||||
PreviewFailed{file: String, backtrace: Backtrace},
|
PreviewFailed{file: String},
|
||||||
#[fail(display = "StalePreviewer for file: {}", file)]
|
#[fail(display = "StalePreviewer for file: {}", file)]
|
||||||
StalePreviewError{file: String},
|
StalePreviewError{file: String},
|
||||||
#[fail(display = "Accessed stale value")]
|
#[fail(display = "Accessed stale value")]
|
||||||
StaleError(Backtrace),
|
StaleError,
|
||||||
#[fail(display = "Failed: {}", error)]
|
#[fail(display = "Failed: {}", _0)]
|
||||||
Error{#[cause] error: failure::Error , backtrace: Backtrace},
|
Error(String),
|
||||||
#[fail(display = "Was None!")]
|
#[fail(display = "Was None!")]
|
||||||
NoneError(Backtrace),
|
NoneError,
|
||||||
#[fail(display = "Not ready yet!")]
|
#[fail(display = "Not ready yet!")]
|
||||||
WillBeNotReady(Backtrace),
|
WillBeNotReady,
|
||||||
#[fail(display = "Not ready yet!")]
|
#[fail(display = "Not ready yet!")]
|
||||||
AsyncNotReadyError(Backtrace),
|
AsyncNotReadyError,
|
||||||
|
#[fail(display = "Async is stale!")]
|
||||||
|
AsyncStaleError,
|
||||||
#[fail(display = "Value has already been taken!")]
|
#[fail(display = "Value has already been taken!")]
|
||||||
AsyncAlreadyTakenError(Backtrace),
|
AsyncAlreadyTakenError,
|
||||||
#[fail(display = "Async Error: {}", _0)]
|
#[fail(display = "Async Error: {}", _0)]
|
||||||
AsyncError(String),
|
AsyncError(String),
|
||||||
#[fail(display = "No widget found")]
|
#[fail(display = "No widget found")]
|
||||||
NoWidgetError(Backtrace),
|
NoWidgetError,
|
||||||
#[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)]
|
#[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)]
|
||||||
WrongDirectoryError{ path: PathBuf, dir: PathBuf , backtrace: Backtrace},
|
WrongDirectoryError{ path: PathBuf, dir: PathBuf},
|
||||||
#[fail(display = "Widget finnished")]
|
#[fail(display = "Widget finnished")]
|
||||||
PopupFinnished,
|
PopupFinnished,
|
||||||
#[fail(display = "No completions found")]
|
#[fail(display = "No completions found")]
|
||||||
@ -54,7 +56,7 @@ pub enum HError {
|
|||||||
#[fail(display = "No more history")]
|
#[fail(display = "No more history")]
|
||||||
NoHistoryError,
|
NoHistoryError,
|
||||||
#[fail(display = "No core for widget")]
|
#[fail(display = "No core for widget")]
|
||||||
NoWidgetCoreError(Backtrace),
|
NoWidgetCoreError,
|
||||||
#[fail(display = "No header for widget")]
|
#[fail(display = "No header for widget")]
|
||||||
NoHeaderError,
|
NoHeaderError,
|
||||||
#[fail(display = "You wanted this!")]
|
#[fail(display = "You wanted this!")]
|
||||||
@ -62,11 +64,11 @@ pub enum HError {
|
|||||||
#[fail(display = "HBox ratio mismatch: {} widgets, ratio is {:?}", wnum, ratio)]
|
#[fail(display = "HBox ratio mismatch: {} widgets, ratio is {:?}", wnum, ratio)]
|
||||||
HBoxWrongRatioError{ wnum: usize, ratio: Vec<usize> },
|
HBoxWrongRatioError{ wnum: usize, ratio: Vec<usize> },
|
||||||
#[fail(display = "Got wrong widget: {}! Wanted: {}", got, wanted)]
|
#[fail(display = "Got wrong widget: {}! Wanted: {}", got, wanted)]
|
||||||
WrongWidgetError{got: String, wanted: String, backtrace: Backtrace},
|
WrongWidgetError{got: String, wanted: String},
|
||||||
#[fail(display = "Strip Prefix Error: {}", error)]
|
#[fail(display = "Strip Prefix Error: {}", error)]
|
||||||
StripPrefixError{#[cause] error: std::path::StripPrefixError, backtrace: Backtrace},
|
StripPrefixError{#[cause] error: std::path::StripPrefixError},
|
||||||
#[fail(display = "INofify failed: {}", error)]
|
#[fail(display = "INofify failed: {}", _0)]
|
||||||
INotifyError{#[cause] error: notify::Error, backtrace: Backtrace},
|
INotifyError(String),
|
||||||
#[fail(display = "Tags not loaded yet")]
|
#[fail(display = "Tags not loaded yet")]
|
||||||
TagsNotLoadedYetError,
|
TagsNotLoadedYetError,
|
||||||
#[fail(display = "Input cancelled!")]
|
#[fail(display = "Input cancelled!")]
|
||||||
@ -79,6 +81,8 @@ pub enum HError {
|
|||||||
TerminalResizedError,
|
TerminalResizedError,
|
||||||
#[fail(display = "{}", _0)]
|
#[fail(display = "{}", _0)]
|
||||||
Log(String),
|
Log(String),
|
||||||
|
#[fail(display = "Metadata already processed")]
|
||||||
|
MetadataProcessedError
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HError {
|
impl HError {
|
||||||
@ -92,12 +96,12 @@ impl HError {
|
|||||||
Err(HError::HBoxWrongRatioError{ wnum: wnum, ratio: ratio })
|
Err(HError::HBoxWrongRatioError{ wnum: wnum, ratio: ratio })
|
||||||
}
|
}
|
||||||
pub fn no_widget<T>() -> HResult<T> {
|
pub fn no_widget<T>() -> HResult<T> {
|
||||||
Err(HError::NoWidgetError(Backtrace::new()))
|
Err(HError::NoWidgetError)
|
||||||
}
|
}
|
||||||
pub fn wrong_widget<T>(got: &str, wanted: &str) -> HResult<T> {
|
pub fn wrong_widget<T>(got: &str, wanted: &str) -> HResult<T> {
|
||||||
Err(HError::WrongWidgetError{ got: got.to_string(),
|
Err(HError::WrongWidgetError{ got: got.to_string(),
|
||||||
wanted: wanted.to_string(),
|
wanted: wanted.to_string() })
|
||||||
backtrace: Backtrace::new()})
|
|
||||||
}
|
}
|
||||||
pub fn popup_finnished<T>() -> HResult<T> {
|
pub fn popup_finnished<T>() -> HResult<T> {
|
||||||
Err(HError::PopupFinnished)
|
Err(HError::PopupFinnished)
|
||||||
@ -116,13 +120,13 @@ impl HError {
|
|||||||
}
|
}
|
||||||
pub fn wrong_directory<T>(path: PathBuf, dir: PathBuf) -> HResult<T> {
|
pub fn wrong_directory<T>(path: PathBuf, dir: PathBuf) -> HResult<T> {
|
||||||
Err(HError::WrongDirectoryError{ path: path,
|
Err(HError::WrongDirectoryError{ path: path,
|
||||||
dir: dir,
|
dir: dir })
|
||||||
backtrace: Backtrace::new() })
|
|
||||||
}
|
}
|
||||||
pub fn preview_failed<T>(file: &crate::files::File) -> HResult<T> {
|
pub fn preview_failed<T>(file: &crate::files::File) -> HResult<T> {
|
||||||
let name = file.name.clone();
|
let name = file.name.clone();
|
||||||
Err(HError::PreviewFailed{ file: name,
|
Err(HError::PreviewFailed{ file: name })
|
||||||
backtrace: Backtrace::new() })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminal_resized<T>() -> HResult<T> {
|
pub fn terminal_resized<T>() -> HResult<T> {
|
||||||
@ -130,20 +134,24 @@ impl HError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn stale<T>() -> HResult<T> {
|
pub fn stale<T>() -> HResult<T> {
|
||||||
Err(HError::StaleError(Backtrace::new()))
|
Err(HError::StaleError)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn async_not_ready<T>() -> HResult<T> {
|
pub fn async_not_ready<T>() -> HResult<T> {
|
||||||
Err(HError::AsyncNotReadyError(Backtrace::new()))
|
Err(HError::AsyncNotReadyError)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn async_taken<T>() -> HResult<T> {
|
pub fn async_taken<T>() -> HResult<T> {
|
||||||
Err(HError::AsyncAlreadyTakenError(Backtrace::new()))
|
Err(HError::AsyncAlreadyTakenError)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn async_error<T>(error: &HError) -> HResult<T> {
|
pub fn async_error<T>(error: &HError) -> HResult<T> {
|
||||||
Err(HError::AsyncError(format!("{}", error)))
|
Err(HError::AsyncError(format!("{}", error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn metadata_processed<T>() -> HResult<T> {
|
||||||
|
Err(HError::MetadataProcessedError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -195,7 +203,7 @@ impl<T> ErrorLog for HResult<T> {
|
|||||||
impl From<std::io::Error> for HError {
|
impl From<std::io::Error> for HError {
|
||||||
fn from(error: std::io::Error) -> Self {
|
fn from(error: std::io::Error) -> Self {
|
||||||
// dbg!(&error);
|
// dbg!(&error);
|
||||||
let err = HError::IoError { error: error, backtrace: Backtrace::new() };
|
let err = HError::IoError(format!("{}", error));
|
||||||
put_log(&err).ok();
|
put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
@ -204,7 +212,7 @@ impl From<std::io::Error> for HError {
|
|||||||
impl From<failure::Error> for HError {
|
impl From<failure::Error> for HError {
|
||||||
fn from(error: failure::Error) -> Self {
|
fn from(error: failure::Error) -> Self {
|
||||||
// dbg!(&error);
|
// dbg!(&error);
|
||||||
let err = HError::Error { error: error, backtrace: Backtrace::new() };
|
let err = HError::Error(format!("{}", error));
|
||||||
put_log(&err).ok();
|
put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
@ -231,7 +239,7 @@ impl From<std::sync::mpsc::RecvError> for HError {
|
|||||||
impl<T> From<std::sync::mpsc::SendError<T>> for HError {
|
impl<T> From<std::sync::mpsc::SendError<T>> for HError {
|
||||||
fn from(error: std::sync::mpsc::SendError<T>) -> Self {
|
fn from(error: std::sync::mpsc::SendError<T>) -> Self {
|
||||||
dbg!(&error);
|
dbg!(&error);
|
||||||
let err = HError::ChannelSendError(Backtrace::new());
|
let err = HError::ChannelSendError;
|
||||||
put_log(&err).ok();
|
put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
@ -240,7 +248,7 @@ impl<T> From<std::sync::mpsc::SendError<T>> for HError {
|
|||||||
impl<T> From<std::sync::PoisonError<T>> for HError {
|
impl<T> From<std::sync::PoisonError<T>> for HError {
|
||||||
fn from(_: std::sync::PoisonError<T>) -> Self {
|
fn from(_: std::sync::PoisonError<T>) -> Self {
|
||||||
// dbg!("Poisoned Mutex");
|
// dbg!("Poisoned Mutex");
|
||||||
let err = HError::MutexError(Backtrace::new());
|
let err = HError::MutexError;
|
||||||
put_log(&err).ok();
|
put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
@ -249,7 +257,7 @@ impl<T> From<std::sync::PoisonError<T>> for HError {
|
|||||||
impl<T> From<std::sync::TryLockError<T>> for HError {
|
impl<T> From<std::sync::TryLockError<T>> for HError {
|
||||||
fn from(error: std::sync::TryLockError<T>) -> Self {
|
fn from(error: std::sync::TryLockError<T>) -> Self {
|
||||||
// dbg!(&error);
|
// dbg!(&error);
|
||||||
let err = HError::TryLockError(Backtrace::new());
|
let err = HError::TryLockError;
|
||||||
put_log(&err).ok();
|
put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
@ -258,7 +266,7 @@ impl<T> From<std::sync::TryLockError<T>> for HError {
|
|||||||
impl From<std::option::NoneError> for HError {
|
impl From<std::option::NoneError> for HError {
|
||||||
fn from(error: std::option::NoneError) -> Self {
|
fn from(error: std::option::NoneError) -> Self {
|
||||||
//dbg!(&error);
|
//dbg!(&error);
|
||||||
let err = HError::NoneError(Backtrace::new());
|
let err = HError::NoneError;
|
||||||
//put_log(&err).ok();
|
//put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
@ -267,7 +275,7 @@ impl From<std::option::NoneError> for HError {
|
|||||||
impl From<std::path::StripPrefixError> for HError {
|
impl From<std::path::StripPrefixError> for HError {
|
||||||
fn from(error: std::path::StripPrefixError) -> Self {
|
fn from(error: std::path::StripPrefixError) -> Self {
|
||||||
// dbg!(&error);
|
// dbg!(&error);
|
||||||
let err = HError::StripPrefixError{error: error, backtrace: Backtrace::new() };
|
let err = HError::StripPrefixError{error: error };
|
||||||
put_log(&err).ok();
|
put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
@ -276,7 +284,7 @@ impl From<std::path::StripPrefixError> for HError {
|
|||||||
impl From<notify::Error> for HError {
|
impl From<notify::Error> for HError {
|
||||||
fn from(error: notify::Error) -> Self {
|
fn from(error: notify::Error) -> Self {
|
||||||
// dbg!(&error);
|
// dbg!(&error);
|
||||||
let err = HError::INotifyError{error: error, backtrace: Backtrace::new() };
|
let err = HError::INotifyError(format!("{}", error));
|
||||||
put_log(&err).ok();
|
put_log(&err).ok();
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
@ -194,6 +194,7 @@ impl FileBrowser {
|
|||||||
let mut list = ListView::new(&core_m,
|
let mut list = ListView::new(&core_m,
|
||||||
Files::new_from_path(&main_path)?);
|
Files::new_from_path(&main_path)?);
|
||||||
list.animate_slide_up().log();
|
list.animate_slide_up().log();
|
||||||
|
list.content.meta_all();
|
||||||
Ok(list)
|
Ok(list)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -216,7 +217,7 @@ impl FileBrowser {
|
|||||||
columns.refresh().log();
|
columns.refresh().log();
|
||||||
|
|
||||||
|
|
||||||
let cwd = File::new_from_path(&cwd).unwrap();
|
let cwd = File::new_from_path(&cwd, None).unwrap();
|
||||||
let dir_events = Arc::new(Mutex::new(vec![]));
|
let dir_events = Arc::new(Mutex::new(vec![]));
|
||||||
|
|
||||||
let (tx_watch, rx_watch) = channel();
|
let (tx_watch, rx_watch) = channel();
|
||||||
@ -323,6 +324,7 @@ impl FileBrowser {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut list = ListView::new(&core, files);
|
let mut list = ListView::new(&core, files);
|
||||||
|
list.content.meta_all();
|
||||||
|
|
||||||
if let Some(file) = &selected_file {
|
if let Some(file) = &selected_file {
|
||||||
list.select_file(file);
|
list.select_file(file);
|
||||||
@ -395,7 +397,7 @@ impl FileBrowser {
|
|||||||
|
|
||||||
pub fn goto_bookmark(&mut self) -> HResult<()> {
|
pub fn goto_bookmark(&mut self) -> HResult<()> {
|
||||||
let path = self.get_boomark()?;
|
let path = self.get_boomark()?;
|
||||||
let path = File::new_from_path(&PathBuf::from(path))?;
|
let path = File::new_from_path(&PathBuf::from(path), None)?;
|
||||||
self.main_widget_goto(&path)?;
|
self.main_widget_goto(&path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -664,7 +666,7 @@ impl FileBrowser {
|
|||||||
match dir {
|
match dir {
|
||||||
Ok(dir) => {
|
Ok(dir) => {
|
||||||
self.columns.widgets.clear();
|
self.columns.widgets.clear();
|
||||||
let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?;
|
let cwd = File::new_from_path(&std::path::PathBuf::from(&dir), None)?;
|
||||||
self.cwd = cwd;
|
self.cwd = cwd;
|
||||||
let dir = std::path::PathBuf::from(&dir);
|
let dir = std::path::PathBuf::from(&dir);
|
||||||
let left_dir = std::path::PathBuf::from(&dir);
|
let left_dir = std::path::PathBuf::from(&dir);
|
||||||
@ -752,13 +754,14 @@ impl FileBrowser {
|
|||||||
pub fn get_footer(&self) -> HResult<String> {
|
pub fn get_footer(&self) -> HResult<String> {
|
||||||
let xsize = self.get_coordinates()?.xsize();
|
let xsize = self.get_coordinates()?.xsize();
|
||||||
let ypos = self.get_coordinates()?.position().y();
|
let ypos = self.get_coordinates()?.position().y();
|
||||||
let file = self.selected_file()?;
|
let pos = self.main_widget()?.get_selection();
|
||||||
|
let file = self.main_widget()?.content.files.get(pos)?;
|
||||||
|
|
||||||
let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".into());
|
let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".into());
|
||||||
let user = file.pretty_user().unwrap_or("NOUSER".into());
|
let user = file.pretty_user().unwrap_or("NOUSER".into());
|
||||||
let group = file.pretty_group().unwrap_or("NOGROUP".into());
|
let group = file.pretty_group().unwrap_or("NOGROUP".into());
|
||||||
let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into());
|
let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into());
|
||||||
let target = if let Some(target) = file.target {
|
let target = if let Some(target) = &file.target {
|
||||||
"--> ".to_string() + &target.short_string()
|
"--> ".to_string() + &target.short_string()
|
||||||
} else { "".to_string() };
|
} else { "".to_string() };
|
||||||
|
|
||||||
|
349
src/files.rs
349
src/files.rs
@ -1,8 +1,10 @@
|
|||||||
use std::cmp::{Ord, Ordering};
|
use std::cmp::{Ord, Ordering};
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
use std::fs::Metadata;
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::os::unix::ffi::{OsStringExt, OsStrExt};
|
use std::os::unix::ffi::{OsStringExt, OsStrExt};
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
@ -16,11 +18,13 @@ use users::{get_current_username,
|
|||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use notify::DebouncedEvent;
|
use notify::DebouncedEvent;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
||||||
|
|
||||||
use crate::fail::{HResult, HError};
|
use crate::fail::{HResult, HError, ErrorLog};
|
||||||
use crate::dirty::{DirtyBit, Dirtyable};
|
use crate::dirty::{AsyncDirtyBit, DirtyBit, Dirtyable};
|
||||||
|
use crate::preview::Async;
|
||||||
|
use crate::widget::Events;
|
||||||
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -28,6 +32,23 @@ lazy_static! {
|
|||||||
static ref TAGS: Mutex<(bool, Vec<PathBuf>)> = Mutex::new((false, vec![]));
|
static ref TAGS: Mutex<(bool, Vec<PathBuf>)> = Mutex::new((false, vec![]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_pool(sender: Option<Sender<Events>>) -> ThreadPool {
|
||||||
|
let sender = Arc::new(Mutex::new(sender));
|
||||||
|
ThreadPoolBuilder::new()
|
||||||
|
.num_threads(8)
|
||||||
|
.exit_handler(move |thread_num| {
|
||||||
|
if thread_num == 0 {
|
||||||
|
if let Ok(lock) = sender.lock() {
|
||||||
|
if let Some(sender) = lock.as_ref() {
|
||||||
|
sender.send(Events::WidgetReady).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
.expect("Failed to create thread pool")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_tags() -> HResult<()> {
|
pub fn load_tags() -> HResult<()> {
|
||||||
std::thread::spawn(|| -> HResult<()> {
|
std::thread::spawn(|| -> HResult<()> {
|
||||||
let tag_path = crate::paths::tagfile_path()?;
|
let tag_path = crate::paths::tagfile_path()?;
|
||||||
@ -53,16 +74,19 @@ pub fn tags_loaded() -> HResult<()> {
|
|||||||
else { HError::tags_not_loaded() }
|
else { HError::tags_not_loaded() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||||
pub struct Files {
|
pub struct Files {
|
||||||
pub directory: File,
|
pub directory: File,
|
||||||
pub files: Vec<File>,
|
pub files: Vec<File>,
|
||||||
|
pub meta_upto: Option<usize>,
|
||||||
pub sort: SortBy,
|
pub sort: SortBy,
|
||||||
pub dirs_first: bool,
|
pub dirs_first: bool,
|
||||||
pub reverse: bool,
|
pub reverse: bool,
|
||||||
pub show_hidden: bool,
|
pub show_hidden: bool,
|
||||||
pub filter: Option<String>,
|
pub filter: Option<String>,
|
||||||
pub dirty: DirtyBit
|
pub dirty: DirtyBit,
|
||||||
|
pub dirty_meta: AsyncDirtyBit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for Files {
|
impl Index<usize> for Files {
|
||||||
@ -74,12 +98,16 @@ impl Index<usize> for Files {
|
|||||||
|
|
||||||
|
|
||||||
impl Dirtyable for Files {
|
impl Dirtyable for Files {
|
||||||
fn get_bit(&self) -> &DirtyBit {
|
fn is_dirty(&self) -> bool {
|
||||||
&self.dirty
|
self.dirty.is_dirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bit_mut(&mut self) -> &mut DirtyBit {
|
fn set_dirty(&mut self) {
|
||||||
&mut self.dirty
|
self.dirty.set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_clean(&mut self) {
|
||||||
|
self.dirty.set_clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +115,8 @@ impl Dirtyable for Files {
|
|||||||
impl Files {
|
impl Files {
|
||||||
pub fn new_from_path(path: &Path) -> Result<Files, Error> {
|
pub fn new_from_path(path: &Path) -> Result<Files, Error> {
|
||||||
let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
|
let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
|
||||||
|
let dirty = DirtyBit::new();
|
||||||
|
let dirty_meta = AsyncDirtyBit::new();
|
||||||
|
|
||||||
let files: Vec<_> = direntries?
|
let files: Vec<_> = direntries?
|
||||||
.iter()
|
.iter()
|
||||||
@ -94,23 +124,29 @@ impl Files {
|
|||||||
let name = file.file_name();
|
let name = file.file_name();
|
||||||
let name = name.to_string_lossy();
|
let name = name.to_string_lossy();
|
||||||
let path = file.path();
|
let path = file.path();
|
||||||
File::new(&name, path)
|
File::new(&name,
|
||||||
|
path,
|
||||||
|
Some(dirty_meta.clone()))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut files = Files {
|
let mut files = Files {
|
||||||
directory: File::new_from_path(&path)?,
|
directory: File::new_from_path(&path, None)?,
|
||||||
files: files,
|
files: files,
|
||||||
|
meta_upto: None,
|
||||||
sort: SortBy::Name,
|
sort: SortBy::Name,
|
||||||
dirs_first: true,
|
dirs_first: true,
|
||||||
reverse: false,
|
reverse: false,
|
||||||
show_hidden: true,
|
show_hidden: true,
|
||||||
filter: None,
|
filter: None,
|
||||||
dirty: DirtyBit::new()
|
dirty: dirty,
|
||||||
|
dirty_meta: dirty_meta,
|
||||||
};
|
};
|
||||||
|
|
||||||
files.sort();
|
files.sort();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if files.files.len() == 0 {
|
if files.files.len() == 0 {
|
||||||
files.files = vec![File::new_placeholder(&path)?];
|
files.files = vec![File::new_placeholder(&path)?];
|
||||||
}
|
}
|
||||||
@ -118,8 +154,12 @@ impl Files {
|
|||||||
Ok(files)
|
Ok(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_path_cancellable(path: &Path, stale: Arc<Mutex<bool>>) -> Result<Files, Error> {
|
pub fn new_from_path_cancellable(path: &Path,
|
||||||
|
stale: Arc<Mutex<bool>>)
|
||||||
|
-> Result<Files, Error> {
|
||||||
let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
|
let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
|
||||||
|
let dirty = DirtyBit::new();
|
||||||
|
let dirty_meta = AsyncDirtyBit::new();
|
||||||
|
|
||||||
let files: Vec<_> = direntries?
|
let files: Vec<_> = direntries?
|
||||||
.iter()
|
.iter()
|
||||||
@ -130,7 +170,9 @@ impl Files {
|
|||||||
let name = file.file_name();
|
let name = file.file_name();
|
||||||
let name = name.to_string_lossy();
|
let name = name.to_string_lossy();
|
||||||
let path = file.path();
|
let path = file.path();
|
||||||
Some(File::new(&name, path))
|
Some(File::new(&name,
|
||||||
|
path,
|
||||||
|
Some(dirty_meta.clone())))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fuse()
|
.fuse()
|
||||||
@ -144,14 +186,16 @@ impl Files {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut files = Files {
|
let mut files = Files {
|
||||||
directory: File::new_from_path(&path)?,
|
directory: File::new_from_path(&path, None)?,
|
||||||
files: files,
|
files: files,
|
||||||
|
meta_upto: None,
|
||||||
sort: SortBy::Name,
|
sort: SortBy::Name,
|
||||||
dirs_first: true,
|
dirs_first: true,
|
||||||
reverse: false,
|
reverse: false,
|
||||||
show_hidden: true,
|
show_hidden: true,
|
||||||
filter: None,
|
filter: None,
|
||||||
dirty: DirtyBit::new()
|
dirty: dirty,
|
||||||
|
dirty_meta: dirty_meta,
|
||||||
};
|
};
|
||||||
|
|
||||||
files.sort();
|
files.sort();
|
||||||
@ -163,13 +207,33 @@ impl Files {
|
|||||||
Ok(files)
|
Ok(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_files(&self) -> Vec<&File> {
|
||||||
|
self.files
|
||||||
|
.iter()
|
||||||
|
.filter(|f| !(self.filter.is_some() &&
|
||||||
|
!f.name.contains(self.filter.as_ref().unwrap())))
|
||||||
|
.filter(|f| !(!self.show_hidden && f.name.starts_with(".")))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_files_mut(&mut self) -> Vec<&mut File> {
|
||||||
|
let filter = self.filter.clone();
|
||||||
|
let show_hidden = self.show_hidden;
|
||||||
|
self.files
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|f| !(filter.is_some() &&
|
||||||
|
!f.name.contains(filter.as_ref().unwrap())))
|
||||||
|
.filter(|f| !(!show_hidden && f.name.starts_with(".")))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sort(&mut self) {
|
pub fn sort(&mut self) {
|
||||||
match self.sort {
|
match self.sort {
|
||||||
SortBy::Name => self
|
SortBy::Name => self
|
||||||
.files
|
.files
|
||||||
.sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)),
|
.sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)),
|
||||||
SortBy::Size => {
|
SortBy::Size => {
|
||||||
self.meta_all();
|
self.meta_all_sync();
|
||||||
self.files.sort_by(|a, b| {
|
self.files.sort_by(|a, b| {
|
||||||
if a.meta().unwrap().size() == b.meta().unwrap().size() {
|
if a.meta().unwrap().size() == b.meta().unwrap().size() {
|
||||||
return alphanumeric_sort::compare_str(&b.name, &a.name);
|
return alphanumeric_sort::compare_str(&b.name, &a.name);
|
||||||
@ -178,7 +242,7 @@ impl Files {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
SortBy::MTime => {
|
SortBy::MTime => {
|
||||||
self.meta_all();
|
self.meta_all_sync();
|
||||||
self.files.sort_by(|a, b| {
|
self.files.sort_by(|a, b| {
|
||||||
if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() {
|
if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() {
|
||||||
return alphanumeric_sort::compare_str(&a.name, &b.name);
|
return alphanumeric_sort::compare_str(&a.name, &b.name);
|
||||||
@ -227,25 +291,14 @@ impl Files {
|
|||||||
self.show_hidden = !self.show_hidden
|
self.show_hidden = !self.show_hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload_files(&mut self) {
|
|
||||||
let dir = self.directory.clone();
|
|
||||||
let files = Files::new_from_path(&dir.path()).unwrap();
|
|
||||||
let files = files
|
|
||||||
.files
|
|
||||||
.into_iter()
|
|
||||||
.skip_while(|f| f.name.starts_with(".") && !self.show_hidden )
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.files = files;
|
|
||||||
self.set_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_event(&mut self, event: &DebouncedEvent) -> HResult<()> {
|
pub fn handle_event(&mut self, event: &DebouncedEvent) -> HResult<()> {
|
||||||
match event {
|
match event {
|
||||||
DebouncedEvent::Create(path) => {
|
DebouncedEvent::Create(path) => {
|
||||||
self.path_in_here(&path)?;
|
self.path_in_here(&path)?;
|
||||||
let file = File::new_from_path(&path)?;
|
let file = File::new_from_path(&path,
|
||||||
|
Some(self.dirty_meta.clone()))?;
|
||||||
self.files.push(file);
|
self.files.push(file);
|
||||||
|
self.sort();
|
||||||
},
|
},
|
||||||
DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => {
|
DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => {
|
||||||
self.path_in_here(&path)?;
|
self.path_in_here(&path)?;
|
||||||
@ -262,6 +315,7 @@ impl Files {
|
|||||||
let mut file = self.find_file_with_path(&old_path)?;
|
let mut file = self.find_file_with_path(&old_path)?;
|
||||||
file.name = new_path.file_name()?.to_string_lossy().to_string();
|
file.name = new_path.file_name()?.to_string_lossy().to_string();
|
||||||
file.path = new_path.into();
|
file.path = new_path.into();
|
||||||
|
file.reload_meta()?;
|
||||||
},
|
},
|
||||||
DebouncedEvent::Error(err, path) => {
|
DebouncedEvent::Error(err, path) => {
|
||||||
dbg!(err);
|
dbg!(err);
|
||||||
@ -287,17 +341,58 @@ impl Files {
|
|||||||
self.files.iter_mut().find(|file| file.path == path)
|
self.files.iter_mut().find(|file| file.path == path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn meta_all(&mut self) {
|
pub fn meta_all_sync(&mut self) -> HResult<()> {
|
||||||
let len = self.files.len();
|
for file in self.files.iter_mut() {
|
||||||
self.meta_upto(len);
|
if !file.meta_processed {
|
||||||
|
let path = file.path.clone();
|
||||||
|
file.meta = Async::new(Box::new(move|_| {
|
||||||
|
let meta = std::fs::metadata(&path)?;
|
||||||
|
Ok(meta)
|
||||||
|
}));
|
||||||
|
file.meta.wait()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dirty_meta.set_dirty();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn meta_upto(&mut self, to: usize) {
|
pub fn meta_all(&mut self) {
|
||||||
for file in self.files.iter_mut().take(to) {
|
let len = self.len();
|
||||||
file.get_meta().ok();
|
self.meta_upto(len, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meta_upto(&mut self, to: usize, sender: Option<Sender<Events>>) {
|
||||||
|
let meta_files = if self.meta_upto > Some(to) {
|
||||||
|
self.meta_upto.unwrap()
|
||||||
|
} else {
|
||||||
|
if to > self.len() {
|
||||||
|
self.len()
|
||||||
|
} else {
|
||||||
|
to
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.meta_upto >= Some(meta_files) && !self.dirty_meta.is_dirty() { return }
|
||||||
|
|
||||||
|
self.set_dirty();
|
||||||
|
self.dirty_meta.set_clean();
|
||||||
|
|
||||||
|
let meta_pool = make_pool(sender.clone());
|
||||||
|
let dirsize_pool = make_pool(sender);
|
||||||
|
|
||||||
|
for file in self.files.iter_mut().take(meta_files) {
|
||||||
|
if !file.meta_processed {
|
||||||
|
file.take_meta(&meta_pool).ok();
|
||||||
|
}
|
||||||
|
if file.is_dir() {
|
||||||
|
file.take_dirsize(&dirsize_pool).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.meta_upto = Some(meta_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn set_filter(&mut self, filter: Option<String>) {
|
pub fn set_filter(&mut self, filter: Option<String>) {
|
||||||
self.filter = filter;
|
self.filter = filter;
|
||||||
self.set_dirty();
|
self.set_dirty();
|
||||||
@ -308,15 +403,7 @@ impl Files {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match &self.filter {
|
self.get_files().len()
|
||||||
None => self.files.len(),
|
|
||||||
Some(filter) => {
|
|
||||||
self.files
|
|
||||||
.iter()
|
|
||||||
.filter(|f| f.name.contains(filter))
|
|
||||||
.count()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_selected(&self) -> Vec<&File> {
|
pub fn get_selected(&self) -> Vec<&File> {
|
||||||
@ -370,14 +457,51 @@ impl Hash for File {
|
|||||||
|
|
||||||
impl Eq for File {}
|
impl Eq for File {}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl Clone for File {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let meta = self.meta.value.clone();
|
||||||
|
let meta = match meta {
|
||||||
|
Ok(meta) => Async::new_with_value(meta.clone()),
|
||||||
|
Err(_) => File::make_async_meta(&self.path, self.dirty_meta.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let dirsize = if let Some(ref dirsize) = self.dirsize {
|
||||||
|
let dirsize = dirsize.value.clone();
|
||||||
|
let dirsize = match dirsize {
|
||||||
|
Ok(dirsize) => Async::new_with_value(dirsize),
|
||||||
|
Err(_) => File::make_async_dirsize(&self.path,
|
||||||
|
self.dirty_meta.clone())
|
||||||
|
};
|
||||||
|
Some(dirsize)
|
||||||
|
} else { None };
|
||||||
|
|
||||||
|
File {
|
||||||
|
name: self.name.clone(),
|
||||||
|
path: self.path.clone(),
|
||||||
|
kind: self.kind.clone(),
|
||||||
|
dirsize: dirsize,
|
||||||
|
target: self.target.clone(),
|
||||||
|
color: self.color.clone(),
|
||||||
|
meta: meta,
|
||||||
|
dirty_meta: self.dirty_meta.clone(),
|
||||||
|
meta_processed: self.meta_processed.clone(),
|
||||||
|
selected: self.selected.clone(),
|
||||||
|
tag: self.tag.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub kind: Kind,
|
pub kind: Kind,
|
||||||
|
pub dirsize: Option<Async<usize>>,
|
||||||
pub target: Option<PathBuf>,
|
pub target: Option<PathBuf>,
|
||||||
pub color: Option<lscolors::Color>,
|
pub color: Option<lscolors::Color>,
|
||||||
pub meta: Option<std::fs::Metadata>,
|
pub meta: Async<Metadata>,
|
||||||
|
pub dirty_meta: Option<AsyncDirtyBit>,
|
||||||
|
pub meta_processed: bool,
|
||||||
pub selected: bool,
|
pub selected: bool,
|
||||||
pub tag: Option<bool>
|
pub tag: Option<bool>
|
||||||
// flags: Option<String>,
|
// flags: Option<String>,
|
||||||
@ -387,63 +511,138 @@ impl File {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
name: &str,
|
name: &str,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
) -> File {
|
dirty_meta: Option<AsyncDirtyBit>) -> File {
|
||||||
let tag = check_tag(&path).ok();
|
let tag = check_tag(&path).ok();
|
||||||
|
let meta = File::make_async_meta(&path, dirty_meta.clone());
|
||||||
|
let dirsize = if path.is_dir() {
|
||||||
|
Some(File::make_async_dirsize(&path, dirty_meta.clone()))
|
||||||
|
} else { None };
|
||||||
|
|
||||||
File {
|
File {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
kind: if path.is_dir() { Kind::Directory } else { Kind::File },
|
kind: if path.is_dir() { Kind::Directory } else { Kind::File },
|
||||||
path: path,
|
path: path,
|
||||||
|
dirsize: dirsize,
|
||||||
target: None,
|
target: None,
|
||||||
meta: None,
|
meta: meta,
|
||||||
|
meta_processed: false,
|
||||||
|
dirty_meta: dirty_meta,
|
||||||
color: None,
|
color: None,
|
||||||
selected: false,
|
selected: false,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_path(path: &Path) -> HResult<File> {
|
pub fn new_from_path(path: &Path,
|
||||||
|
dirty_meta: Option<AsyncDirtyBit>) -> HResult<File> {
|
||||||
let pathbuf = path.to_path_buf();
|
let pathbuf = path.to_path_buf();
|
||||||
let name = path
|
let name = path
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|name| name.to_string_lossy().to_string())
|
.map(|name| name.to_string_lossy().to_string())
|
||||||
.unwrap_or("/".to_string());
|
.unwrap_or("/".to_string());
|
||||||
|
|
||||||
Ok(File::new(&name, pathbuf))
|
Ok(File::new(&name, pathbuf, dirty_meta))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_placeholder(path: &Path) -> Result<File, Error> {
|
pub fn new_placeholder(path: &Path) -> Result<File, Error> {
|
||||||
let mut file = File::new_from_path(path)?;
|
let mut file = File::new_from_path(path, None)?;
|
||||||
file.name = "<empty>".to_string();
|
file.name = "<empty>".to_string();
|
||||||
file.kind = Kind::Placeholder;
|
file.kind = Kind::Placeholder;
|
||||||
Ok(file)
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn meta(&self) -> HResult<std::fs::Metadata> {
|
pub fn make_async_meta(path: &PathBuf,
|
||||||
match &self.meta {
|
dirty_meta: Option<AsyncDirtyBit>) -> Async<Metadata> {
|
||||||
Some(meta) => Ok(meta.clone()),
|
let path = path.clone();
|
||||||
None => { Ok(std::fs::symlink_metadata(&self.path)?) }
|
|
||||||
|
let mut meta = Async::new(Box::new(move |stale| {
|
||||||
|
if *stale.lock()? { HError::stale()? }
|
||||||
|
Ok(std::fs::symlink_metadata(&path).unwrap())
|
||||||
|
}));
|
||||||
|
if let Some(dirty_meta) = dirty_meta {
|
||||||
|
meta.on_ready(Box::new(move |_| {
|
||||||
|
let mut dirty_meta = dirty_meta.clone();
|
||||||
|
dirty_meta.set_dirty();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
meta
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_meta(&mut self) -> HResult<()> {
|
pub fn make_async_dirsize(path: &PathBuf,
|
||||||
if let Some(_) = self.meta { return Ok(()) }
|
dirty_meta: Option<AsyncDirtyBit>) -> Async<usize> {
|
||||||
|
let path = path.clone();
|
||||||
|
|
||||||
let meta = std::fs::symlink_metadata(&self.path)?;
|
let mut dirsize = Async::new(Box::new(move |stale| {
|
||||||
|
if *stale.lock()? { HError::stale()? }
|
||||||
|
Ok(std::fs::read_dir(&path)?.count())
|
||||||
|
}));
|
||||||
|
if let Some(dirty_meta) = dirty_meta {
|
||||||
|
dirsize.on_ready(Box::new(move |_| {
|
||||||
|
let mut dirty_meta = dirty_meta.clone();
|
||||||
|
dirty_meta.set_dirty();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
dirsize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meta(&self) -> HResult<&Metadata> {
|
||||||
|
self.meta.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_dirsize(&mut self, pool: &ThreadPool) -> HResult<()> {
|
||||||
|
let dirsize = self.dirsize.as_mut()?;
|
||||||
|
if let Ok(_) = dirsize.value { return Ok(()) }
|
||||||
|
|
||||||
|
match dirsize.take_async() {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(HError::AsyncNotReadyError) => { dirsize.run_pooled(&*pool).ok(); },
|
||||||
|
Err(HError::AsyncAlreadyTakenError) => {},
|
||||||
|
Err(HError::NoneError) => {},
|
||||||
|
err @ Err(_) => { err?; }
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_meta(&mut self, pool: &ThreadPool) -> HResult<()> {
|
||||||
|
if self.meta_processed { return Ok(()) }
|
||||||
|
|
||||||
|
match self.meta.take_async() {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(HError::AsyncNotReadyError) => { self.meta.run_pooled(&*pool).ok(); },
|
||||||
|
Err(HError::AsyncAlreadyTakenError) => {},
|
||||||
|
Err(HError::NoneError) => {},
|
||||||
|
err @ Err(_) => { err?; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(meta) = self.meta.get() {
|
||||||
let color = self.get_color(&meta);
|
let color = self.get_color(&meta);
|
||||||
let target = if meta.file_type().is_symlink() {
|
let target = if meta.file_type().is_symlink() {
|
||||||
self.path.read_link().ok()
|
self.path.read_link().ok()
|
||||||
} else { None };
|
} else { None };
|
||||||
|
|
||||||
self.meta = Some(meta);
|
|
||||||
self.color = color;
|
self.color = color;
|
||||||
self.target = target;
|
self.target = target;
|
||||||
|
self.meta_processed = true;
|
||||||
|
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload_meta(&mut self) -> HResult<()> {
|
pub fn reload_meta(&mut self) -> HResult<()> {
|
||||||
self.meta = None;
|
self.dirty_meta.as_mut()?.set_dirty();
|
||||||
self.get_meta()
|
self.meta_processed = false;
|
||||||
|
self.meta = File::make_async_meta(&self.path, self.dirty_meta.clone());
|
||||||
|
if self.dirsize.is_some() {
|
||||||
|
self.dirsize
|
||||||
|
= Some(File::make_async_dirsize(&self.path, self.dirty_meta.clone()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_color(&self, meta: &std::fs::Metadata) -> Option<lscolors::Color> {
|
fn get_color(&self, meta: &std::fs::Metadata) -> Option<lscolors::Color> {
|
||||||
@ -454,13 +653,8 @@ impl File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_size(&self) -> HResult<(u64, String)> {
|
pub fn calculate_size(&self) -> HResult<(u64, String)> {
|
||||||
if self.is_dir() {
|
if let Some(ref dirsize) = self.dirsize {
|
||||||
let dir_iterator = std::fs::read_dir(&self.path);
|
return Ok((dirsize.value.clone()? as u64, "".to_string()))
|
||||||
match dir_iterator {
|
|
||||||
Ok(dir_iterator) => return Ok((dir_iterator.count() as u64,
|
|
||||||
"".to_string())),
|
|
||||||
Err(_) => return Ok((0, "".to_string()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -496,7 +690,7 @@ impl File {
|
|||||||
|
|
||||||
pub fn parent_as_file(&self) -> HResult<File> {
|
pub fn parent_as_file(&self) -> HResult<File> {
|
||||||
let pathbuf = self.parent()?;
|
let pathbuf = self.parent()?;
|
||||||
File::new_from_path(&pathbuf)
|
File::new_from_path(&pathbuf, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grand_parent(&self) -> Option<PathBuf> {
|
pub fn grand_parent(&self) -> Option<PathBuf> {
|
||||||
@ -505,7 +699,7 @@ impl File {
|
|||||||
|
|
||||||
pub fn grand_parent_as_file(&self) -> HResult<File> {
|
pub fn grand_parent_as_file(&self) -> HResult<File> {
|
||||||
let pathbuf = self.grand_parent()?;
|
let pathbuf = self.grand_parent()?;
|
||||||
File::new_from_path(&pathbuf)
|
File::new_from_path(&pathbuf, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_dir(&self) -> bool {
|
pub fn is_dir(&self) -> bool {
|
||||||
@ -761,14 +955,10 @@ impl OsStrTools for OsStr {
|
|||||||
let pat = pat.as_bytes().to_vec();
|
let pat = pat.as_bytes().to_vec();
|
||||||
let pat_len = pat.len();
|
let pat_len = pat.len();
|
||||||
|
|
||||||
dbg!(&self);
|
|
||||||
|
|
||||||
let split_string = orig_string
|
let split_string = orig_string
|
||||||
.windows(pat_len)
|
.windows(pat_len)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.fold(Vec::new(), |mut split_pos, (i, chars)| {
|
.fold(Vec::new(), |mut split_pos, (i, chars)| {
|
||||||
dbg!(&chars);
|
|
||||||
dbg!(&split_pos);
|
|
||||||
if chars == pat.as_slice() {
|
if chars == pat.as_slice() {
|
||||||
if split_pos.len() == 0 {
|
if split_pos.len() == 0 {
|
||||||
split_pos.push((0, i));
|
split_pos.push((0, i));
|
||||||
@ -816,8 +1006,6 @@ impl OsStrTools for OsStr {
|
|||||||
return vec![OsString::from(self)];
|
return vec![OsString::from(self)];
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&self);
|
|
||||||
|
|
||||||
let pos = pos.unwrap();
|
let pos = pos.unwrap();
|
||||||
let string = self.as_bytes().to_vec();
|
let string = self.as_bytes().to_vec();
|
||||||
let from = from.as_bytes().to_vec();
|
let from = from.as_bytes().to_vec();
|
||||||
@ -826,9 +1014,6 @@ impl OsStrTools for OsStr {
|
|||||||
let lpart = OsString::from_vec(string[0..pos].to_vec());
|
let lpart = OsString::from_vec(string[0..pos].to_vec());
|
||||||
let rpart = OsString::from_vec(string[pos+fromlen..].to_vec());
|
let rpart = OsString::from_vec(string[pos+fromlen..].to_vec());
|
||||||
|
|
||||||
dbg!(&lpart);
|
|
||||||
dbg!(&rpart);
|
|
||||||
|
|
||||||
let mut result = vec![
|
let mut result = vec![
|
||||||
vec![lpart.trim_last_space()],
|
vec![lpart.trim_last_space()],
|
||||||
to,
|
to,
|
||||||
|
@ -28,12 +28,20 @@ impl Listable for ListView<Files> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_refresh(&mut self) -> HResult<()> {
|
fn on_refresh(&mut self) -> HResult<()> {
|
||||||
let visible_file_num = self.selection + self.get_coordinates()?.ysize() as usize;
|
let sender = self.core.get_sender();
|
||||||
self.content.meta_upto(visible_file_num);
|
|
||||||
|
let visible_files = self.core.coordinates.size_u().1 + self.offset + 1;
|
||||||
|
|
||||||
|
self.content.meta_upto(visible_files, Some(sender.clone()));
|
||||||
|
|
||||||
if self.content.is_dirty() {
|
if self.content.is_dirty() {
|
||||||
self.core.set_dirty();
|
|
||||||
self.content.set_clean();
|
self.content.set_clean();
|
||||||
|
self.core.set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.content.dirty_meta.is_dirty() {
|
||||||
|
self.content.meta_upto(visible_files, Some(sender.clone()));
|
||||||
|
self.core.set_dirty();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -52,7 +60,7 @@ impl Listable for ListView<Files> {
|
|||||||
},
|
},
|
||||||
Key::Char('S') => { self.search_file().log(); }
|
Key::Char('S') => { self.search_file().log(); }
|
||||||
Key::Alt('s') => { self.search_next().log(); }
|
Key::Alt('s') => { self.search_next().log(); }
|
||||||
Key::Char('F') => { self.filter().log(); }
|
Key::Ctrl('f') => { self.filter().log(); }
|
||||||
Key::Left => self.goto_grand_parent()?,
|
Key::Left => self.goto_grand_parent()?,
|
||||||
Key::Right => self.goto_selected()?,
|
Key::Right => self.goto_selected()?,
|
||||||
Key::Char(' ') => self.multi_select_file(),
|
Key::Char(' ') => self.multi_select_file(),
|
||||||
@ -285,7 +293,7 @@ impl ListView<Files>
|
|||||||
fn toggle_hidden(&mut self) {
|
fn toggle_hidden(&mut self) {
|
||||||
let file = self.clone_selected_file();
|
let file = self.clone_selected_file();
|
||||||
self.content.toggle_hidden();
|
self.content.toggle_hidden();
|
||||||
self.content.reload_files();
|
//self.content.reload_files();
|
||||||
self.select_file(&file);
|
self.select_file(&file);
|
||||||
self.refresh().log();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
@ -438,21 +446,13 @@ impl ListView<Files>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self) -> Vec<String> {
|
fn render(&self) -> Vec<String> {
|
||||||
match self.content.get_filter() {
|
self.content
|
||||||
Some(filter) => self.content
|
.get_files()
|
||||||
.files
|
|
||||||
.iter()
|
|
||||||
.filter(|f| f.name.contains(&filter))
|
|
||||||
.map(|file| self.render_line(&file))
|
|
||||||
.collect(),
|
|
||||||
None => self.content
|
|
||||||
.files
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|file| self.render_line(&file))
|
.map(|file| self.render_line(&file))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
||||||
|
102
src/preview.rs
102
src/preview.rs
@ -1,5 +1,7 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use rayon::ThreadPool;
|
||||||
|
|
||||||
use crate::files::{File, Files, Kind};
|
use crate::files::{File, Files, Kind};
|
||||||
use crate::listview::ListView;
|
use crate::listview::ListView;
|
||||||
use crate::textview::TextView;
|
use crate::textview::TextView;
|
||||||
@ -36,12 +38,24 @@ pub fn is_stale(stale: &Arc<Mutex<bool>>) -> HResult<bool> {
|
|||||||
Ok(stale)
|
Ok(stale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
impl<T: Send + Debug> Debug for Async<T> {
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> {
|
||||||
|
write!(formatter,
|
||||||
|
"{:?}, {:?} {:?}",
|
||||||
|
self.value,
|
||||||
|
self.async_value,
|
||||||
|
self.stale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct Async<T: Send> {
|
pub struct Async<T: Send> {
|
||||||
pub value: HResult<T>,
|
pub value: HResult<T>,
|
||||||
async_value: AsyncValue<T>,
|
async_value: AsyncValue<T>,
|
||||||
async_closure: Option<AsyncValueFn<T>>,
|
async_closure: Option<AsyncValueFn<T>>,
|
||||||
on_ready: Arc<Mutex<Option<AsyncReadyFn<T>>>>,
|
on_ready: Option<AsyncReadyFn<T>>,
|
||||||
stale: Stale
|
stale: Stale
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,35 +66,79 @@ impl<T: Send + 'static> Async<T> {
|
|||||||
value: HError::async_not_ready(),
|
value: HError::async_not_ready(),
|
||||||
async_value: Arc::new(Mutex::new(None)),
|
async_value: Arc::new(Mutex::new(None)),
|
||||||
async_closure: Some(closure),
|
async_closure: Some(closure),
|
||||||
on_ready: Arc::new(Mutex::new(None)),
|
on_ready: None,
|
||||||
stale: Arc::new(Mutex::new(false)) };
|
stale: Arc::new(Mutex::new(false)) };
|
||||||
|
|
||||||
async_value
|
async_value
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> HResult<()> {
|
pub fn new_with_value(val: T) -> Async<T> {
|
||||||
|
Async {
|
||||||
|
value: Ok(val),
|
||||||
|
async_value: Arc::new(Mutex::new(None)),
|
||||||
|
async_closure: None,
|
||||||
|
on_ready: None,
|
||||||
|
stale: Arc::new(Mutex::new(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> HResult<()> {
|
||||||
let closure = self.async_closure.take()?;
|
let closure = self.async_closure.take()?;
|
||||||
let async_value = self.async_value.clone();
|
let async_value = self.async_value.clone();
|
||||||
let stale = self.stale.clone();
|
let stale = self.stale.clone();
|
||||||
let on_ready_fn = self.on_ready.clone();
|
let on_ready_fn = self.on_ready.take();
|
||||||
|
|
||||||
std::thread::spawn(move|| -> HResult<()> {
|
std::thread::spawn(move|| -> HResult<()> {
|
||||||
let value = closure(stale);
|
let mut value = closure(stale);
|
||||||
match value {
|
|
||||||
Ok(mut value) => {
|
if let Ok(ref mut value) = value {
|
||||||
match *on_ready_fn.lock()? {
|
if let Some(on_ready_fn) = on_ready_fn {
|
||||||
Some(ref on_ready) => { on_ready(&mut value).log(); },
|
on_ready_fn(value);
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
async_value.lock()?.replace(Ok(value));
|
|
||||||
},
|
|
||||||
Err(err) => *async_value.lock()? = Some(Err(err))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async_value.lock()?.replace(value);
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_pooled(&mut self, pool: &ThreadPool) -> HResult<()> {
|
||||||
|
let closure = self.async_closure.take()?;
|
||||||
|
let async_value = self.async_value.clone();
|
||||||
|
let stale = self.stale.clone();
|
||||||
|
let on_ready_fn = self.on_ready.take();
|
||||||
|
|
||||||
|
pool.spawn(move || {
|
||||||
|
let mut value = closure(stale);
|
||||||
|
|
||||||
|
if let Ok(ref mut value) = value {
|
||||||
|
if let Some(on_ready_fn) = on_ready_fn {
|
||||||
|
on_ready_fn(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async_value
|
||||||
|
.lock()
|
||||||
|
.map(|mut async_value| async_value.replace(value));
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait(&mut self) -> HResult<()> {
|
||||||
|
let closure = self.async_closure.take()?;
|
||||||
|
let mut value = closure(self.stale.clone());
|
||||||
|
let on_ready_fn = self.on_ready.take();
|
||||||
|
|
||||||
|
|
||||||
|
if let Ok(ref mut value) = value {
|
||||||
|
on_ready_fn.map(|on_ready_fn| on_ready_fn(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.value = value;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_stale(&mut self) -> HResult<()> {
|
pub fn set_stale(&mut self) -> HResult<()> {
|
||||||
*self.stale.lock()? = true;
|
*self.stale.lock()? = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -91,7 +149,7 @@ impl<T: Send + 'static> Async<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_async(&mut self) -> HResult<()> {
|
pub fn take_async(&mut self) -> HResult<()> {
|
||||||
if self.value.is_ok() { return Ok(()) }
|
if self.value.is_ok() { HError::async_taken()? }
|
||||||
|
|
||||||
let mut async_value = self.async_value.lock()?;
|
let mut async_value = self.async_value.lock()?;
|
||||||
match async_value.as_ref() {
|
match async_value.as_ref() {
|
||||||
@ -99,7 +157,7 @@ impl<T: Send + 'static> Async<T> {
|
|||||||
let value = async_value.take()?;
|
let value = async_value.take()?;
|
||||||
self.value = value;
|
self.value = value;
|
||||||
}
|
}
|
||||||
Some(Err(HError::AsyncAlreadyTakenError(..))) => HError::async_taken()?,
|
Some(Err(HError::AsyncAlreadyTakenError)) => HError::async_taken()?,
|
||||||
Some(Err(_)) => {
|
Some(Err(_)) => {
|
||||||
let value = async_value.take()?;
|
let value = async_value.take()?;
|
||||||
self.value = value;
|
self.value = value;
|
||||||
@ -126,14 +184,8 @@ impl<T: Send + 'static> Async<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_ready(&mut self,
|
pub fn on_ready(&mut self,
|
||||||
fun: AsyncReadyFn<T>)
|
fun: AsyncReadyFn<T>) {
|
||||||
-> HResult<()> {
|
self.on_ready = Some(fun);
|
||||||
if self.value.is_ok() {
|
|
||||||
fun(self.value.as_mut().unwrap())?;
|
|
||||||
} else {
|
|
||||||
*self.on_ready.lock()? = Some(fun);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +213,7 @@ impl<W: Widget + Send + 'static> AsyncWidget<W> {
|
|||||||
widget.on_ready(Box::new(move |_| {
|
widget.on_ready(Box::new(move |_| {
|
||||||
sender.send(crate::widget::Events::WidgetReady)?;
|
sender.send(crate::widget::Events::WidgetReady)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})).log();
|
}));
|
||||||
widget.run().log();
|
widget.run().log();
|
||||||
|
|
||||||
AsyncWidget {
|
AsyncWidget {
|
||||||
@ -182,7 +234,7 @@ impl<W: Widget + Send + 'static> AsyncWidget<W> {
|
|||||||
widget.on_ready(Box::new(move |_| {
|
widget.on_ready(Box::new(move |_| {
|
||||||
sender.send(crate::widget::Events::WidgetReady)?;
|
sender.send(crate::widget::Events::WidgetReady)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}))?;
|
}));
|
||||||
|
|
||||||
widget.run().log();
|
widget.run().log();
|
||||||
|
|
||||||
|
@ -90,12 +90,14 @@ impl WidgetCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Dirtyable for WidgetCore {
|
impl Dirtyable for WidgetCore {
|
||||||
fn get_bit(&self) -> &DirtyBit {
|
fn is_dirty(&self) -> bool {
|
||||||
&self.dirty
|
self.dirty.is_dirty()
|
||||||
}
|
}
|
||||||
|
fn set_dirty(&mut self) {
|
||||||
fn get_bit_mut(&mut self) -> &mut DirtyBit {
|
self.dirty.set_dirty();
|
||||||
&mut self.dirty
|
}
|
||||||
|
fn set_clean(&mut self) {
|
||||||
|
self.dirty.set_clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user