Skip to content

Commit 5f58c59

Browse files
author
Alex Klepov
committed
symlink MVP
1 parent 7cebbef commit 5f58c59

2 files changed

Lines changed: 169 additions & 37 deletions

File tree

src/mounter/gvfs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
217217
dir_size: DirSize::NotDirectory,
218218
cut: false,
219219
checksums: ChecksumState::default(),
220+
is_symlink: info.boolean(gio::FILE_ATTRIBUTE_STANDARD_IS_SYMLINK),
220221
});
221222
}
222223
Ok(items)

src/tab.rs

Lines changed: 168 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use cosmic::iced::{
1616
};
1717
use cosmic::widget::menu::action::MenuAction;
1818
use cosmic::widget::menu::key_bind::KeyBind;
19-
use cosmic::widget::{self, DndDestination, DndSource, Id, RcElementWrapper, Widget, space};
19+
use cosmic::widget::{self, DndDestination, DndSource, Id, RcElementWrapper, Widget, space, icon};
2020
use cosmic::{Apply, Element, cosmic_theme, font, theme};
2121
use i18n_embed::LanguageLoader;
2222
use icu::datetime::input::DateTime;
@@ -81,6 +81,9 @@ const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32);
8181
const TEXT_PREVIEW_MAX_BYTES: usize = 256 * 1024; // 256 KiB
8282
/// Maximum file size (bytes) to attempt text preview; files larger than this are skipped entirely.
8383
const TEXT_PREVIEW_MAX_FILE_BYTES: u64 = 8 * 1000 * 1000; // 8 MiB
84+
// TODO: after creating "emblem-symbolic-link" icon in cosmic-icons need change this
85+
const SYMLINK_ICON_NAME: &str = "mail-forward-symbolic";
86+
8487

8588
// Thumbnail generation semaphore - limits parallel thumbnail workers
8689
// Uses 4 workers for balanced throughput and memory usage
@@ -708,6 +711,7 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS
708711

709712
let display_name = display_name_for_file(&path, &file_info.display_name(), false, is_desktop);
710713
let hidden = file_name.starts_with('.');
714+
let symlink = file_info.symlink_target();
711715

712716
Item {
713717
name: file_name.into(),
@@ -741,6 +745,7 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS
741745
dir_size,
742746
cut: false,
743747
checksums: ChecksumState::default(),
748+
is_symlink: symlink.is_some(),
744749
}
745750
}
746751

@@ -751,6 +756,13 @@ pub fn item_from_search_item(search_item: SearchItem, sizes: IconSizes) -> Item
751756
}
752757
}
753758

759+
pub fn check_symlink(path: &PathBuf) -> bool {
760+
fs::symlink_metadata(path)
761+
.ok()
762+
.filter(|meta| meta.is_symlink())
763+
.is_some()
764+
}
765+
754766
pub fn item_from_entry(
755767
path: PathBuf,
756768
name: String,
@@ -847,6 +859,8 @@ pub fn item_from_entry(
847859
}
848860

849861
let display_name = display_name_for_file(&path, &name, is_gvfs, is_desktop);
862+
let is_symlink = check_symlink(&path);
863+
let location_opt = Some(Location::Path(path));
850864

851865
Item {
852866
name,
@@ -857,7 +871,7 @@ pub fn item_from_entry(
857871
children_opt,
858872
},
859873
hidden,
860-
location_opt: Some(Location::Path(path)),
874+
location_opt: location_opt,
861875
image_dimensions: None,
862876
mime,
863877
icon_handle_grid,
@@ -873,6 +887,7 @@ pub fn item_from_entry(
873887
dir_size,
874888
cut: false,
875889
checksums: ChecksumState::default(),
890+
is_symlink: is_symlink,
876891
}
877892
}
878893

@@ -885,7 +900,9 @@ pub fn item_from_trash_entry(
885900
let name = entry.name.to_string_lossy().into_owned();
886901
let display_name = Item::display_name(&name);
887902

888-
let location = crate::trash::trash_item_path(&entry).map(Location::Path);
903+
let trash_path = crate::trash::trash_item_path(&entry);
904+
let is_symlink = trash_path.as_ref().map_or(false, |p| check_symlink(p));
905+
let location = trash_path.map(Location::Path);
889906

890907
let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) = match metadata.size
891908
{
@@ -932,6 +949,7 @@ pub fn item_from_trash_entry(
932949
dir_size: DirSize::NotDirectory,
933950
cut: false,
934951
checksums: ChecksumState::default(),
952+
is_symlink: is_symlink,
935953
}
936954
}
937955

@@ -1399,6 +1417,7 @@ pub fn scan_desktop(
13991417
dir_size: DirSize::NotDirectory,
14001418
cut: false,
14011419
checksums: ChecksumState::default(),
1420+
is_symlink: false,
14021421
});
14031422
}
14041423

@@ -2319,6 +2338,7 @@ pub struct Item {
23192338
pub overlaps_drag_rect: bool,
23202339
pub dir_size: DirSize,
23212340
pub checksums: ChecksumState,
2341+
pub is_symlink: bool,
23222342
}
23232343

23242344
impl Item {
@@ -2775,6 +2795,139 @@ impl fmt::Debug for SearchContextWrapper {
27752795
}
27762796
}
27772797

2798+
pub struct ListViewRowBuilder {
2799+
icon_size: u16,
2800+
row_height: u16,
2801+
modified_width: f32,
2802+
size_width: f32,
2803+
space_xxs: u16,
2804+
}
2805+
2806+
// TODO: Here are all the possible parameters for different list view modes. This is not ideal; it will require some refactoring in the future.
2807+
impl ListViewRowBuilder {
2808+
pub fn new(
2809+
icon_size: u16,
2810+
row_height: u16,
2811+
modified_width: f32,
2812+
size_width: f32,
2813+
space_xxs: u16,
2814+
) -> Self {
2815+
Self {
2816+
icon_size,
2817+
row_height,
2818+
modified_width,
2819+
size_width,
2820+
space_xxs,
2821+
}
2822+
}
2823+
2824+
fn condensed<'b>(
2825+
&self,
2826+
display_name: String,
2827+
icon_handle_list_condensed: widget::icon::Handle,
2828+
modified_text: String,
2829+
size_text: String,
2830+
is_symlink: bool,
2831+
) -> widget::Row<'b, Message, cosmic::prelude::Theme> {
2832+
let row_height = self.row_height;
2833+
let space_xxs = self.space_xxs;
2834+
let icon_size = self.icon_size;
2835+
let link_icon = icon::from_name(SYMLINK_ICON_NAME).size(icon_size).handle();
2836+
let link_icon_size = icon_size/2;
2837+
2838+
let item_name_widget = if is_symlink {
2839+
widget::row::with_children([
2840+
widget::icon::icon(link_icon)
2841+
.content_fit(ContentFit::Contain)
2842+
.width(Length::Fixed(link_icon_size as f32))
2843+
.height(Length::Fixed(link_icon_size as f32))
2844+
.size(icon_size)
2845+
.into(),
2846+
Item::list_display_name(display_name).into(),
2847+
])
2848+
} else {
2849+
widget::row::with_children([
2850+
Item::list_display_name(display_name).into(),
2851+
])
2852+
};
2853+
2854+
widget::row::with_children([
2855+
widget::icon::icon(icon_handle_list_condensed)
2856+
.content_fit(ContentFit::Contain)
2857+
.size(icon_size)
2858+
.into(),
2859+
widget::column::with_children([
2860+
item_name_widget
2861+
.align_y(Alignment::Center)
2862+
.into(),
2863+
widget::text::caption(format!("{modified_text} - {size_text}")).into(),
2864+
])
2865+
.into(),
2866+
])
2867+
.height(Length::Fixed(f32::from(row_height)))
2868+
.align_y(Alignment::Center)
2869+
.spacing(space_xxs)
2870+
.into()
2871+
}
2872+
2873+
pub fn wide<'b>(
2874+
&self,
2875+
display_name: String,
2876+
icon_handle_list: widget::icon::Handle,
2877+
modified_text: String,
2878+
size_text: String,
2879+
is_symlink: bool,
2880+
) -> widget::Row<'b, Message, cosmic::prelude::Theme> {
2881+
let icon_size = self.icon_size;
2882+
let modified_width = self.modified_width;
2883+
let size_width = self.size_width;
2884+
let row_height = self.row_height;
2885+
let space_xxs = self.space_xxs;
2886+
let link_icon = icon::from_name(SYMLINK_ICON_NAME).size(icon_size).handle();
2887+
let item_name_widget = if is_symlink {
2888+
widget::row::with_children([
2889+
widget::icon::icon(link_icon)
2890+
.content_fit(ContentFit::Contain)
2891+
.width(Length::Fixed(icon_size as f32))
2892+
.height(Length::Fixed(icon_size as f32))
2893+
.size(icon_size)
2894+
.into(),
2895+
Item::list_display_name(display_name)
2896+
.width(Length::Fill)
2897+
.into(),
2898+
])
2899+
} else {
2900+
widget::row::with_children([
2901+
Item::list_display_name(display_name)
2902+
.width(Length::Fill)
2903+
.into(),
2904+
])
2905+
};
2906+
2907+
widget::row::with_children([
2908+
widget::icon::icon(icon_handle_list.clone())
2909+
.content_fit(ContentFit::Contain)
2910+
.size(icon_size)
2911+
.into(),
2912+
item_name_widget
2913+
.spacing(self.space_xxs)
2914+
.align_y(Alignment::Center)
2915+
.width(Length::Fill)
2916+
.into(),
2917+
widget::text::body(modified_text.clone())
2918+
.width(Length::Fixed(modified_width))
2919+
.into(),
2920+
widget::text::body(size_text.clone())
2921+
.width(Length::Fixed(size_width))
2922+
.into(),
2923+
])
2924+
.height(Length::Fixed(f32::from(row_height)))
2925+
.align_y(Alignment::Center)
2926+
.spacing(space_xxs)
2927+
}
2928+
}
2929+
2930+
27782931
// TODO when creating items, pass <Arc<SelectedItems>> to each item
27792932
// as a drag data, so that when dnd is initiated, they are all included
27802933
pub struct Tab {
@@ -6085,6 +6238,11 @@ impl Tab {
60856238
if let Some(items) = self.column_sort() {
60866239
let mut count = 0;
60876240
let mut hidden = 0;
6241+
let row_builder = ListViewRowBuilder::new(icon_size.clone(),
6242+
row_height.clone(),
6243+
modified_width.clone(),
6244+
size_width.clone(),
6245+
space_xxs.clone());
60886246
for (i, item) in items {
60896247
if item.hidden && !show_hidden {
60906248
item.pos_opt.set(None);
@@ -6186,22 +6344,8 @@ impl Tab {
61866344
};
61876345

61886346
let row = if condensed {
6189-
widget::row::with_children([
6190-
widget::icon::icon(item.icon_handle_list_condensed.clone())
6191-
.content_fit(ContentFit::Contain)
6192-
.size(icon_size)
6193-
.into(),
6194-
widget::column::with_children([
6195-
Item::list_display_name(item.display_name.clone()).into(),
6196-
//TODO: translate?
6197-
widget::text::caption(format!("{modified_text} - {size_text}"))
6198-
.into(),
6199-
])
6200-
.into(),
6201-
])
6202-
.height(Length::Fixed(f32::from(row_height)))
6203-
.align_y(Alignment::Center)
6204-
.spacing(space_xxs)
6347+
row_builder.condensed(item.display_name.clone(), item.icon_handle_list_condensed.clone(),
6348+
modified_text.clone(), size_text.clone(), item.is_symlink)
62056349
} else if is_search {
62066350
widget::row::with_children([
62076351
widget::icon::icon(item.icon_handle_list_condensed.clone())
@@ -6229,24 +6373,11 @@ impl Tab {
62296373
.align_y(Alignment::Center)
62306374
.spacing(space_xxs)
62316375
} else {
6232-
widget::row::with_children([
6233-
widget::icon::icon(item.icon_handle_list.clone())
6234-
.content_fit(ContentFit::Contain)
6235-
.size(icon_size)
6236-
.into(),
6237-
Item::list_display_name(item.display_name.clone())
6238-
.width(Length::Fill)
6239-
.into(),
6240-
widget::text::body(modified_text.clone())
6241-
.width(Length::Fixed(modified_width))
6242-
.into(),
6243-
widget::text::body(size_text.clone())
6244-
.width(Length::Fixed(size_width))
6245-
.into(),
6246-
])
6247-
.height(Length::Fixed(f32::from(row_height)))
6248-
.align_y(Alignment::Center)
6249-
.spacing(space_xxs)
6376+
row_builder.wide(item.display_name.clone(),
6377+
item.icon_handle_list.clone(),
6378+
modified_text.clone(),
6379+
size_text.clone(),
6380+
item.is_symlink)
62506381
};
62516382

62526383
let button = |row| {

0 commit comments

Comments
 (0)