@@ -21,9 +21,7 @@ use cosmic::iced::wayland::actions::data_device::DndIcon;
21
21
use cosmic:: iced:: wayland:: actions:: window:: SctkWindowSettings ;
22
22
use cosmic:: iced:: wayland:: popup:: destroy_popup;
23
23
use cosmic:: iced:: wayland:: popup:: get_popup;
24
- use cosmic:: iced:: widget:: dnd_source;
25
- use cosmic:: iced:: widget:: mouse_listener;
26
- use cosmic:: iced:: widget:: { column, row} ;
24
+ use cosmic:: iced:: widget:: { column, dnd_source, mouse_listener, row, text, Column , Row } ;
27
25
use cosmic:: iced:: Settings ;
28
26
use cosmic:: iced:: { window, Application , Command , Subscription } ;
29
27
use cosmic:: iced_native:: alignment:: Horizontal ;
@@ -126,7 +124,7 @@ impl DockItem {
126
124
& self ,
127
125
applet_helper : & CosmicAppletHelper ,
128
126
rectangle_tracker : Option < & RectangleTracker < u32 > > ,
129
- has_popup : bool ,
127
+ interaction_enabled : bool ,
130
128
) -> Element < ' _ , Message > {
131
129
let DockItem {
132
130
toplevels,
@@ -182,23 +180,28 @@ impl DockItem {
182
180
let mut icon_button = cosmic:: widget:: button ( Button :: Text )
183
181
. custom ( vec ! [ icon_wrapper] )
184
182
. padding ( 8 ) ;
185
- if !has_popup {
186
- icon_button = icon_button. on_press (
187
- toplevels
188
- . first ( )
189
- . map ( |t| Message :: Activate ( t. 0 . clone ( ) ) )
190
- . unwrap_or_else ( || Message :: Exec ( desktop_info. exec . clone ( ) ) ) ,
191
- ) ;
192
- }
193
-
194
- // TODO tooltip on hover
195
- let icon_button = dnd_source (
196
- mouse_listener ( icon_button. width ( Length :: Shrink ) . height ( Length :: Shrink ) )
183
+ let icon_button = if interaction_enabled {
184
+ dnd_source (
185
+ mouse_listener (
186
+ icon_button
187
+ . on_press (
188
+ toplevels
189
+ . first ( )
190
+ . map ( |t| Message :: Activate ( t. 0 . clone ( ) ) )
191
+ . unwrap_or_else ( || Message :: Exec ( desktop_info. exec . clone ( ) ) ) ,
192
+ )
193
+ . width ( Length :: Shrink )
194
+ . height ( Length :: Shrink ) ,
195
+ )
197
196
. on_right_release ( Message :: Popup ( desktop_info. id . clone ( ) ) ) ,
198
- )
199
- . on_drag ( Message :: StartDrag ( * id) )
200
- . on_cancelled ( Message :: DragFinished )
201
- . on_finished ( Message :: DragFinished ) ;
197
+ )
198
+ . on_drag ( Message :: StartDrag ( desktop_info. id . clone ( ) ) )
199
+ . on_cancelled ( Message :: DragFinished )
200
+ . on_finished ( Message :: DragFinished )
201
+ } else {
202
+ dnd_source ( icon_button)
203
+ } ;
204
+
202
205
if let Some ( tracker) = rectangle_tracker {
203
206
tracker. container ( * id, icon_button) . into ( )
204
207
} else {
@@ -230,6 +233,7 @@ struct CosmicAppList {
230
233
rectangle_tracker : Option < RectangleTracker < u32 > > ,
231
234
rectangles : HashMap < u32 , iced:: Rectangle > ,
232
235
dnd_offer : Option < DndOffer > ,
236
+ is_listening_for_dnd : bool ,
233
237
}
234
238
235
239
// TODO DnD after sctk merges DnD
@@ -247,13 +251,15 @@ enum Message {
247
251
NewSeat ( WlSeat ) ,
248
252
RemovedSeat ( WlSeat ) ,
249
253
Rectangle ( RectangleUpdate < u32 > ) ,
250
- StartDrag ( u32 ) , // id of the DockItem
254
+ StartDrag ( String ) , // id of the DockItem
251
255
DragFinished ,
252
256
DndEnter ( f32 , f32 ) ,
253
257
DndExit ,
254
258
DndMotion ( f32 , f32 ) ,
255
259
DndDrop ,
256
260
DndData ( PathBuf ) ,
261
+ StartListeningForDnd ,
262
+ StopListeningForDnd ,
257
263
}
258
264
259
265
#[ derive( Debug , Clone , Default ) ]
@@ -304,24 +310,6 @@ fn desktop_info_for_app_ids(mut app_ids: Vec<String>) -> Vec<DesktopInfo> {
304
310
ret
305
311
}
306
312
307
- fn split_toplevel_favorites (
308
- toplevel_list : Vec < DockItem > ,
309
- existing_favorites : & mut Vec < DockItem > ,
310
- ) -> Vec < DockItem > {
311
- let mut active_list = Vec :: new ( ) ;
312
- for toplevel in toplevel_list {
313
- if let Some ( favorite) = existing_favorites. iter_mut ( ) . find ( |f| {
314
- f. desktop_info . name == toplevel. desktop_info . id
315
- || f. desktop_info . id == toplevel. desktop_info . id
316
- } ) {
317
- favorite. toplevels = toplevel. toplevels ;
318
- } else {
319
- active_list. push ( toplevel) ;
320
- }
321
- }
322
- active_list
323
- }
324
-
325
313
fn index_in_list (
326
314
mut list_len : usize ,
327
315
item_size : f32 ,
@@ -462,7 +450,6 @@ impl Application for CosmicAppList {
462
450
. iter ( )
463
451
. position ( |t| t. desktop_info . id == id)
464
452
{
465
- println ! ( "Removing favorite 2 {}" , id) ;
466
453
let entry = self . favorite_list . remove ( i) ;
467
454
self . rectangles . remove ( & entry. id ) ;
468
455
if !entry. toplevels . is_empty ( ) {
@@ -474,6 +461,9 @@ impl Application for CosmicAppList {
474
461
}
475
462
}
476
463
Message :: Activate ( handle) => {
464
+ if let Some ( p) = self . popup . take ( ) {
465
+ return destroy_popup ( p. 0 ) ;
466
+ }
477
467
if let ( Some ( tx) , Some ( seat) ) = ( self . toplevel_sender . as_ref ( ) , self . seat . as_ref ( ) )
478
468
{
479
469
let _ = tx. send ( ToplevelRequest :: Activate ( handle, seat. clone ( ) ) ) ;
@@ -501,14 +491,18 @@ impl Application for CosmicAppList {
501
491
. active_list
502
492
. iter ( )
503
493
. find_map ( |t| {
504
- if t. id == id {
494
+ if t. desktop_info . id == id {
505
495
Some ( ( false , t. clone ( ) ) )
506
496
} else {
507
497
None
508
498
}
509
499
} )
510
500
. or_else ( || {
511
- if let Some ( pos) = self . favorite_list . iter ( ) . position ( |t| t. id == id) {
501
+ if let Some ( pos) = self
502
+ . favorite_list
503
+ . iter ( )
504
+ . position ( |t| t. desktop_info . id == id)
505
+ {
512
506
let t = self . favorite_list . remove ( pos) ;
513
507
let _ = self . config . remove_favorite ( t. desktop_info . id . clone ( ) ) ;
514
508
Some ( ( true , t) )
@@ -763,11 +757,21 @@ impl Application for CosmicAppList {
763
757
return destroy_popup ( p. 0 ) ;
764
758
}
765
759
}
760
+ Message :: StartListeningForDnd => {
761
+ self . is_listening_for_dnd = true ;
762
+ }
763
+ Message :: StopListeningForDnd => {
764
+ self . is_listening_for_dnd = false ;
765
+ }
766
766
}
767
767
Command :: none ( )
768
768
}
769
769
770
770
fn view ( & self , id : window:: Id ) -> Element < Message > {
771
+ let is_horizontal = match self . applet_helper . anchor {
772
+ PanelAnchor :: Top | PanelAnchor :: Bottom => true ,
773
+ PanelAnchor :: Left | PanelAnchor :: Right => false ,
774
+ } ;
771
775
if let Some ( ( _, item, _) ) = self . dnd_source . as_ref ( ) . filter ( |s| s. 0 == id) {
772
776
return cosmic:: widget:: icon (
773
777
Path :: new ( & item. desktop_info . icon ) ,
@@ -847,7 +851,7 @@ impl Application for CosmicAppList {
847
851
dock_item. as_icon (
848
852
& self . applet_helper ,
849
853
self . rectangle_tracker . as_ref ( ) ,
850
- self . popup . is_some ( ) ,
854
+ self . popup . is_none ( ) ,
851
855
)
852
856
} )
853
857
. collect ( ) ;
@@ -858,68 +862,105 @@ impl Application for CosmicAppList {
858
862
. and_then ( |o| o. dock_item . as_ref ( ) . map ( |item| ( item, o. preview_index ) ) )
859
863
{
860
864
favorites. insert ( index, item. as_icon ( & self . applet_helper , None , false ) ) ;
865
+ } else if self . is_listening_for_dnd && self . favorite_list . is_empty ( ) {
866
+ // show star indicating favorite_list is drag target
867
+ favorites. push (
868
+ container ( cosmic:: widget:: icon (
869
+ "starred-symbolic.symbolic" ,
870
+ self . applet_helper . suggested_size ( ) . 0 ,
871
+ ) )
872
+ . padding ( 8 )
873
+ . into ( ) ,
874
+ ) ;
861
875
}
862
- let active = self
876
+
877
+ let active: Vec < _ > = self
863
878
. active_list
864
879
. iter ( )
865
880
. map ( |dock_item| {
866
881
dock_item. as_icon (
867
882
& self . applet_helper ,
868
883
self . rectangle_tracker . as_ref ( ) ,
869
- self . popup . is_some ( ) ,
884
+ self . popup . is_none ( ) ,
870
885
)
871
886
} )
872
887
. collect ( ) ;
873
888
874
- let ( w, h) = match self . applet_helper . anchor {
875
- PanelAnchor :: Top | PanelAnchor :: Bottom => ( Length :: Shrink , Length :: Fill ) ,
876
- PanelAnchor :: Left | PanelAnchor :: Right => ( Length :: Fill , Length :: Shrink ) ,
889
+ let ( w, h, favorites, active, divider) = if is_horizontal {
890
+ (
891
+ Length :: Fill ,
892
+ Length :: Shrink ,
893
+ dnd_listener ( row ( favorites) ) ,
894
+ row ( active) . into ( ) ,
895
+ vertical_rule ( 1 ) . into ( ) ,
896
+ )
897
+ } else {
898
+ (
899
+ Length :: Shrink ,
900
+ Length :: Fill ,
901
+ dnd_listener ( column ( favorites) ) ,
902
+ column ( active) . into ( ) ,
903
+ divider:: horizontal:: light ( ) . into ( ) ,
904
+ )
877
905
} ;
878
906
879
- let favorites = match self . applet_helper . anchor {
880
- PanelAnchor :: Left | PanelAnchor :: Right => dnd_listener ( column ( favorites) ) ,
881
- PanelAnchor :: Top | PanelAnchor :: Bottom => dnd_listener ( row ( favorites) ) ,
882
- }
883
- . on_enter ( |_actions, mime_types, location| {
884
- if mime_types. iter ( ) . any ( |m| m == MIME_TYPE ) {
885
- Message :: DndEnter ( location. 0 , location. 1 )
886
- } else {
887
- Message :: Ignore
888
- }
889
- } )
890
- . on_motion ( if self . dnd_offer . is_some ( ) {
891
- |x, y| Message :: DndMotion ( x, y)
892
- } else {
893
- |_, _| Message :: Ignore
894
- } )
895
- . on_exit ( Message :: DndExit )
896
- . on_drop ( Message :: DndDrop )
897
- . on_data ( |mime_type, data| {
898
- if mime_type == MIME_TYPE {
899
- if let Some ( p) = String :: from_utf8 ( data)
900
- . ok ( )
901
- . and_then ( |s| Url :: from_str ( & s) . ok ( ) )
902
- . and_then ( |u| u. to_file_path ( ) . ok ( ) )
903
- {
904
- Message :: DndData ( p)
907
+ let favorites = favorites
908
+ . on_enter ( |_actions, mime_types, location| {
909
+ if self . is_listening_for_dnd || mime_types. iter ( ) . any ( |m| m == MIME_TYPE ) {
910
+ Message :: DndEnter ( location. 0 , location. 1 )
905
911
} else {
906
912
Message :: Ignore
907
913
}
914
+ } )
915
+ . on_motion ( if self . dnd_offer . is_some ( ) {
916
+ |x, y| Message :: DndMotion ( x, y)
908
917
} else {
909
- Message :: Ignore
910
- }
911
- } ) ;
918
+ |_, _| Message :: Ignore
919
+ } )
920
+ . on_exit ( Message :: DndExit )
921
+ . on_drop ( Message :: DndDrop )
922
+ . on_data ( |mime_type, data| {
923
+ if mime_type == MIME_TYPE {
924
+ if let Some ( p) = String :: from_utf8 ( data)
925
+ . ok ( )
926
+ . and_then ( |s| Url :: from_str ( & s) . ok ( ) )
927
+ . and_then ( |u| u. to_file_path ( ) . ok ( ) )
928
+ {
929
+ Message :: DndData ( p)
930
+ } else {
931
+ Message :: Ignore
932
+ }
933
+ } else {
934
+ Message :: Ignore
935
+ }
936
+ } ) ;
937
+
938
+ let show_favorites =
939
+ !self . favorite_list . is_empty ( ) || self . dnd_offer . is_some ( ) || self . is_listening_for_dnd ;
940
+ let content_list: Vec < Element < _ > > = if show_favorites && !self . active_list . is_empty ( ) {
941
+ vec ! [ favorites. into( ) , divider, active]
942
+ } else if show_favorites {
943
+ vec ! [ favorites. into( ) ]
944
+ } else if !self . active_list . is_empty ( ) {
945
+ vec ! [ active]
946
+ } else {
947
+ vec ! [ cosmic:: widget:: icon(
948
+ "com.system76.CosmicAppList" ,
949
+ self . applet_helper. suggested_size( ) . 0 ,
950
+ )
951
+ . into( ) ]
952
+ } ;
912
953
913
954
let content = match & self . applet_helper . anchor {
914
955
PanelAnchor :: Left | PanelAnchor :: Right => container (
915
- column ! [ favorites , divider :: horizontal :: light ( ) , column ( active ) ]
956
+ Column :: with_children ( content_list )
916
957
. spacing ( 4 )
917
958
. align_items ( Alignment :: Center )
918
959
. height ( h)
919
960
. width ( w) ,
920
961
) ,
921
962
PanelAnchor :: Top | PanelAnchor :: Bottom => container (
922
- row ! [ favorites , vertical_rule ( 1 ) , row ( active ) ]
963
+ Row :: with_children ( content_list )
923
964
. spacing ( 4 )
924
965
. align_items ( Alignment :: Center )
925
966
. height ( h)
@@ -928,7 +969,7 @@ impl Application for CosmicAppList {
928
969
} ;
929
970
if self . popup . is_some ( ) {
930
971
mouse_listener ( content)
931
- . on_right_press ( Message :: ClosePopup )
972
+ . on_right_release ( Message :: ClosePopup )
932
973
. on_press ( Message :: ClosePopup )
933
974
. into ( )
934
975
} else {
@@ -962,6 +1003,30 @@ impl Application for CosmicAppList {
962
1003
) ,
963
1004
) ,
964
1005
) => Some ( Message :: DragFinished ) ,
1006
+ cosmic:: iced_native:: Event :: PlatformSpecific (
1007
+ cosmic:: iced_native:: event:: PlatformSpecific :: Wayland (
1008
+ cosmic:: iced_native:: event:: wayland:: Event :: DndOffer (
1009
+ cosmic:: iced_native:: event:: wayland:: DndOfferEvent :: Enter {
1010
+ mime_types,
1011
+ ..
1012
+ } ,
1013
+ ) ,
1014
+ ) ,
1015
+ ) => {
1016
+ if mime_types. iter( ) . any( |m| m == MIME_TYPE ) {
1017
+ Some ( Message :: StartListeningForDnd )
1018
+ } else {
1019
+ None
1020
+ }
1021
+ }
1022
+ cosmic:: iced_native:: Event :: PlatformSpecific (
1023
+ cosmic:: iced_native:: event:: PlatformSpecific :: Wayland (
1024
+ cosmic:: iced_native:: event:: wayland:: Event :: DndOffer (
1025
+ cosmic:: iced_native:: event:: wayland:: DndOfferEvent :: Leave
1026
+ | cosmic:: iced_native:: event:: wayland:: DndOfferEvent :: DropPerformed ,
1027
+ ) ,
1028
+ ) ,
1029
+ ) => Some ( Message :: StopListeningForDnd ) ,
965
1030
_ => None ,
966
1031
} ) ,
967
1032
rectangle_tracker_subscription( 0 ) . map( |( _, update) | Message :: Rectangle ( update) ) ,
0 commit comments