@@ -16,7 +16,7 @@ use cosmic::iced::{
1616} ;
1717use cosmic:: widget:: menu:: action:: MenuAction ;
1818use 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 } ;
2020use cosmic:: { Apply , Element , cosmic_theme, font, theme} ;
2121use i18n_embed:: LanguageLoader ;
2222use icu:: datetime:: input:: DateTime ;
@@ -81,6 +81,9 @@ const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32);
8181const 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.
8383const 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+
754766pub 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
23242344impl 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
27802933pub 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