Enable preview of svg
This commit is contained in:
parent
eff3c631df
commit
f76ab74a9d
1 changed files with 55 additions and 52 deletions
107
src/tab.rs
107
src/tab.rs
|
|
@ -243,10 +243,14 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec<Item> {
|
||||||
|
|
||||||
let open_with = mime_apps(&mime);
|
let open_with = mime_apps(&mime);
|
||||||
|
|
||||||
let thumbnail_res_opt = if mime.type_() == "image" {
|
let thumbnail_opt = if mime.type_() == mime::IMAGE {
|
||||||
None
|
if mime.subtype() == mime::SVG {
|
||||||
|
Some(ItemThumbnail::Svg)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(Err(()))
|
Some(ItemThumbnail::NotImage)
|
||||||
};
|
};
|
||||||
|
|
||||||
let children = if metadata.is_dir() {
|
let children = if metadata.is_dir() {
|
||||||
|
|
@ -272,7 +276,7 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec<Item> {
|
||||||
icon_handle_list,
|
icon_handle_list,
|
||||||
icon_handle_list_condensed,
|
icon_handle_list_condensed,
|
||||||
open_with,
|
open_with,
|
||||||
thumbnail_res_opt,
|
thumbnail_opt,
|
||||||
button_id: widget::Id::unique(),
|
button_id: widget::Id::unique(),
|
||||||
pos_opt: Cell::new(None),
|
pos_opt: Cell::new(None),
|
||||||
rect_opt: Cell::new(None),
|
rect_opt: Cell::new(None),
|
||||||
|
|
@ -364,7 +368,7 @@ pub fn scan_trash(sizes: IconSizes) -> Vec<Item> {
|
||||||
icon_handle_list,
|
icon_handle_list,
|
||||||
icon_handle_list_condensed,
|
icon_handle_list_condensed,
|
||||||
open_with: Vec::new(),
|
open_with: Vec::new(),
|
||||||
thumbnail_res_opt: Some(Err(())),
|
thumbnail_opt: Some(ItemThumbnail::NotImage),
|
||||||
button_id: widget::Id::unique(),
|
button_id: widget::Id::unique(),
|
||||||
pos_opt: Cell::new(None),
|
pos_opt: Cell::new(None),
|
||||||
rect_opt: Cell::new(None),
|
rect_opt: Cell::new(None),
|
||||||
|
|
@ -431,7 +435,7 @@ pub enum Message {
|
||||||
RightClick(usize),
|
RightClick(usize),
|
||||||
Scroll(Viewport),
|
Scroll(Viewport),
|
||||||
SelectAll,
|
SelectAll,
|
||||||
Thumbnail(PathBuf, Result<image::RgbaImage, ()>),
|
Thumbnail(PathBuf, ItemThumbnail),
|
||||||
ToggleShowHidden,
|
ToggleShowHidden,
|
||||||
View(View),
|
View(View),
|
||||||
ToggleSort(HeadingOptions),
|
ToggleSort(HeadingOptions),
|
||||||
|
|
@ -461,6 +465,13 @@ impl ItemMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ItemThumbnail {
|
||||||
|
NotImage,
|
||||||
|
Rgba(image::RgbaImage),
|
||||||
|
Svg,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
@ -472,7 +483,7 @@ pub struct Item {
|
||||||
pub icon_handle_list: widget::icon::Handle,
|
pub icon_handle_list: widget::icon::Handle,
|
||||||
pub icon_handle_list_condensed: widget::icon::Handle,
|
pub icon_handle_list_condensed: widget::icon::Handle,
|
||||||
pub open_with: Vec<MimeApp>,
|
pub open_with: Vec<MimeApp>,
|
||||||
pub thumbnail_res_opt: Option<Result<image::RgbaImage, ()>>,
|
pub thumbnail_opt: Option<ItemThumbnail>,
|
||||||
pub button_id: widget::Id,
|
pub button_id: widget::Id,
|
||||||
pub pos_opt: Cell<Option<(usize, usize)>>,
|
pub pos_opt: Cell<Option<(usize, usize)>>,
|
||||||
pub rect_opt: Cell<Option<Rectangle>>,
|
pub rect_opt: Cell<Option<Rectangle>>,
|
||||||
|
|
@ -481,6 +492,26 @@ pub struct Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
|
fn preview(&self, sizes: IconSizes) -> Element<app::Message> {
|
||||||
|
// This loads the image only if thumbnailing worked
|
||||||
|
match self
|
||||||
|
.thumbnail_opt
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&ItemThumbnail::NotImage)
|
||||||
|
{
|
||||||
|
ItemThumbnail::NotImage => widget::icon::icon(self.icon_handle_grid.clone())
|
||||||
|
.content_fit(ContentFit::Contain)
|
||||||
|
.size(sizes.grid())
|
||||||
|
.into(),
|
||||||
|
ItemThumbnail::Rgba(_) => {
|
||||||
|
widget::image::viewer(widget::image::Handle::from_path(&self.path))
|
||||||
|
.min_scale(1.0)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
ItemThumbnail::Svg => widget::Svg::from_path(&self.path).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn open_with_view(&self, sizes: IconSizes) -> Element<app::Message> {
|
pub fn open_with_view(&self, sizes: IconSizes) -> Element<app::Message> {
|
||||||
let cosmic_theme::Spacing {
|
let cosmic_theme::Spacing {
|
||||||
space_xs,
|
space_xs,
|
||||||
|
|
@ -492,21 +523,7 @@ impl Item {
|
||||||
|
|
||||||
column = column.push(widget::row::with_children(vec![
|
column = column.push(widget::row::with_children(vec![
|
||||||
widget::horizontal_space(Length::Fill).into(),
|
widget::horizontal_space(Length::Fill).into(),
|
||||||
// This loads the image only if thumbnailing worked
|
self.preview(sizes),
|
||||||
if self
|
|
||||||
.thumbnail_res_opt
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |res| res.is_ok())
|
|
||||||
{
|
|
||||||
widget::image::viewer(widget::image::Handle::from_path(&self.path))
|
|
||||||
.min_scale(1.0)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
widget::icon::icon(self.icon_handle_grid.clone())
|
|
||||||
.content_fit(ContentFit::Contain)
|
|
||||||
.size(sizes.grid())
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
widget::horizontal_space(Length::Fill).into(),
|
widget::horizontal_space(Length::Fill).into(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
|
@ -544,21 +561,7 @@ impl Item {
|
||||||
|
|
||||||
column = column.push(widget::row::with_children(vec![
|
column = column.push(widget::row::with_children(vec![
|
||||||
widget::horizontal_space(Length::Fill).into(),
|
widget::horizontal_space(Length::Fill).into(),
|
||||||
// This loads the image only if thumbnailing worked
|
self.preview(sizes),
|
||||||
if self
|
|
||||||
.thumbnail_res_opt
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |res| res.is_ok())
|
|
||||||
{
|
|
||||||
widget::image::viewer(widget::image::Handle::from_path(&self.path))
|
|
||||||
.min_scale(1.0)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
widget::icon::icon(self.icon_handle_grid.clone())
|
|
||||||
.content_fit(ContentFit::Contain)
|
|
||||||
.size(sizes.grid())
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
widget::horizontal_space(Length::Fill).into(),
|
widget::horizontal_space(Length::Fill).into(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
|
@ -1177,21 +1180,21 @@ impl Tab {
|
||||||
commands.push(Command::FocusButton(widget::Id::unique()));
|
commands.push(Command::FocusButton(widget::Id::unique()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Thumbnail(path, thumbnail_res) => {
|
Message::Thumbnail(path, thumbnail) => {
|
||||||
if let Some(ref mut items) = self.items_opt {
|
if let Some(ref mut items) = self.items_opt {
|
||||||
for item in items.iter_mut() {
|
for item in items.iter_mut() {
|
||||||
if item.path == path {
|
if item.path == path {
|
||||||
if let Ok(thumbnail) = &thumbnail_res {
|
if let ItemThumbnail::Rgba(rgba) = &thumbnail {
|
||||||
//TODO: pass handles already generated to avoid blocking main thread
|
//TODO: pass handles already generated to avoid blocking main thread
|
||||||
let handle = widget::icon::from_raster_pixels(
|
let handle = widget::icon::from_raster_pixels(
|
||||||
thumbnail.width(),
|
rgba.width(),
|
||||||
thumbnail.height(),
|
rgba.height(),
|
||||||
thumbnail.as_raw().clone(),
|
rgba.as_raw().clone(),
|
||||||
);
|
);
|
||||||
item.icon_handle_grid = handle.clone();
|
item.icon_handle_grid = handle.clone();
|
||||||
item.icon_handle_list = handle;
|
item.icon_handle_list = handle;
|
||||||
}
|
}
|
||||||
item.thumbnail_res_opt = Some(thumbnail_res);
|
item.thumbnail_opt = Some(thumbnail);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1858,7 +1861,7 @@ impl Tab {
|
||||||
};
|
};
|
||||||
|
|
||||||
for item in items.iter() {
|
for item in items.iter() {
|
||||||
if item.thumbnail_res_opt.is_some() {
|
if item.thumbnail_opt.is_some() {
|
||||||
// Skip items that already have a thumbnail
|
// Skip items that already have a thumbnail
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1881,33 +1884,33 @@ impl Tab {
|
||||||
path.clone(),
|
path.clone(),
|
||||||
1,
|
1,
|
||||||
|mut output| async move {
|
|mut output| async move {
|
||||||
let (path, thumbnail_res) = tokio::task::spawn_blocking(move || {
|
let (path, thumbnail) = tokio::task::spawn_blocking(move || {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
let thumbnail_res = match image::io::Reader::open(&path) {
|
let thumbnail = match image::io::Reader::open(&path) {
|
||||||
Ok(reader) => match reader.decode() {
|
Ok(reader) => match reader.decode() {
|
||||||
Ok(image) => {
|
Ok(image) => {
|
||||||
//TODO: configurable thumbnail size
|
//TODO: configurable thumbnail size?
|
||||||
let thumbnail = image.thumbnail(64, 64);
|
let thumbnail = image.thumbnail(64, 64);
|
||||||
Ok(thumbnail.to_rgba8())
|
ItemThumbnail::Rgba(thumbnail.to_rgba8())
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("failed to decode {:?}: {}", path, err);
|
log::warn!("failed to decode {:?}: {}", path, err);
|
||||||
Err(())
|
ItemThumbnail::NotImage
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("failed to read {:?}: {}", path, err);
|
log::warn!("failed to read {:?}: {}", path, err);
|
||||||
Err(())
|
ItemThumbnail::NotImage
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
log::info!("thumbnailed {:?} in {:?}", path, start.elapsed());
|
log::info!("thumbnailed {:?} in {:?}", path, start.elapsed());
|
||||||
(path, thumbnail_res)
|
(path, thumbnail)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
match output
|
match output
|
||||||
.send(Message::Thumbnail(path.clone(), thumbnail_res))
|
.send(Message::Thumbnail(path.clone(), thumbnail))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue