From 6764a8899737af83dc5fee0761da9a9bc9081181 Mon Sep 17 00:00:00 2001 From: Francesco Pio Gaglione Date: Sun, 11 Aug 2024 18:40:46 +0200 Subject: [PATCH 1/4] bookmark column --- src/tab.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/tab.rs b/src/tab.rs index f80056cc..1cab9bd7 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -890,6 +890,7 @@ pub enum HeadingOptions { Name = 0, Modified, Size, + Bookmark, } impl fmt::Display for HeadingOptions { @@ -898,6 +899,7 @@ impl fmt::Display for HeadingOptions { HeadingOptions::Name => write!(f, "{}", fl!("name")), HeadingOptions::Modified => write!(f, "{}", fl!("modified")), HeadingOptions::Size => write!(f, "{}", fl!("size")), + HeadingOptions::Bookmark => write!(f, "{}", "Bookmark"), } } } @@ -908,6 +910,7 @@ impl HeadingOptions { HeadingOptions::Name.to_string(), HeadingOptions::Modified.to_string(), HeadingOptions::Size.to_string(), + HeadingOptions::Bookmark.to_string(), ] } } @@ -1943,6 +1946,10 @@ impl Tab { } }); } + HeadingOptions::Bookmark => { + //TODO + todo!() + } } Some(items) } @@ -2025,7 +2032,8 @@ impl Tab { let name_width = 300.0; let modified_width = 200.0; let size_width = 100.0; - let condensed = size.width < (name_width + modified_width + size_width); + let bookmark_width = 10.0; + let condensed = size.width < (name_width + modified_width + size_width + bookmark_width); let heading_item = |name, width, msg| { let mut row = widget::row::with_capacity(2) @@ -2057,6 +2065,11 @@ impl Tab { HeadingOptions::Modified, ), heading_item(fl!("size"), Length::Fixed(size_width), HeadingOptions::Size), + heading_item( + "".to_string(), + Length::Fixed(bookmark_width), + HeadingOptions::Bookmark, + ), ]) .align_items(Alignment::Center) .height(Length::Fixed((space_m + 4).into())) @@ -2621,7 +2634,8 @@ impl Tab { let name_width = 300.0; let modified_width = 200.0; let size_width = 100.0; - let condensed = size.width < (name_width + modified_width + size_width); + let bookmark_width = 10.0; + let condensed = size.width < (name_width + modified_width + size_width + bookmark_width); let icon_size = if condensed { icon_sizes.list_condensed() } else { @@ -2719,6 +2733,9 @@ impl Tab { widget::text(size_text.clone()) .width(Length::Fixed(size_width)) .into(), + widget::icon::from_name("user-bookmarks-symbolic") + .size(icon_size / 2) + .into(), ]) .align_items(Alignment::Center) .spacing(space_xxs) From 9397998b79d89f1d6e6ab7100f96c47a87d36229 Mon Sep 17 00:00:00 2001 From: Francesco Pio Gaglione Date: Thu, 15 Aug 2024 15:50:07 +0200 Subject: [PATCH 2/4] moved action to context menu (because of a button bug). Bookmarks handling in progess --- .gitignore | 2 + i18n/en/cosmic_files.ftl | 3 + src/app.rs | 22 +++ src/config.rs | 2 + src/menu.rs | 3 + src/tab.rs | 405 +++++++++++++++++++++------------------ 6 files changed, 253 insertions(+), 184 deletions(-) diff --git a/.gitignore b/.gitignore index 54059777..12ee6d5a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ /target/ /vendor.tar /vendor/ + +.idea \ No newline at end of file diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index 7a7f5872..30bd7933 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -5,6 +5,7 @@ filesystem = Filesystem home = Home notification-in-progress = File operations are in progress. trash = Trash +bookmarks = Bookmarks undo = Undo # List view @@ -134,6 +135,8 @@ new-file = New file... new-folder = New folder... open-in-terminal = Open in terminal move-to-trash = Move to trash +add_bookmark = Add bookmark +remove_bookmark = Remove bookmark restore-from-trash = Restore from trash remove-from-sidebar = Remove from sidebar sort-by-name = Sort by name diff --git a/src/app.rs b/src/app.rs index ef9f54fc..99bdd589 100644 --- a/src/app.rs +++ b/src/app.rs @@ -107,6 +107,7 @@ pub enum Action { ZoomDefault, ZoomIn, ZoomOut, + AddBookmark, } impl Action { @@ -164,6 +165,7 @@ impl Action { Action::ZoomDefault => Message::TabMessage(entity_opt, tab::Message::ZoomDefault), Action::ZoomIn => Message::TabMessage(entity_opt, tab::Message::ZoomIn), Action::ZoomOut => Message::TabMessage(entity_opt, tab::Message::ZoomOut), + Action::AddBookmark => Message::AddBookmark(entity_opt), } } } @@ -267,6 +269,7 @@ pub enum Message { DndExitTab, DndDropTab(Entity, Option, DndAction), DndDropNav(Entity, Option, DndAction), + AddBookmark(Option), } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -484,6 +487,13 @@ impl App { fn update_nav_model(&mut self) { let mut nav_model = segmented_button::ModelBuilder::default(); + + nav_model = nav_model.insert(|b| { + b.text("Bookmark") + .icon(widget::icon::from_name("user-bookmarks-symbolic")) + .data(Location::Bookmarks) + }); + for (favorite_i, favorite) in self.config.favorites.iter().enumerate() { if let Some(path) = favorite.path_opt() { let name = if matches!(favorite, Favorite::Home) { @@ -1116,6 +1126,7 @@ impl Application for App { self.nav_model.activate(entity); if let Some(location) = self.nav_model.data::(entity) { + log::info!("location on nav select: {:?}", location); let message = Message::TabMessage(None, tab::Message::Location(location.clone())); return self.update(message); } @@ -2152,6 +2163,9 @@ impl Application for App { Some(Location::Trash) => { return self.open_tab(Location::Trash); } + Some(Location::Bookmarks) => { + return self.open_tab(Location::Bookmarks); + } _ => {} } } @@ -2195,6 +2209,14 @@ impl Application for App { self.dialog_pages.push_front(DialogPage::EmptyTrash); } }, + Message::AddBookmark(entity) => { + let selected_paths = self.selected_paths(entity); + let mut bookmarks = self.config.bookmarks.clone(); + for path in selected_paths { + bookmarks.push(path); + } + config_set!(bookmarks, bookmarks); + } } Command::none() diff --git a/src/config.rs b/src/config.rs index 96c5ed3f..f760e359 100644 --- a/src/config.rs +++ b/src/config.rs @@ -95,6 +95,7 @@ pub struct Config { pub app_theme: AppTheme, pub favorites: Vec, pub tab: TabConfig, + pub bookmarks: Vec, } impl Default for Config { @@ -110,6 +111,7 @@ impl Default for Config { Favorite::Videos, ], tab: TabConfig::default(), + bookmarks: Vec::new(), } } } diff --git a/src/menu.rs b/src/menu.rs index b0aa3fc7..dfbb0e8f 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -121,6 +121,8 @@ pub fn context_menu<'a>( children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into()); children.push(container(horizontal_rule(1)).padding([0, 8]).into()); children.push(menu_item(fl!("move-to-trash"), Action::MoveToTrash).into()); + //TODO this should be add or remove if it already exist not only add + children.push(menu_item(fl!("add_bookmark"), Action::AddBookmark).into()); } else { //TODO: need better designs for menu with no selection //TODO: have things like properties but they apply to the folder? @@ -152,6 +154,7 @@ pub fn context_menu<'a>( children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified)); children.push(sort_item(fl!("sort-by-size"), HeadingOptions::Size)); } + Location::Bookmarks => todo!(), } widget::container(widget::column::with_children(children)) diff --git a/src/tab.rs b/src/tab.rs index 1cab9bd7..5da48f9a 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1,37 +1,30 @@ -use cosmic::{ - cosmic_theme, font, - iced::{ - advanced::{ - graphics, - text::{self, Paragraph}, - }, - alignment::{Horizontal, Vertical}, - clipboard::dnd::DndAction, - futures::SinkExt, - keyboard::Modifiers, - subscription::{self, Subscription}, - //TODO: export in cosmic::widget - widget::{ - container, horizontal_rule, - scrollable::{AbsoluteOffset, Viewport}, - }, - Alignment, - Border, - Color, - ContentFit, - Length, - Point, - Rectangle, - Size, +use cosmic::{cosmic_config, cosmic_theme, font, iced::{ + advanced::{ + graphics, + text::{self, Paragraph}, }, - iced_core::widget::tree, - theme, widget, + alignment::{Horizontal, Vertical}, + clipboard::dnd::DndAction, + futures::SinkExt, + keyboard::Modifiers, + subscription::{self, Subscription}, + //TODO: export in cosmic::widget widget::{ - menu::{action::MenuAction, key_bind::KeyBind}, - vertical_space, DndDestination, DndSource, Id, Widget, + container, horizontal_rule, + scrollable::{AbsoluteOffset, Viewport}, }, - Element, -}; + Alignment, + Border, + Color, + ContentFit, + Length, + Point, + Rectangle, + Size, +}, iced_core::widget::tree, theme, widget, widget::{ + menu::{action::MenuAction, key_bind::KeyBind}, + vertical_space, DndDestination, DndSource, Id, Widget, +}, Application, Element}; use mime_guess::{mime, Mime}; use once_cell::sync::Lazy; @@ -47,7 +40,7 @@ use std::{ sync::{Arc, Mutex}, time::{Duration, Instant}, }; - +use cosmic::cosmic_config::CosmicConfigEntry; use crate::{ app::{self, Action}, clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, @@ -60,6 +53,8 @@ use crate::{ mime_icon::{mime_for_path, mime_icon}, mouse_area, }; +use crate::app::App; +use crate::config::{Config, CONFIG_VERSION}; pub const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500); pub const HOVER_DURATION: Duration = Duration::from_millis(1600); @@ -159,8 +154,8 @@ pub fn folder_icon_symbolic(path: &PathBuf, icon_size: u16) -> widget::icon::Han "{}-symbolic", SPECIAL_DIRS.get(path).map_or("folder", |x| *x) )) - .size(icon_size) - .handle() + .size(icon_size) + .handle() } pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { @@ -176,8 +171,8 @@ pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { } else { "user-trash-symbolic" }) - .size(icon_size) - .handle() + .size(icon_size) + .handle() } //TODO: translate, add more levels? @@ -543,11 +538,41 @@ pub fn scan_trash(sizes: IconSizes) -> Vec { items } +pub fn scan_bookmarks(sizes: IconSizes) -> Vec { + let mut items: Vec = Vec::new(); + let bookmarks = match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) { + Ok(config_handler) => { + let config = Config::get_entry(&config_handler).unwrap_or_else(|(errs, config)| { + log::info!("errors loading config: {:?}", errs); + config + }); + Some(config.bookmarks) + } + Err(err) => { + log::error!("failed to create config handler: {}", err); + None + } + }; + log::info!("bookmarks letti: {:?}", bookmarks); + + if let Some(bookmarks) = bookmarks { + //TODO guarda la funzione scan_path e copia l'implementazione + for b in bookmarks { + if let Some(name) = b.file_name() { + let grid_name = Item::grid_name(&name.to_string_lossy()); + } + } + } + + items +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum Location { Path(PathBuf), Search(PathBuf, String), Trash, + Bookmarks, } impl std::fmt::Display for Location { @@ -556,6 +581,7 @@ impl std::fmt::Display for Location { Self::Path(path) => write!(f, "{}", path.display()), Self::Search(path, term) => write!(f, "search {} for {}", path.display(), term), Self::Trash => write!(f, "trash"), + Self::Bookmarks => write!(f, "bookmarks"), } } } @@ -566,6 +592,7 @@ impl Location { Self::Path(path) => scan_path(path, sizes), Self::Search(path, term) => scan_search(path, term, sizes), Self::Trash => scan_trash(sizes), + Self::Bookmarks => scan_bookmarks(sizes), } } } @@ -764,12 +791,12 @@ impl Item { widget::text(&app.name).into() }, ]) - .spacing(space_xs), + .spacing(space_xs), ) - //TODO: do not clone so much? - .on_press(app::Message::OpenWith(path.clone(), app.clone())) - .padding(space_xs) - .width(Length::Fill), + //TODO: do not clone so much? + .on_press(app::Message::OpenWith(path.clone(), app.clone())) + .padding(space_xs) + .width(Length::Fill), ); } } @@ -890,7 +917,6 @@ pub enum HeadingOptions { Name = 0, Modified, Size, - Bookmark, } impl fmt::Display for HeadingOptions { @@ -899,7 +925,6 @@ impl fmt::Display for HeadingOptions { HeadingOptions::Name => write!(f, "{}", fl!("name")), HeadingOptions::Modified => write!(f, "{}", fl!("modified")), HeadingOptions::Size => write!(f, "{}", fl!("size")), - HeadingOptions::Bookmark => write!(f, "{}", "Bookmark"), } } } @@ -910,7 +935,6 @@ impl HeadingOptions { HeadingOptions::Name.to_string(), HeadingOptions::Modified.to_string(), HeadingOptions::Size.to_string(), - HeadingOptions::Bookmark.to_string(), ] } } @@ -982,6 +1006,9 @@ impl Tab { Location::Trash => { fl!("trash") } + Location::Bookmarks => { + fl!("bookmarks") + } } } @@ -1382,9 +1409,9 @@ impl Tab { } else { let dont_unset = mod_ctrl || self.column_sort().is_some_and(|l| { - l.iter() - .any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected) - }); + l.iter() + .any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected) + }); *self.cached_selected.borrow_mut() = None; if let Some(ref mut items) = self.items_opt { for (i, item) in items.iter_mut().enumerate() { @@ -1448,8 +1475,8 @@ impl Tab { Location::Search(ref path, _) => Some(path), _ => None, } - .and_then(|path| path.ancestors().nth(ancestor_index)) - .map(|path| path.to_path_buf()) + .and_then(|path| path.ancestors().nth(ancestor_index)) + .map(|path| path.to_path_buf()) }; match action { LocationMenuAction::OpenInNewTab(ancestor_index) => { @@ -1651,6 +1678,9 @@ impl Tab { Location::Trash => { cd = Some(location); } + Location::Bookmarks => { + cd = Some(location) + } } } Message::LocationUp => { @@ -1791,6 +1821,10 @@ impl Tab { Location::Trash => { log::warn!("Copy to trash is not supported."); } + Location::Bookmarks => { + // enable copy to add to bookmarks? + log::warn!("Copy to bookmarks is not supported"); + } }; } Message::Drop(None) => { @@ -1863,6 +1897,7 @@ impl Tab { Location::Path(path) => path.is_dir(), Location::Search(path, _term) => path.is_dir(), Location::Trash => true, + Location::Bookmarks => true, } { self.change_location(&location, history_i_opt); commands.push(Command::ChangeLocation(self.title(), location)); @@ -1946,10 +1981,6 @@ impl Tab { } }); } - HeadingOptions::Bookmark => { - //TODO - todo!() - } } Some(items) } @@ -2032,8 +2063,7 @@ impl Tab { let name_width = 300.0; let modified_width = 200.0; let size_width = 100.0; - let bookmark_width = 10.0; - let condensed = size.width < (name_width + modified_width + size_width + bookmark_width); + let condensed = size.width < (name_width + modified_width + size_width); let heading_item = |name, width, msg| { let mut row = widget::row::with_capacity(2) @@ -2065,16 +2095,11 @@ impl Tab { HeadingOptions::Modified, ), heading_item(fl!("size"), Length::Fixed(size_width), HeadingOptions::Size), - heading_item( - "".to_string(), - Length::Fixed(bookmark_width), - HeadingOptions::Bookmark, - ), ]) - .align_items(Alignment::Center) - .height(Length::Fixed((space_m + 4).into())) - .padding([0, space_xxs]) - .spacing(space_xxs); + .align_items(Alignment::Center) + .height(Length::Fixed((space_m + 4).into())) + .padding([0, space_xxs]) + .spacing(space_xxs); if let Some(location) = &self.edit_location { match location { @@ -2114,8 +2139,8 @@ impl Tab { .padding(space_xxs) .style(theme::Button::Icon), ) - .on_press(move |_| Message::EditLocation(Some(self.location.clone()))) - .on_middle_press(move |_| Message::OpenInNewTab(path.clone())), + .on_press(move |_| Message::EditLocation(Some(self.location.clone()))) + .on_middle_press(move |_| Message::OpenInNewTab(path.clone())), ); w += 16.0 + 2.0 * space_xxs as f32; } else if let Location::Search(_, term) = &self.location { @@ -2127,14 +2152,15 @@ impl Tab { .into(), widget::text::body(term).wrap(text::Wrap::None).into(), ]) - .spacing(space_xxs), + .spacing(space_xxs), ) - .padding(space_xxs) - .style(theme::Button::Icon), + .padding(space_xxs) + .style(theme::Button::Icon), ); w += text_width_body(term) + 16.0 + 3.0 * space_xxs as f32; } + let mut children: Vec> = Vec::new(); match &self.location { Location::Path(path) | Location::Search(path, ..) => { @@ -2150,7 +2176,7 @@ impl Tab { &ancestor.to_path_buf(), 16, )) - .size(16); + .size(16); found_home = true; (fl!("home"), Some(icon)) } else { @@ -2216,15 +2242,15 @@ impl Tab { .padding(space_xxxs) .style(theme::Button::Link), ) - .on_press(move |_| { - Message::Location(match &self.location { - Location::Path(_) => Location::Path(ancestor.to_path_buf()), - Location::Search(_, term) => { - Location::Search(ancestor.to_path_buf(), term.clone()) - } - other => other.clone(), - }) - }); + .on_press(move |_| { + Message::Location(match &self.location { + Location::Path(_) => Location::Path(ancestor.to_path_buf()), + Location::Search(_, term) => { + Location::Search(ancestor.to_path_buf(), term.clone()) + } + other => other.clone(), + }) + }); if self.location_context_menu_index.is_some() { mouse_area = mouse_area.on_right_press(move |_point_opt| { @@ -2266,6 +2292,21 @@ impl Tab { .into(), ); } + Location::Bookmarks => { + let mut row = widget::row::with_capacity(2) + .align_items(Alignment::Center) + .spacing(space_xxxs); + row = row.push(widget::icon::from_name("user-bookmarks-symbolic").size(16)); + row = row.push(widget::text::heading(fl!("bookmarks"))); + + children.push( + widget::button(row) + .padding(space_xxxs) + .on_press(Message::Location(Location::Bookmarks)) + .style(theme::Button::Text) + .into(), + ); + } } for child in children { @@ -2310,17 +2351,17 @@ impl Tab { } else { fl!("empty-folder") }) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs), + .align_items(Alignment::Center) + .spacing(space_xxs), ) - .align_x(Horizontal::Center) - .align_y(Vertical::Center) - .width(Length::Fill) - .height(Length::Fill) - .into()]) - .into() + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .width(Length::Fill) + .height(Length::Fill) + .into()]) + .into() } pub fn grid_view( @@ -2411,8 +2452,8 @@ impl Tab { .content_fit(ContentFit::Contain) .size(icon_sizes.grid()), ) - .padding(space_xxxs) - .style(button_style(item.selected, false, false)), + .padding(space_xxxs) + .style(button_style(item.selected, false, false)), widget::button(widget::text::body(&item.grid_name)) .id(item.button_id.clone()) .padding([0, space_xxxs]) @@ -2458,31 +2499,31 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) - .on_leave(move || Message::DndLeave(tab_location_leave.clone())), + .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) + .on_leave(move || Message::DndLeave(tab_location_leave.clone())), ) - .style(if is_dnd_hovered { - theme::Container::custom(|t| { - let mut a = cosmic::iced_style::container::StyleSheet::appearance( - t, - &theme::Container::default(), - ); - let t = t.cosmic(); - // todo use theme drop target color - let mut bg = t.accent_color(); - bg.alpha = 0.2; - a.background = Some(Color::from(bg).into()); - a.border = Border { - color: t.accent_color().into(), - width: 1.0, - radius: t.radius_s().into(), - }; - a + .style(if is_dnd_hovered { + theme::Container::custom(|t| { + let mut a = cosmic::iced_style::container::StyleSheet::appearance( + t, + &theme::Container::default(), + ); + let t = t.cosmic(); + // todo use theme drop target color + let mut bg = t.accent_color(); + bg.alpha = 0.2; + a.background = Some(Color::from(bg).into()); + a.border = Border { + color: t.accent_color().into(), + width: 1.0, + radius: t.radius_s().into(), + }; + a + }) + } else { + theme::Container::default() }) - } else { - theme::Container::default() - }) - .into() + .into() } else { column.into() }; @@ -2560,13 +2601,13 @@ impl Tab { .content_fit(ContentFit::Contain) .size(icon_sizes.grid()), ) - .on_press(Message::Click(Some(*i))) - .padding(space_xxxs) - .style(button_style( - item.selected, - false, - false, - )), + .on_press(Message::Click(Some(*i))) + .padding(space_xxxs) + .style(button_style( + item.selected, + false, + false, + )), widget::button(widget::text(item.grid_name.clone())) .id(item.button_id.clone()) .on_press(Message::Click(Some(*i))) @@ -2598,12 +2639,12 @@ impl Tab { mouse_area::MouseArea::new( widget::container(widget::column::with_children(children)).width(Length::Fill), ) - .on_press(|_| Message::Click(None)) - .on_drag(Message::Drag) - .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) - .on_release(|_| Message::ClickRelease(None)) - .into(), + .on_press(|_| Message::Click(None)) + .on_drag(Message::Drag) + .on_drag_end(|_| Message::DragEnd(None)) + .show_drag_rect(true) + .on_release(|_| Message::ClickRelease(None)) + .into(), true, ) } @@ -2634,8 +2675,7 @@ impl Tab { let name_width = 300.0; let modified_width = 200.0; let size_width = 100.0; - let bookmark_width = 10.0; - let condensed = size.width < (name_width + modified_width + size_width + bookmark_width); + let condensed = size.width < (name_width + modified_width + size_width); let icon_size = if condensed { icon_sizes.list_condensed() } else { @@ -2716,10 +2756,10 @@ impl Tab { widget::text::caption(format!("{} - {}", modified_text, size_text)) .into(), ]) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) + .align_items(Alignment::Center) + .spacing(space_xxs) } else { widget::row::with_children(vec![ widget::icon::icon(item.icon_handle_list.clone()) @@ -2733,12 +2773,9 @@ impl Tab { widget::text(size_text.clone()) .width(Length::Fixed(size_width)) .into(), - widget::icon::from_name("user-bookmarks-symbolic") - .size(icon_size / 2) - .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) + .align_items(Alignment::Center) + .spacing(space_xxs) }; let button = |row| { @@ -2758,10 +2795,10 @@ impl Tab { }) .style(button_style(item.selected, true, false)), ) - .on_press(move |_| Message::Click(Some(i))) - .on_double_click(move |_| Message::DoubleClick(Some(i))) - .on_release(move |_| Message::ClickRelease(Some(i))) - .on_middle_press(move |_| Message::MiddleClick(i)); + .on_press(move |_| Message::Click(Some(i))) + .on_double_click(move |_| Message::DoubleClick(Some(i))) + .on_release(move |_| Message::ClickRelease(Some(i))) + .on_middle_press(move |_| Message::MiddleClick(i)); if self.context_menu.is_some() { mouse_area @@ -2796,32 +2833,32 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) - .on_leave(move || Message::DndLeave(tab_location_leave.clone())), + .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) + .on_leave(move || Message::DndLeave(tab_location_leave.clone())), ) - // todo refactor into the dnd destination wrapper - .style(if is_dnd_hovered { - theme::Container::custom(|t| { - let mut a = cosmic::iced_style::container::StyleSheet::appearance( - t, - &theme::Container::default(), - ); - let t = t.cosmic(); - // todo use theme drop target color - let mut bg = t.accent_color(); - bg.alpha = 0.2; - a.background = Some(Color::from(bg).into()); - a.border = Border { - color: t.accent_color().into(), - width: 1.0, - radius: t.radius_s().into(), - }; - a + // todo refactor into the dnd destination wrapper + .style(if is_dnd_hovered { + theme::Container::custom(|t| { + let mut a = cosmic::iced_style::container::StyleSheet::appearance( + t, + &theme::Container::default(), + ); + let t = t.cosmic(); + // todo use theme drop target color + let mut bg = t.accent_color(); + bg.alpha = 0.2; + a.background = Some(Color::from(bg).into()); + a.border = Border { + color: t.accent_color().into(), + width: 1.0, + radius: t.radius_s().into(), + }; + a + }) + } else { + theme::Container::default() }) - } else { - theme::Container::default() - }) - .into() + .into() } else { button_row.into() }; @@ -2840,11 +2877,11 @@ impl Tab { //TODO: translate? widget::text(format!("{} - {}", modified_text, size_text)).into(), ]) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) - .into() + .align_items(Alignment::Center) + .spacing(space_xxs) + .into() } else { widget::row::with_children(vec![ widget::icon::icon(item.icon_handle_list.clone()) @@ -2859,9 +2896,9 @@ impl Tab { .width(Length::Fixed(size_width)) .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) - .into() + .align_items(Alignment::Center) + .spacing(space_xxs) + .into() }; if item.selected { drag_items.push( @@ -2903,13 +2940,13 @@ impl Tab { mouse_area::MouseArea::new( widget::column::with_children(children).padding([0, space_s]), ) - .with_id(Id::new("list-view")) - .on_press(|_| Message::Click(None)) - .on_drag(Message::Drag) - .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) - .on_release(|_| Message::ClickRelease(None)) - .into(), + .with_id(Id::new("list-view")) + .on_press(|_| Message::Click(None)) + .on_drag(Message::Drag) + .on_drag_end(|_| Message::DragEnd(None)) + .show_drag_rect(true) + .on_release(|_| Message::ClickRelease(None)) + .into(), true, ) } @@ -3009,8 +3046,8 @@ impl Tab { .on_press(Message::EmptyTrash) .into(), ])) - .padding([space_xxs, space_xs]) - .layer(cosmic_theme::Layer::Primary), + .padding([space_xxs, space_xs]) + .layer(cosmic_theme::Layer::Primary), ); } } @@ -3052,8 +3089,8 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_2.clone())) - .on_leave(move || Message::DndLeave(tab_location_3.clone())); + .on_enter(move |_, _, _| Message::DndEnter(tab_location_2.clone())) + .on_leave(move || Message::DndLeave(tab_location_3.clone())); dnd_dest.into() } @@ -3135,8 +3172,8 @@ impl Tab { log::info!("thumbnailed {:?} in {:?}", path, start.elapsed()); (path, thumbnail) }) - .await - .unwrap(); + .await + .unwrap(); match output .send(Message::Thumbnail(path.clone(), thumbnail)) From 211f60b289122255c09ef91401078c8be1755225 Mon Sep 17 00:00:00 2001 From: Francesco Pio Gaglione Date: Thu, 15 Aug 2024 16:39:35 +0200 Subject: [PATCH 3/4] rendered items in bookmark sections and some improvements --- i18n/en/cosmic_files.ftl | 3 +- i18n/it/cosmic_files.ftl | 2 + src/app.rs | 10 +- src/menu.rs | 5 +- src/tab.rs | 391 ++++++++++++++++++++------------------- 5 files changed, 218 insertions(+), 193 deletions(-) diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index 131bd216..89ddbbf1 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -136,8 +136,7 @@ new-file = New file... new-folder = New folder... open-in-terminal = Open in terminal move-to-trash = Move to trash -add_bookmark = Add bookmark -remove_bookmark = Remove bookmark +add_remove_bookmark = Add/remove bookmark restore-from-trash = Restore from trash remove-from-sidebar = Remove from sidebar sort-by-name = Sort by name diff --git a/i18n/it/cosmic_files.ftl b/i18n/it/cosmic_files.ftl index 2a21623d..d2ec5518 100644 --- a/i18n/it/cosmic_files.ftl +++ b/i18n/it/cosmic_files.ftl @@ -4,6 +4,7 @@ empty-folder-hidden = Cartella vuota (contiene elementi nascosti) filesystem = Filesystem home = Home trash = Cestino +bookmarks = Segnalibri # New File/Folder Dialog create-new-file = Crea file @@ -73,6 +74,7 @@ new-file = Nuovo file new-folder = Nuova cartella open-with = Apri con move-to-trash = Sposta nel cestino +add_remove_bookmark = Aggiungi/rimuovi segnalibro restore-from-trash = Ripristina dal cestino # Menu diff --git a/src/app.rs b/src/app.rs index afef058f..51e843bb 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1150,7 +1150,6 @@ impl Application for App { self.nav_model.activate(entity); if let Some(location) = self.nav_model.data::(entity) { - log::info!("location on nav select: {:?}", location); let message = Message::TabMessage(None, tab::Message::Location(location.clone())); return self.update(message); } @@ -2239,9 +2238,16 @@ impl Application for App { Message::AddBookmark(entity) => { let selected_paths = self.selected_paths(entity); let mut bookmarks = self.config.bookmarks.clone(); + for path in selected_paths { - bookmarks.push(path); + if !bookmarks.iter().any(|p| p == &path) { + bookmarks.push(path); + } + else { + bookmarks.retain(|p| p != &path); + } } + config_set!(bookmarks, bookmarks); } } diff --git a/src/menu.rs b/src/menu.rs index dfbb0e8f..5fb521b4 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -92,7 +92,7 @@ pub fn context_menu<'a>( let mut children: Vec> = Vec::new(); match tab.location { - Location::Path(_) | Location::Search(_, _) => { + Location::Path(_) | Location::Search(_, _) | Location::Bookmarks => { if selected > 0 { if selected_dir == 1 && selected == 1 || selected_dir == 0 { children.push(menu_item(fl!("open"), Action::Open).into()); @@ -122,7 +122,7 @@ pub fn context_menu<'a>( children.push(container(horizontal_rule(1)).padding([0, 8]).into()); children.push(menu_item(fl!("move-to-trash"), Action::MoveToTrash).into()); //TODO this should be add or remove if it already exist not only add - children.push(menu_item(fl!("add_bookmark"), Action::AddBookmark).into()); + children.push(menu_item(fl!("add_remove_bookmark"), Action::AddBookmark).into()); } else { //TODO: need better designs for menu with no selection //TODO: have things like properties but they apply to the folder? @@ -154,7 +154,6 @@ pub fn context_menu<'a>( children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified)); children.push(sort_item(fl!("sort-by-size"), HeadingOptions::Size)); } - Location::Bookmarks => todo!(), } widget::container(widget::column::with_children(children)) diff --git a/src/tab.rs b/src/tab.rs index 828a7c35..e845c7a4 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1,31 +1,53 @@ -use cosmic::{cosmic_config, cosmic_theme, font, iced::{ - advanced::{ - graphics, - text::{self, Paragraph}, +use cosmic::{ + cosmic_config, cosmic_theme, font, + iced::{ + advanced::{ + graphics, + text::{self, Paragraph}, + }, + alignment::{Horizontal, Vertical}, + clipboard::dnd::DndAction, + futures::SinkExt, + keyboard::Modifiers, + subscription::{self, Subscription}, + //TODO: export in cosmic::widget + widget::{ + container, horizontal_rule, + scrollable::{AbsoluteOffset, Viewport}, + }, + Alignment, + Border, + Color, + ContentFit, + Length, + Point, + Rectangle, + Size, }, - alignment::{Horizontal, Vertical}, - clipboard::dnd::DndAction, - futures::SinkExt, - keyboard::Modifiers, - subscription::{self, Subscription}, - //TODO: export in cosmic::widget + iced_core::widget::tree, + theme, widget, widget::{ - container, horizontal_rule, - scrollable::{AbsoluteOffset, Viewport}, + menu::{action::MenuAction, key_bind::KeyBind}, + vertical_space, DndDestination, DndSource, Id, Widget, }, - Alignment, - Border, - Color, - ContentFit, - Length, - Point, - Rectangle, - Size, -}, iced_core::widget::tree, theme, widget, widget::{ - menu::{action::MenuAction, key_bind::KeyBind}, - vertical_space, DndDestination, DndSource, Id, Widget, -}, Application, Element}; + Application, Element, +}; +use crate::app::App; +use crate::config::{Config, CONFIG_VERSION}; +use crate::{ + app::{self, Action}, + clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, + config::{IconSizes, TabConfig, ICON_SCALE_MAX, ICON_SIZE_GRID}, + dialog::DialogKind, + fl, + localize::{LANGUAGE_CHRONO, LANGUAGE_SORTER}, + menu, + mime_app::{mime_apps, MimeApp}, + mime_icon::{mime_for_path, mime_icon}, + mouse_area, +}; +use cosmic::cosmic_config::CosmicConfigEntry; use mime_guess::{mime, Mime}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -40,21 +62,7 @@ use std::{ sync::{Arc, Mutex}, time::{Duration, Instant}, }; -use cosmic::cosmic_config::CosmicConfigEntry; -use crate::{ - app::{self, Action}, - clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, - config::{IconSizes, TabConfig, ICON_SCALE_MAX, ICON_SIZE_GRID}, - dialog::DialogKind, - fl, - localize::{LANGUAGE_CHRONO, LANGUAGE_SORTER}, - menu, - mime_app::{mime_apps, MimeApp}, - mime_icon::{mime_for_path, mime_icon}, - mouse_area, -}; -use crate::app::App; -use crate::config::{Config, CONFIG_VERSION}; +use rayon::prelude::*; pub const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500); pub const HOVER_DURATION: Duration = Duration::from_millis(1600); @@ -154,8 +162,8 @@ pub fn folder_icon_symbolic(path: &PathBuf, icon_size: u16) -> widget::icon::Han "{}-symbolic", SPECIAL_DIRS.get(path).map_or("folder", |x| *x) )) - .size(icon_size) - .handle() + .size(icon_size) + .handle() } pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { @@ -171,8 +179,8 @@ pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { } else { "user-trash-symbolic" }) - .size(icon_size) - .handle() + .size(icon_size) + .handle() } //TODO: translate, add more levels? @@ -553,18 +561,32 @@ pub fn scan_bookmarks(sizes: IconSizes) -> Vec { None } }; - log::info!("bookmarks letti: {:?}", bookmarks); - if let Some(bookmarks) = bookmarks { - //TODO guarda la funzione scan_path e copia l'implementazione - for b in bookmarks { - if let Some(name) = b.file_name() { - let grid_name = Item::grid_name(&name.to_string_lossy()); - } - } - } + bookmarks + .unwrap_or_default() + .into_par_iter() // Parallel iterator + .filter_map(|path| { + let name = match path.file_name() { + Some(name) => name.to_string_lossy().to_string(), + None => { + log::warn!( + "failed to parse entry at {:?} is not valid UTF-8", + path, + ); + return None + } + }; - items + let metadata = match path.metadata() { + Ok(ok) => ok, + Err(err) => { + log::warn!("failed to read metadata for entry at {:?}: {}", path, err); + return None + } + }; + Some(item_from_entry(path, name, metadata, sizes)) + }) + .collect() } #[derive(Clone, Debug, Eq, PartialEq)] @@ -791,12 +813,12 @@ impl Item { widget::text(&app.name).into() }, ]) - .spacing(space_xs), + .spacing(space_xs), ) - //TODO: do not clone so much? - .on_press(app::Message::OpenWith(path.clone(), app.clone())) - .padding(space_xs) - .width(Length::Fill), + //TODO: do not clone so much? + .on_press(app::Message::OpenWith(path.clone(), app.clone())) + .padding(space_xs) + .width(Length::Fill), ); } } @@ -1409,9 +1431,9 @@ impl Tab { } else { let dont_unset = mod_ctrl || self.column_sort().is_some_and(|l| { - l.iter() - .any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected) - }); + l.iter() + .any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected) + }); *self.cached_selected.borrow_mut() = None; if let Some(ref mut items) = self.items_opt { for (i, item) in items.iter_mut().enumerate() { @@ -1475,8 +1497,8 @@ impl Tab { Location::Search(ref path, _) => Some(path), _ => None, } - .and_then(|path| path.ancestors().nth(ancestor_index)) - .map(|path| path.to_path_buf()) + .and_then(|path| path.ancestors().nth(ancestor_index)) + .map(|path| path.to_path_buf()) }; match action { LocationMenuAction::OpenInNewTab(ancestor_index) => { @@ -1678,9 +1700,7 @@ impl Tab { Location::Trash => { cd = Some(location); } - Location::Bookmarks => { - cd = Some(location) - } + Location::Bookmarks => cd = Some(location), } } Message::LocationUp => { @@ -2096,10 +2116,10 @@ impl Tab { ), heading_item(fl!("size"), Length::Fixed(size_width), HeadingOptions::Size), ]) - .align_items(Alignment::Center) - .height(Length::Fixed((space_m + 4).into())) - .padding([0, space_xxs]) - .spacing(space_xxs); + .align_items(Alignment::Center) + .height(Length::Fixed((space_m + 4).into())) + .padding([0, space_xxs]) + .spacing(space_xxs); if let Some(location) = &self.edit_location { match location { @@ -2139,8 +2159,8 @@ impl Tab { .padding(space_xxs) .style(theme::Button::Icon), ) - .on_press(move |_| Message::EditLocation(Some(self.location.clone()))) - .on_middle_press(move |_| Message::OpenInNewTab(path.clone())), + .on_press(move |_| Message::EditLocation(Some(self.location.clone()))) + .on_middle_press(move |_| Message::OpenInNewTab(path.clone())), ); w += 16.0 + 2.0 * space_xxs as f32; } else if let Location::Search(_, term) = &self.location { @@ -2152,15 +2172,14 @@ impl Tab { .into(), widget::text::body(term).wrap(text::Wrap::None).into(), ]) - .spacing(space_xxs), + .spacing(space_xxs), ) - .padding(space_xxs) - .style(theme::Button::Icon), + .padding(space_xxs) + .style(theme::Button::Icon), ); w += text_width_body(term) + 16.0 + 3.0 * space_xxs as f32; } - let mut children: Vec> = Vec::new(); match &self.location { Location::Path(path) | Location::Search(path, ..) => { @@ -2176,7 +2195,7 @@ impl Tab { &ancestor.to_path_buf(), 16, )) - .size(16); + .size(16); found_home = true; (fl!("home"), Some(icon)) } else { @@ -2242,15 +2261,15 @@ impl Tab { .padding(space_xxxs) .style(theme::Button::Link), ) - .on_press(move |_| { - Message::Location(match &self.location { - Location::Path(_) => Location::Path(ancestor.to_path_buf()), - Location::Search(_, term) => { - Location::Search(ancestor.to_path_buf(), term.clone()) - } - other => other.clone(), - }) - }); + .on_press(move |_| { + Message::Location(match &self.location { + Location::Path(_) => Location::Path(ancestor.to_path_buf()), + Location::Search(_, term) => { + Location::Search(ancestor.to_path_buf(), term.clone()) + } + other => other.clone(), + }) + }); if self.location_context_menu_index.is_some() { mouse_area = mouse_area.on_right_press(move |_point_opt| { @@ -2353,17 +2372,17 @@ impl Tab { } else { fl!("empty-folder") }) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs), + .align_items(Alignment::Center) + .spacing(space_xxs), ) - .align_x(Horizontal::Center) - .align_y(Vertical::Center) - .width(Length::Fill) - .height(Length::Fill) - .into()]) - .into() + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .width(Length::Fill) + .height(Length::Fill) + .into()]) + .into() } pub fn grid_view( @@ -2454,8 +2473,8 @@ impl Tab { .content_fit(ContentFit::Contain) .size(icon_sizes.grid()), ) - .padding(space_xxxs) - .style(button_style(item.selected, false, false)), + .padding(space_xxxs) + .style(button_style(item.selected, false, false)), widget::button(widget::text::body(&item.grid_name)) .id(item.button_id.clone()) .padding([0, space_xxxs]) @@ -2501,31 +2520,31 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) - .on_leave(move || Message::DndLeave(tab_location_leave.clone())), + .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) + .on_leave(move || Message::DndLeave(tab_location_leave.clone())), ) - .style(if is_dnd_hovered { - theme::Container::custom(|t| { - let mut a = cosmic::iced_style::container::StyleSheet::appearance( - t, - &theme::Container::default(), - ); - let t = t.cosmic(); - // todo use theme drop target color - let mut bg = t.accent_color(); - bg.alpha = 0.2; - a.background = Some(Color::from(bg).into()); - a.border = Border { - color: t.accent_color().into(), - width: 1.0, - radius: t.radius_s().into(), - }; - a - }) - } else { - theme::Container::default() + .style(if is_dnd_hovered { + theme::Container::custom(|t| { + let mut a = cosmic::iced_style::container::StyleSheet::appearance( + t, + &theme::Container::default(), + ); + let t = t.cosmic(); + // todo use theme drop target color + let mut bg = t.accent_color(); + bg.alpha = 0.2; + a.background = Some(Color::from(bg).into()); + a.border = Border { + color: t.accent_color().into(), + width: 1.0, + radius: t.radius_s().into(), + }; + a }) - .into() + } else { + theme::Container::default() + }) + .into() } else { column.into() }; @@ -2603,13 +2622,13 @@ impl Tab { .content_fit(ContentFit::Contain) .size(icon_sizes.grid()), ) - .on_press(Message::Click(Some(*i))) - .padding(space_xxxs) - .style(button_style( - item.selected, - false, - false, - )), + .on_press(Message::Click(Some(*i))) + .padding(space_xxxs) + .style(button_style( + item.selected, + false, + false, + )), widget::button(widget::text(item.grid_name.clone())) .id(item.button_id.clone()) .on_press(Message::Click(Some(*i))) @@ -2641,12 +2660,12 @@ impl Tab { mouse_area::MouseArea::new( widget::container(widget::column::with_children(children)).width(Length::Fill), ) - .on_press(|_| Message::Click(None)) - .on_drag(Message::Drag) - .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) - .on_release(|_| Message::ClickRelease(None)) - .into(), + .on_press(|_| Message::Click(None)) + .on_drag(Message::Drag) + .on_drag_end(|_| Message::DragEnd(None)) + .show_drag_rect(true) + .on_release(|_| Message::ClickRelease(None)) + .into(), true, ) } @@ -2758,10 +2777,10 @@ impl Tab { widget::text::caption(format!("{} - {}", modified_text, size_text)) .into(), ]) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) + .align_items(Alignment::Center) + .spacing(space_xxs) } else { widget::row::with_children(vec![ widget::icon::icon(item.icon_handle_list.clone()) @@ -2776,8 +2795,8 @@ impl Tab { .width(Length::Fixed(size_width)) .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) + .align_items(Alignment::Center) + .spacing(space_xxs) }; let button = |row| { @@ -2797,10 +2816,10 @@ impl Tab { }) .style(button_style(item.selected, true, false)), ) - .on_press(move |_| Message::Click(Some(i))) - .on_double_click(move |_| Message::DoubleClick(Some(i))) - .on_release(move |_| Message::ClickRelease(Some(i))) - .on_middle_press(move |_| Message::MiddleClick(i)); + .on_press(move |_| Message::Click(Some(i))) + .on_double_click(move |_| Message::DoubleClick(Some(i))) + .on_release(move |_| Message::ClickRelease(Some(i))) + .on_middle_press(move |_| Message::MiddleClick(i)); if self.context_menu.is_some() { mouse_area @@ -2835,32 +2854,32 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) - .on_leave(move || Message::DndLeave(tab_location_leave.clone())), + .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) + .on_leave(move || Message::DndLeave(tab_location_leave.clone())), ) - // todo refactor into the dnd destination wrapper - .style(if is_dnd_hovered { - theme::Container::custom(|t| { - let mut a = cosmic::iced_style::container::StyleSheet::appearance( - t, - &theme::Container::default(), - ); - let t = t.cosmic(); - // todo use theme drop target color - let mut bg = t.accent_color(); - bg.alpha = 0.2; - a.background = Some(Color::from(bg).into()); - a.border = Border { - color: t.accent_color().into(), - width: 1.0, - radius: t.radius_s().into(), - }; - a - }) - } else { - theme::Container::default() + // todo refactor into the dnd destination wrapper + .style(if is_dnd_hovered { + theme::Container::custom(|t| { + let mut a = cosmic::iced_style::container::StyleSheet::appearance( + t, + &theme::Container::default(), + ); + let t = t.cosmic(); + // todo use theme drop target color + let mut bg = t.accent_color(); + bg.alpha = 0.2; + a.background = Some(Color::from(bg).into()); + a.border = Border { + color: t.accent_color().into(), + width: 1.0, + radius: t.radius_s().into(), + }; + a }) - .into() + } else { + theme::Container::default() + }) + .into() } else { button_row.into() }; @@ -2879,11 +2898,11 @@ impl Tab { //TODO: translate? widget::text(format!("{} - {}", modified_text, size_text)).into(), ]) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) - .into() + .align_items(Alignment::Center) + .spacing(space_xxs) + .into() } else { widget::row::with_children(vec![ widget::icon::icon(item.icon_handle_list.clone()) @@ -2898,9 +2917,9 @@ impl Tab { .width(Length::Fixed(size_width)) .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) - .into() + .align_items(Alignment::Center) + .spacing(space_xxs) + .into() }; if item.selected { drag_items.push( @@ -2942,13 +2961,13 @@ impl Tab { mouse_area::MouseArea::new( widget::column::with_children(children).padding([0, space_s]), ) - .with_id(Id::new("list-view")) - .on_press(|_| Message::Click(None)) - .on_drag(Message::Drag) - .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) - .on_release(|_| Message::ClickRelease(None)) - .into(), + .with_id(Id::new("list-view")) + .on_press(|_| Message::Click(None)) + .on_drag(Message::Drag) + .on_drag_end(|_| Message::DragEnd(None)) + .show_drag_rect(true) + .on_release(|_| Message::ClickRelease(None)) + .into(), true, ) } @@ -3048,8 +3067,8 @@ impl Tab { .on_press(Message::EmptyTrash) .into(), ])) - .padding([space_xxs, space_xs]) - .layer(cosmic_theme::Layer::Primary), + .padding([space_xxs, space_xs]) + .layer(cosmic_theme::Layer::Primary), ); } } @@ -3091,8 +3110,8 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_2.clone())) - .on_leave(move || Message::DndLeave(tab_location_3.clone())); + .on_enter(move |_, _, _| Message::DndEnter(tab_location_2.clone())) + .on_leave(move || Message::DndLeave(tab_location_3.clone())); dnd_dest.into() } @@ -3174,8 +3193,8 @@ impl Tab { log::info!("thumbnailed {:?} in {:?}", path, start.elapsed()); (path, thumbnail) }) - .await - .unwrap(); + .await + .unwrap(); match output .send(Message::Thumbnail(path.clone(), thumbnail)) From c944a495fa2f8f39fd194a7314cbd3f3aa19e604 Mon Sep 17 00:00:00 2001 From: Francesco-gaglione Date: Thu, 5 Sep 2024 10:30:09 +0200 Subject: [PATCH 4/4] merge --- src/app.rs | 2 +- src/tab.rs | 332 +++++++++++++++++++++++++---------------------------- 2 files changed, 157 insertions(+), 177 deletions(-) diff --git a/src/app.rs b/src/app.rs index 30d56947..726350aa 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2263,7 +2263,7 @@ impl Application for App { return self.open_tab(Location::Trash, false, None); } Some(Location::Bookmarks) => { - return self.open_tab(Location::Bookmarks); + return self.open_tab(Location::Bookmarks, false, None); } _ => {} } diff --git a/src/tab.rs b/src/tab.rs index 5133559d..f8656922 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1,53 +1,32 @@ -use cosmic::{ - cosmic_config, cosmic_theme, font, - iced::{ - advanced::{ - graphics, - text::{self, Paragraph}, - }, - alignment::{Horizontal, Vertical}, - clipboard::dnd::DndAction, - futures::SinkExt, - keyboard::Modifiers, - subscription::{self, Subscription}, - //TODO: export in cosmic::widget - widget::{ - container, horizontal_rule, - scrollable::{AbsoluteOffset, Viewport}, - }, - Alignment, - Border, - Color, - ContentFit, - Length, - Point, - Rectangle, - Size, +use cosmic::{cosmic_config, cosmic_theme, font, iced::{ + advanced::{ + graphics, + text::{self, Paragraph}, }, - iced_core::widget::tree, - theme, widget, + alignment::{Horizontal, Vertical}, + clipboard::dnd::DndAction, + futures::SinkExt, + keyboard::Modifiers, + subscription::{self, Subscription}, + //TODO: export in cosmic::widget widget::{ - menu::{action::MenuAction, key_bind::KeyBind}, - vertical_space, DndDestination, DndSource, Id, Widget, + container, horizontal_rule, + scrollable::{AbsoluteOffset, Viewport}, }, - Application, Element, -}; - -use crate::app::App; + Alignment, + Border, + Color, + ContentFit, + Length, + Point, + Rectangle, + Size, +}, iced_core::widget::tree, theme, widget, widget::{ + menu::{action::MenuAction, key_bind::KeyBind}, + vertical_space, DndDestination, DndSource, Id, Widget, +}, Application, Element}; +use rayon::prelude::*; use crate::config::{Config, CONFIG_VERSION}; -use crate::{ - app::{self, Action}, - clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, - config::{IconSizes, TabConfig, ICON_SCALE_MAX, ICON_SIZE_GRID}, - dialog::DialogKind, - fl, - localize::{LANGUAGE_CHRONO, LANGUAGE_SORTER}, - menu, - mime_app::{mime_apps, MimeApp}, - mime_icon::{mime_for_path, mime_icon}, - mouse_area, -}; -use cosmic::cosmic_config::CosmicConfigEntry; use mime_guess::{mime, Mime}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -63,8 +42,8 @@ use std::{ sync::{Arc, Mutex}, time::{Duration, Instant}, }; -use rayon::prelude::*; - +use cosmic::cosmic_config::CosmicConfigEntry; +use rayon::iter::IntoParallelIterator; use crate::{ app::{self, Action}, clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, @@ -79,6 +58,7 @@ use crate::{ }; use unix_permissions_ext::UNIXPermissionsExt; use uzers::{get_group_by_gid, get_user_by_uid}; +use crate::app::App; pub const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500); pub const HOVER_DURATION: Duration = Duration::from_millis(1600); @@ -178,8 +158,8 @@ pub fn folder_icon_symbolic(path: &PathBuf, icon_size: u16) -> widget::icon::Han "{}-symbolic", SPECIAL_DIRS.get(path).map_or("folder", |x| *x) )) - .size(icon_size) - .handle() + .size(icon_size) + .handle() } pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { @@ -195,8 +175,8 @@ pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { } else { "user-trash-symbolic" }) - .size(icon_size) - .handle() + .size(icon_size) + .handle() } //TODO: translate, add more levels? @@ -632,7 +612,7 @@ pub fn scan_bookmarks(sizes: IconSizes) -> Vec { "failed to parse entry at {:?} is not valid UTF-8", path, ); - return None + return None; } }; @@ -640,7 +620,7 @@ pub fn scan_bookmarks(sizes: IconSizes) -> Vec { Ok(ok) => ok, Err(err) => { log::warn!("failed to read metadata for entry at {:?}: {}", path, err); - return None + return None; } }; Some(item_from_entry(path, name, metadata, sizes)) @@ -872,12 +852,12 @@ impl Item { widget::text(&app.name).into() }, ]) - .spacing(space_xs), + .spacing(space_xs), ) - //TODO: do not clone so much? - .on_press(app::Message::OpenWith(path.clone(), app.clone())) - .padding(space_xs) - .width(Length::Fill), + //TODO: do not clone so much? + .on_press(app::Message::OpenWith(path.clone(), app.clone())) + .padding(space_xs) + .width(Length::Fill), ); } } @@ -947,7 +927,7 @@ impl Item { ))) .push(widget::text(format!( "({})", - format_permissions(metadata, PermissionOwner::Owner,) + format_permissions(metadata, PermissionOwner::Owner, ) ))) .spacing(10), ); @@ -961,7 +941,7 @@ impl Item { ))) .push(widget::text(format!( "({})", - format_permissions(metadata, PermissionOwner::Group,) + format_permissions(metadata, PermissionOwner::Group, ) ))) .spacing(10), ); @@ -971,7 +951,7 @@ impl Item { .push(widget::text(format!("{}", fl!("other")))) .push(widget::text(format!( "({})", - format_permissions(metadata, PermissionOwner::Other,) + format_permissions(metadata, PermissionOwner::Other, ) ))) .spacing(10), ); @@ -1540,9 +1520,9 @@ impl Tab { } else { let dont_unset = mod_ctrl || self.column_sort().is_some_and(|l| { - l.iter() - .any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected) - }); + l.iter() + .any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected) + }); *self.cached_selected.borrow_mut() = None; if let Some(ref mut items) = self.items_opt { for (i, item) in items.iter_mut().enumerate() { @@ -1606,8 +1586,8 @@ impl Tab { Location::Search(ref path, _) => Some(path), _ => None, } - .and_then(|path| path.ancestors().nth(ancestor_index)) - .map(|path| path.to_path_buf()) + .and_then(|path| path.ancestors().nth(ancestor_index)) + .map(|path| path.to_path_buf()) }; match action { LocationMenuAction::OpenInNewTab(ancestor_index) => { @@ -2231,10 +2211,10 @@ impl Tab { ), heading_item(fl!("size"), Length::Fixed(size_width), HeadingOptions::Size), ]) - .align_items(Alignment::Center) - .height(Length::Fixed((space_m + 4).into())) - .padding([0, space_xxs]) - .spacing(space_xxs); + .align_items(Alignment::Center) + .height(Length::Fixed((space_m + 4).into())) + .padding([0, space_xxs]) + .spacing(space_xxs); if let Some(location) = &self.edit_location { match location { @@ -2275,7 +2255,7 @@ impl Tab { .style(theme::Button::Icon) .on_press(Message::EditLocation(Some(self.location.clone()))), ) - .on_middle_press(move |_| Message::OpenInNewTab(path.clone())), + .on_middle_press(move |_| Message::OpenInNewTab(path.clone())), ); w += 16.0 + 2.0 * space_xxs as f32; } else if let Location::Search(_, term) = &self.location { @@ -2287,10 +2267,10 @@ impl Tab { .into(), widget::text::body(term).wrap(text::Wrap::None).into(), ]) - .spacing(space_xxs), + .spacing(space_xxs), ) - .padding(space_xxs) - .style(theme::Button::Icon), + .padding(space_xxs) + .style(theme::Button::Icon), ); w += text_width_body(term) + 16.0 + 3.0 * space_xxs as f32; } @@ -2310,7 +2290,7 @@ impl Tab { &ancestor.to_path_buf(), 16, )) - .size(16); + .size(16); found_home = true; (fl!("home"), Some(icon)) } else { @@ -2485,17 +2465,17 @@ impl Tab { } else { fl!("empty-folder") }) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs), + .align_items(Alignment::Center) + .spacing(space_xxs), ) - .align_x(Horizontal::Center) - .align_y(Vertical::Center) - .width(Length::Fill) - .height(Length::Fill) - .into()]) - .into() + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .width(Length::Fill) + .height(Length::Fill) + .into()]) + .into() } pub fn grid_view( @@ -2586,8 +2566,8 @@ impl Tab { .content_fit(ContentFit::Contain) .size(icon_sizes.grid()), ) - .padding(space_xxxs) - .style(button_style(item.selected, false, false)), + .padding(space_xxxs) + .style(button_style(item.selected, false, false)), widget::button(widget::text::body(&item.grid_name)) .id(item.button_id.clone()) .padding([0, space_xxxs]) @@ -2633,31 +2613,31 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) - .on_leave(move || Message::DndLeave(tab_location_leave.clone())), + .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) + .on_leave(move || Message::DndLeave(tab_location_leave.clone())), ) - .style(if is_dnd_hovered { - theme::Container::custom(|t| { - let mut a = cosmic::iced_style::container::StyleSheet::appearance( - t, - &theme::Container::default(), - ); - let t = t.cosmic(); - // todo use theme drop target color - let mut bg = t.accent_color(); - bg.alpha = 0.2; - a.background = Some(Color::from(bg).into()); - a.border = Border { - color: t.accent_color().into(), - width: 1.0, - radius: t.radius_s().into(), - }; - a + .style(if is_dnd_hovered { + theme::Container::custom(|t| { + let mut a = cosmic::iced_style::container::StyleSheet::appearance( + t, + &theme::Container::default(), + ); + let t = t.cosmic(); + // todo use theme drop target color + let mut bg = t.accent_color(); + bg.alpha = 0.2; + a.background = Some(Color::from(bg).into()); + a.border = Border { + color: t.accent_color().into(), + width: 1.0, + radius: t.radius_s().into(), + }; + a + }) + } else { + theme::Container::default() }) - } else { - theme::Container::default() - }) - .into() + .into() } else { column.into() }; @@ -2742,13 +2722,13 @@ impl Tab { .content_fit(ContentFit::Contain) .size(icon_sizes.grid()), ) - .on_press(Message::Click(Some(*i))) - .padding(space_xxxs) - .style(button_style( - item.selected, - false, - false, - )), + .on_press(Message::Click(Some(*i))) + .padding(space_xxxs) + .style(button_style( + item.selected, + false, + false, + )), widget::button(widget::text(item.grid_name.clone())) .id(item.button_id.clone()) .on_press(Message::Click(Some(*i))) @@ -2780,12 +2760,12 @@ impl Tab { mouse_area::MouseArea::new( widget::container(widget::column::with_children(children)).width(Length::Fill), ) - .on_press(|_| Message::Click(None)) - .on_drag(Message::Drag) - .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) - .on_release(|_| Message::ClickRelease(None)) - .into(), + .on_press(|_| Message::Click(None)) + .on_drag(Message::Drag) + .on_drag_end(|_| Message::DragEnd(None)) + .show_drag_rect(true) + .on_release(|_| Message::ClickRelease(None)) + .into(), true, ) } @@ -2897,10 +2877,10 @@ impl Tab { widget::text::caption(format!("{} - {}", modified_text, size_text)) .into(), ]) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) + .align_items(Alignment::Center) + .spacing(space_xxs) } else { widget::row::with_children(vec![ widget::icon::icon(item.icon_handle_list.clone()) @@ -2915,8 +2895,8 @@ impl Tab { .width(Length::Fixed(size_width)) .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) + .align_items(Alignment::Center) + .spacing(space_xxs) }; let button = |row| { @@ -2936,10 +2916,10 @@ impl Tab { }) .style(button_style(item.selected, true, false)), ) - .on_press(move |_| Message::Click(Some(i))) - .on_double_click(move |_| Message::DoubleClick(Some(i))) - .on_release(move |_| Message::ClickRelease(Some(i))) - .on_middle_press(move |_| Message::MiddleClick(i)); + .on_press(move |_| Message::Click(Some(i))) + .on_double_click(move |_| Message::DoubleClick(Some(i))) + .on_release(move |_| Message::ClickRelease(Some(i))) + .on_middle_press(move |_| Message::MiddleClick(i)); if self.context_menu.is_some() { mouse_area @@ -2974,32 +2954,32 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) - .on_leave(move || Message::DndLeave(tab_location_leave.clone())), + .on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone())) + .on_leave(move || Message::DndLeave(tab_location_leave.clone())), ) - // todo refactor into the dnd destination wrapper - .style(if is_dnd_hovered { - theme::Container::custom(|t| { - let mut a = cosmic::iced_style::container::StyleSheet::appearance( - t, - &theme::Container::default(), - ); - let t = t.cosmic(); - // todo use theme drop target color - let mut bg = t.accent_color(); - bg.alpha = 0.2; - a.background = Some(Color::from(bg).into()); - a.border = Border { - color: t.accent_color().into(), - width: 1.0, - radius: t.radius_s().into(), - }; - a + // todo refactor into the dnd destination wrapper + .style(if is_dnd_hovered { + theme::Container::custom(|t| { + let mut a = cosmic::iced_style::container::StyleSheet::appearance( + t, + &theme::Container::default(), + ); + let t = t.cosmic(); + // todo use theme drop target color + let mut bg = t.accent_color(); + bg.alpha = 0.2; + a.background = Some(Color::from(bg).into()); + a.border = Border { + color: t.accent_color().into(), + width: 1.0, + radius: t.radius_s().into(), + }; + a + }) + } else { + theme::Container::default() }) - } else { - theme::Container::default() - }) - .into() + .into() } else { button_row.into() }; @@ -3018,11 +2998,11 @@ impl Tab { //TODO: translate? widget::text(format!("{} - {}", modified_text, size_text)).into(), ]) - .into(), + .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) - .into() + .align_items(Alignment::Center) + .spacing(space_xxs) + .into() } else { widget::row::with_children(vec![ widget::icon::icon(item.icon_handle_list.clone()) @@ -3037,9 +3017,9 @@ impl Tab { .width(Length::Fixed(size_width)) .into(), ]) - .align_items(Alignment::Center) - .spacing(space_xxs) - .into() + .align_items(Alignment::Center) + .spacing(space_xxs) + .into() }; if item.selected { drag_items.push( @@ -3087,13 +3067,13 @@ impl Tab { mouse_area::MouseArea::new( widget::column::with_children(children).padding([0, space_s]), ) - .with_id(Id::new("list-view")) - .on_press(|_| Message::Click(None)) - .on_drag(Message::Drag) - .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) - .on_release(|_| Message::ClickRelease(None)) - .into(), + .with_id(Id::new("list-view")) + .on_press(|_| Message::Click(None)) + .on_drag(Message::Drag) + .on_drag_end(|_| Message::DragEnd(None)) + .show_drag_rect(true) + .on_release(|_| Message::ClickRelease(None)) + .into(), true, ) } @@ -3193,8 +3173,8 @@ impl Tab { .on_press(Message::EmptyTrash) .into(), ])) - .padding([space_xxs, space_xs]) - .layer(cosmic_theme::Layer::Primary), + .padding([space_xxs, space_xs]) + .layer(cosmic_theme::Layer::Primary), ); } } @@ -3236,8 +3216,8 @@ impl Tab { Message::Drop(None) } }) - .on_enter(move |_, _, _| Message::DndEnter(tab_location_2.clone())) - .on_leave(move || Message::DndLeave(tab_location_3.clone())); + .on_enter(move |_, _, _| Message::DndEnter(tab_location_2.clone())) + .on_leave(move || Message::DndLeave(tab_location_3.clone())); dnd_dest.into() } @@ -3319,8 +3299,8 @@ impl Tab { log::info!("thumbnailed {:?} in {:?}", path, start.elapsed()); (path, thumbnail) }) - .await - .unwrap(); + .await + .unwrap(); match output .send(Message::Thumbnail(path.clone(), thumbnail))