2021-12-30 19:01:59 -05:00
use cascade ::cascade ;
2021-12-27 17:59:49 -05:00
use gdk4 ::ContentProvider ;
use gdk4 ::Display ;
2021-12-30 15:35:10 -05:00
use gdk4 ::ModifierType ;
2021-12-14 11:53:18 -05:00
use gdk4 ::Rectangle ;
2021-12-24 18:37:19 -05:00
use gdk4_x11 ::X11Display ;
2021-12-14 11:53:18 -05:00
use gdk4_x11 ::X11Surface ;
2021-12-15 11:37:28 -05:00
use gio ::DesktopAppInfo ;
2021-12-27 17:59:49 -05:00
use gio ::Icon ;
2021-12-29 17:31:01 -05:00
use glib ::Object ;
2021-12-16 17:14:44 -05:00
use glib ::Type ;
2021-12-30 17:53:06 -05:00
use gtk4 ::prelude ::ListModelExt ;
use gtk4 ::prelude ::* ;
use gtk4 ::subclass ::prelude ::* ;
2021-12-30 22:23:17 -05:00
use gtk4 ::Align ;
2021-12-30 19:01:59 -05:00
use gtk4 ::Box ;
2021-12-16 09:38:20 -05:00
use gtk4 ::DropTarget ;
2021-12-14 16:36:28 -05:00
use gtk4 ::EventControllerMotion ;
2021-12-27 17:59:49 -05:00
use gtk4 ::IconTheme ;
2021-12-30 22:25:39 -05:00
use gtk4 ::ListView ;
2021-12-30 19:01:59 -05:00
use gtk4 ::Orientation ;
2021-12-30 22:25:39 -05:00
use gtk4 ::Revealer ;
use gtk4 ::RevealerTransitionType ;
use gtk4 ::Separator ;
2021-12-30 17:53:06 -05:00
use gtk4 ::{ gio , glib } ;
use gtk4 ::{ Application , SignalListItemFactory } ;
use gtk4 ::{ DragSource , GestureClick } ;
2021-12-29 17:31:01 -05:00
use postage ::prelude ::Sink ;
2021-12-30 22:25:39 -05:00
use std ::fs ::File ;
use std ::path ::Path ;
2021-12-29 17:31:01 -05:00
use x11rb ::connection ::Connection ;
2021-12-14 11:53:18 -05:00
use x11rb ::protocol ::xproto ;
2021-12-29 17:31:01 -05:00
use x11rb ::protocol ::xproto ::ConnectionExt ;
use libcosmic ::x ;
use crate ::BoxedWindowList ;
// use crate::ApplicationObject;
use crate ::dock_item ::DockItem ;
use crate ::dock_object ::DockObject ;
2021-12-30 17:53:06 -05:00
use crate ::utils ::data_path ;
2021-12-29 17:31:01 -05:00
use crate ::Event ;
2021-12-30 16:54:35 -05:00
use crate ::Item ;
2021-12-29 17:31:01 -05:00
use crate ::TX ;
use crate ::X11_CONN ;
mod imp ;
2021-12-14 11:53:18 -05:00
glib ::wrapper! {
pub struct Window ( ObjectSubclass < imp ::Window > )
2021-12-30 16:54:35 -05:00
@ extends gtk4 ::ApplicationWindow , gtk4 ::Window , gtk4 ::Widget ,
2021-12-30 19:01:59 -05:00
@ implements gio ::ActionGroup , gio ::ActionMap , gtk4 ::Accessible , gtk4 ::Buildable , gtk4 ::ConstraintTarget , gtk4 ::Native , gtk4 ::Root , gtk4 ::ShortcutManager ;
2021-12-14 11:53:18 -05:00
}
impl Window {
pub fn new ( app : & Application ) -> Self {
let self_ : Self = Object ::new ( & [ ( " application " , app ) ] ) . expect ( " Failed to create `Window`. " ) ;
2021-12-30 19:01:59 -05:00
let imp = imp ::Window ::from_instance ( & self_ ) ;
cascade! {
& self_ ;
2021-12-30 22:23:17 -05:00
.. set_height_request ( 100 ) ;
2021-12-30 19:01:59 -05:00
.. set_width_request ( 128 ) ;
.. set_title ( Some ( " Cosmic Dock " ) ) ;
.. set_decorated ( false ) ;
.. set_resizable ( false ) ;
} ;
let cursor_handle = Box ::new ( Orientation ::Vertical , 0 ) ;
self_ . set_child ( Some ( & cursor_handle ) ) ;
let window_filler = cascade! {
Box ::new ( Orientation ::Vertical , 0 ) ;
.. set_height_request ( 0 ) ; // shrinks to nothing when revealer is shown
.. set_vexpand ( true ) ; // expands to fill window when revealer is hidden, preventingb window from changing size so much...
} ;
cursor_handle . append ( & window_filler ) ;
let revealer = cascade! {
Revealer ::new ( ) ;
.. set_reveal_child ( true ) ;
2021-12-30 22:23:17 -05:00
.. set_valign ( Align ::Baseline ) ;
2021-12-31 14:58:26 -05:00
.. set_transition_duration ( 150 ) ;
.. set_transition_type ( RevealerTransitionType ::SwingUp ) ;
2021-12-30 19:01:59 -05:00
} ;
cursor_handle . append ( & revealer ) ;
let dock = cascade! {
Box ::new ( Orientation ::Horizontal , 4 ) ;
.. set_margin_start ( 4 ) ;
.. set_margin_end ( 4 ) ;
2021-12-30 22:23:17 -05:00
.. set_margin_bottom ( 4 ) ;
2021-12-30 19:01:59 -05:00
} ;
2021-12-30 22:23:17 -05:00
dock . add_css_class ( " dock " ) ;
revealer . set_child ( Some ( & dock ) ) ;
2021-12-30 19:01:59 -05:00
let saved_app_list_view = cascade! {
ListView ::builder ( ) . build ( ) ;
.. set_orientation ( Orientation ::Horizontal ) ;
2021-12-30 22:23:17 -05:00
.. set_width_request ( 64 ) ;
2021-12-30 19:01:59 -05:00
} ;
dock . append ( & saved_app_list_view ) ;
let separator = cascade! {
Separator ::new ( Orientation ::Vertical ) ;
2021-12-30 22:23:17 -05:00
.. set_margin_start ( 8 ) ;
.. set_margin_end ( 8 ) ;
2021-12-30 19:01:59 -05:00
} ;
dock . append ( & separator ) ;
let active_app_list_view = cascade! {
2021-12-31 14:58:26 -05:00
ListView ::default ( ) ;
2021-12-30 19:01:59 -05:00
.. set_orientation ( Orientation ::Horizontal ) ;
} ;
dock . append ( & active_app_list_view ) ;
imp . cursor_handle . set ( cursor_handle ) . unwrap ( ) ;
imp . revealer . set ( revealer ) . unwrap ( ) ;
imp . saved_app_list_view . set ( saved_app_list_view ) . unwrap ( ) ;
imp . active_app_list_view . set ( active_app_list_view ) . unwrap ( ) ;
// Setup
self_ . setup_model ( ) ;
self_ . setup_motion_controller ( ) ;
self_ . setup_click_controller ( ) ;
self_ . setup_drop_target ( ) ;
self_ . setup_drag_source ( ) ;
self_ . restore_saved_apps ( ) ;
self_ . setup_callbacks ( ) ;
self_ . setup_click_callbacks ( ) ;
self_ . setup_factory ( ) ;
// obj.setup_window_callbacks();
// obj.setup_saved_list_callbacks();
// obj.setup_active_list_callbacks();
// obj.setup_drag_callbacks();
2021-12-14 11:53:18 -05:00
self_
}
2021-12-15 14:03:23 -05:00
pub fn saved_app_model ( & self ) -> & gio ::ListStore {
2021-12-14 11:53:18 -05:00
// Get state
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-15 14:03:23 -05:00
imp . saved_app_model
. get ( )
. expect ( " Could not get saved_app_model " )
2021-12-14 11:53:18 -05:00
}
2021-12-21 13:01:32 -05:00
pub fn active_app_model ( & self ) -> & gio ::ListStore {
// Get state
let imp = imp ::Window ::from_instance ( self ) ;
imp . active_app_model
. get ( )
. expect ( " Could not get active_app_model " )
}
2021-12-14 11:53:18 -05:00
fn setup_model ( & self ) {
// Get state and set model
2021-12-15 11:37:28 -05:00
2021-12-14 11:53:18 -05:00
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-20 23:52:59 -05:00
let saved_app_model = gio ::ListStore ::new ( DockObject ::static_type ( ) ) ;
2021-12-15 11:37:28 -05:00
2021-12-30 16:54:35 -05:00
let saved_selection_model = gtk4 ::NoSelection ::new ( Some ( & saved_app_model ) ) ;
2021-12-15 11:37:28 -05:00
2021-12-15 14:03:23 -05:00
imp . saved_app_model
. set ( saved_app_model )
. expect ( " Could not set model " ) ;
2021-12-15 11:37:28 -05:00
// Wrap model with selection and pass it to the list view
2021-12-30 19:01:59 -05:00
let saved_app_list_view = imp . saved_app_list_view . get ( ) . unwrap ( ) ;
saved_app_list_view . set_model ( Some ( & saved_selection_model ) ) ;
2021-12-21 13:01:32 -05:00
let active_app_model = gio ::ListStore ::new ( DockObject ::static_type ( ) ) ;
2021-12-30 16:54:35 -05:00
let active_selection_model = gtk4 ::NoSelection ::new ( Some ( & active_app_model ) ) ;
2021-12-21 13:01:32 -05:00
imp . active_app_model
. set ( active_app_model )
. expect ( " Could not set model " ) ;
// Wrap model with selection and pass it to the list view
2021-12-30 19:01:59 -05:00
let active_app_list_view = imp . active_app_list_view . get ( ) . unwrap ( ) ;
active_app_list_view . set_model ( Some ( & active_selection_model ) ) ;
2021-12-14 11:53:18 -05:00
}
fn setup_callbacks ( & self ) {
// Get state
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-30 16:54:35 -05:00
let window = self . clone ( ) . upcast ::< gtk4 ::Window > ( ) ;
2021-12-30 19:01:59 -05:00
let saved_app_list_view = & imp . saved_app_list_view . get ( ) . unwrap ( ) ;
2021-12-16 09:38:20 -05:00
let saved_app_model = & imp
. saved_app_model
. get ( )
. expect ( " Failed to get saved app model " ) ;
2021-12-15 11:37:28 -05:00
2021-12-30 15:35:10 -05:00
let cursor_event_controller = & imp . cursor_motion_controller . get ( ) . unwrap ( ) ;
2021-12-16 09:38:20 -05:00
let drop_controller = & imp . drop_controller . get ( ) . unwrap ( ) ;
2021-12-17 12:22:45 -05:00
let window_drop_controller = & imp . window_drop_controller . get ( ) . unwrap ( ) ;
2021-12-30 19:01:59 -05:00
let revealer = & imp . revealer . get ( ) . unwrap ( ) ;
2021-12-14 16:36:28 -05:00
window . connect_show (
2021-12-22 17:23:18 -05:00
glib ::clone! ( @ weak revealer , @ weak cursor_event_controller = > move | _ | {
2021-12-23 12:55:55 -05:00
// dbg!(!cursor_event_controller.contains_pointer());
2021-12-22 17:23:18 -05:00
if ! cursor_event_controller . contains_pointer ( ) {
2021-12-17 12:22:45 -05:00
revealer . set_reveal_child ( false ) ;
2021-12-14 16:36:28 -05:00
}
} ) ,
) ;
2021-12-27 17:59:49 -05:00
window . connect_realize ( glib ::clone! ( @ weak revealer , @ weak window_drop_controller , @ weak cursor_event_controller = > move | window | {
2021-12-14 11:53:18 -05:00
if let Some ( ( display , surface ) ) = x ::get_window_x11 ( window ) {
2021-12-24 18:37:19 -05:00
// ignore all x11 errors...
let xdisplay = display . clone ( ) . downcast ::< X11Display > ( ) . expect ( " Failed to downgrade X11 Display. " ) ;
xdisplay . error_trap_push ( ) ;
2021-12-29 17:31:01 -05:00
unsafe {
x ::change_property (
& display ,
& surface ,
" _NET_WM_WINDOW_TYPE " ,
x ::PropMode ::Replace ,
& [ x ::Atom ::new ( & display , " _NET_WM_WINDOW_TYPE_DOCK " ) . unwrap ( ) ] ,
) ;
}
2021-12-27 17:59:49 -05:00
let resize = glib ::clone! ( @ weak window , @ weak revealer = > move | | {
let s = window . surface ( ) . expect ( " Failed to get Surface for Window " ) ;
let height = if revealer . reveals_child ( ) { window . height ( ) } else { 4 } ;
let width = window . width ( ) ;
if let Some ( ( display , _surface ) ) = x ::get_window_x11 ( & window ) {
let monitor = display
. primary_monitor ( )
. expect ( " Failed to get Monitor " ) ;
let Rectangle {
x : monitor_x ,
y : monitor_y ,
width : monitor_width ,
height : monitor_height ,
} = monitor . geometry ( ) ;
2021-12-29 17:31:01 -05:00
// dbg!(monitor_x);
// dbg!(monitor_y);
2021-12-27 17:59:49 -05:00
// dbg!(monitor_width);
// dbg!(monitor_height);
// dbg!(width);
2021-12-27 18:33:17 -05:00
// dbg!(height);
2021-12-27 17:59:49 -05:00
let w_conf = xproto ::ConfigureWindowAux ::default ( )
. x ( ( monitor_x + monitor_width / 2 - width / 2 ) . clamp ( 0 , monitor_x + monitor_width - 1 ) )
. y ( ( monitor_y + monitor_height - height ) . clamp ( 0 , monitor_y + monitor_height - 1 ) ) ;
let conn = X11_CONN . get ( ) . expect ( " Failed to get X11_CONN " ) ;
let x11surface = gdk4_x11 ::X11Surface ::xid (
& s . clone ( ) . downcast ::< X11Surface > ( )
. expect ( " Failed to downcast Surface to X11Surface " ) ,
) ;
conn . configure_window (
x11surface . try_into ( ) . expect ( " Failed to convert XID " ) ,
& w_conf ,
)
. expect ( " failed to configure window... " ) ;
conn . flush ( ) . expect ( " failed to flush " ) ;
}
} ) ;
2021-12-22 17:23:18 -05:00
2021-12-27 17:59:49 -05:00
let resize_drop = resize . clone ( ) ;
window_drop_controller . connect_enter ( glib ::clone! ( @ weak revealer , @ weak window = > @ default - return gdk4 ::DragAction ::COPY , move | _self , _x , _y | {
glib ::source ::idle_add_local_once ( resize_drop . clone ( ) ) ;
revealer . set_reveal_child ( true ) ;
gdk4 ::DragAction ::COPY
} ) ) ;
let resize_cursor = resize . clone ( ) ;
2021-12-22 17:23:18 -05:00
cursor_event_controller . connect_enter ( glib ::clone! ( @ weak revealer , @ weak window = > move | _evc , _x , _y | {
2021-12-23 12:55:55 -05:00
// dbg!("hello, mouse entered me :)");
2021-12-27 17:59:49 -05:00
revealer . set_reveal_child ( true ) ;
glib ::source ::idle_add_local_once ( resize_cursor . clone ( ) ) ;
2021-12-22 17:23:18 -05:00
} ) ) ;
2021-12-27 17:59:49 -05:00
let resize_revealed = resize . clone ( ) ;
2021-12-23 13:49:11 -05:00
revealer . connect_child_revealed_notify ( glib ::clone! ( @ weak window = > move | r | {
if ! r . is_child_revealed ( ) {
2021-12-27 17:59:49 -05:00
glib ::source ::idle_add_local_once ( resize_revealed . clone ( ) ) ;
}
2021-12-23 13:49:11 -05:00
} ) ) ;
2021-12-14 12:12:17 -05:00
let s = window . surface ( ) . expect ( " Failed to get Surface for Window " ) ;
2021-12-27 17:59:49 -05:00
let resize_height = resize . clone ( ) ;
s . connect_height_notify ( move | _s | {
glib ::source ::idle_add_local_once ( resize_height . clone ( ) ) ;
} ) ;
let resize_width = resize . clone ( ) ;
s . connect_width_notify ( move | _s | {
glib ::source ::idle_add_local_once ( resize_width . clone ( ) ) ;
} ) ;
s . connect_scale_factor_notify ( move | _s | {
glib ::source ::idle_add_local_once ( resize . clone ( ) ) ;
2021-12-14 12:12:17 -05:00
} ) ;
2021-12-14 11:53:18 -05:00
} else {
println! ( " failed to get X11 window " ) ;
}
2021-12-14 16:36:28 -05:00
} ) ) ;
2021-12-22 17:23:18 -05:00
cursor_event_controller . connect_leave (
2021-12-17 12:22:45 -05:00
glib ::clone! ( @ weak revealer , @ weak drop_controller = > move | _evc | {
// only hide if DnD is not happening
if drop_controller . current_drop ( ) . is_none ( ) {
2021-12-23 12:55:55 -05:00
// dbg!("hello, mouse left me :)");
2021-12-17 12:22:45 -05:00
revealer . set_reveal_child ( false ) ;
}
} ) ,
) ;
2021-12-16 09:38:20 -05:00
2021-12-17 12:22:45 -05:00
drop_controller . connect_enter ( glib ::clone! ( @ weak revealer = > @ default - return gdk4 ::DragAction ::COPY , move | _self , _x , _y | {
2021-12-22 17:23:18 -05:00
2021-12-17 12:22:45 -05:00
revealer . set_reveal_child ( true ) ;
gdk4 ::DragAction ::COPY
} ) ) ;
2021-12-27 17:59:49 -05:00
window_drop_controller . connect_drop ( | _ , _ , _ , _ | {
println! ( " dropping into window " ) ;
false
} ) ;
2021-12-16 17:14:44 -05:00
2021-12-27 17:59:49 -05:00
// drag end handler
// must be modified in case of reorder...
2021-12-27 20:19:59 -05:00
let drag_end = & imp . saved_drag_end_signal ;
2021-12-27 17:59:49 -05:00
let saved_drag_source = & imp . saved_drag_source ;
2021-12-16 09:38:20 -05:00
drop_controller . connect_drop (
2021-12-27 17:59:49 -05:00
glib ::clone! ( @ weak saved_app_model , @ weak saved_app_list_view , @ weak drag_end , @ weak saved_drag_source = > @ default - return true , move | _self , drop_value , x , _y | {
2021-12-27 20:19:59 -05:00
//calculate insertion location
// dbg!(x);
// dbg!(y);
let max_x = saved_app_list_view . allocated_width ( ) ;
// dbg!(max_x);
// dbg!(max_y);
let n_buckets = saved_app_model . n_items ( ) * 2 ;
let drop_bucket = ( x * n_buckets as f64 / ( max_x as f64 + 0.1 ) ) as u32 ;
let index = if drop_bucket = = 0 {
0
} else if drop_bucket = = n_buckets - 1 {
saved_app_model . n_items ( )
} else {
( drop_bucket + 1 ) / 2
} ;
2021-12-16 17:14:44 -05:00
if let Ok ( Some ( path_str ) ) = drop_value . get ::< Option < String > > ( ) {
let desktop_path = & Path ::new ( & path_str ) ;
if let Some ( pathbase ) = desktop_path . file_name ( ) {
if let Some ( app_info ) = gio ::DesktopAppInfo ::new ( & pathbase . to_string_lossy ( ) ) {
2021-12-27 17:59:49 -05:00
// remove item if already exists
2021-12-16 17:14:44 -05:00
let mut i : u32 = 0 ;
let mut index_of_existing_app : Option < u32 > = None ;
while let Some ( item ) = saved_app_model . item ( i ) {
2021-12-20 23:52:59 -05:00
if let Ok ( cur_app_info ) = item . downcast ::< DockObject > ( ) {
if let Ok ( Some ( cur_app_info ) ) = cur_app_info . property ( " appinfo " ) . expect ( " property appinfo missing from DockObject " ) . get ::< Option < DesktopAppInfo > > ( ) {
2021-12-23 12:55:55 -05:00
// dbg!(cur_app_info.filename());
2021-12-20 23:52:59 -05:00
if cur_app_info . filename ( ) = = Some ( Path ::new ( & path_str ) . to_path_buf ( ) ) {
index_of_existing_app = Some ( i ) ;
}
2021-12-16 17:14:44 -05:00
}
}
i + = 1 ;
}
2021-12-23 12:55:55 -05:00
// dbg!(app_info.name());
// dbg!(index_of_existing_app);
2021-12-16 17:14:44 -05:00
if let Some ( index_of_existing_app ) = index_of_existing_app {
// remove existing entry
saved_app_model . remove ( index_of_existing_app ) ;
2021-12-27 17:59:49 -05:00
if let Some ( old_handle ) = drag_end . replace ( None ) {
glib ::signal_handler_disconnect ( saved_drag_source . get ( ) . expect ( " Failed to get drag handler " ) , old_handle ) ;
}
2021-12-16 17:14:44 -05:00
}
2021-12-23 12:55:55 -05:00
// dbg!(index);
// dbg!("dropped it!");
// dbg!(drop_value.type_());
2021-12-20 23:52:59 -05:00
saved_app_model . insert ( index , & DockObject ::new ( app_info ) ) ;
2021-12-16 09:38:20 -05:00
}
}
}
2021-12-27 20:19:59 -05:00
else if let Ok ( old_index ) = drop_value . get ::< u32 > ( ) {
if let Some ( item ) = saved_app_model . item ( old_index ) {
if let Ok ( dock_object ) = item . downcast ::< DockObject > ( ) {
saved_app_model . remove ( old_index ) ;
saved_app_model . insert ( index , & dock_object ) ;
if let Some ( old_handle ) = drag_end . replace ( None ) {
glib ::signal_handler_disconnect ( saved_drag_source . get ( ) . expect ( " Failed to get drag handler " ) , old_handle ) ;
}
}
}
}
2021-12-16 09:38:20 -05:00
else {
2021-12-23 12:55:55 -05:00
// dbg!("rejecting drop");
2021-12-16 09:38:20 -05:00
_self . reject ( ) ;
}
2021-12-27 20:19:59 -05:00
glib ::MainContext ::default ( ) . spawn_local ( async move {
if let Some ( tx ) = TX . get ( ) {
let mut tx = tx . clone ( ) ;
let _ = tx . send ( Event ::RefreshFromCache ) . await ;
}
} ) ;
2021-12-16 09:38:20 -05:00
true
} ) ,
) ;
2021-12-27 17:59:49 -05:00
saved_app_model . connect_items_changed ( | saved_app_model , _ , _removed , _added | {
Self ::store_saved_apps ( saved_app_model ) ;
} ) ;
2021-12-14 16:36:28 -05:00
}
2021-12-30 15:35:10 -05:00
fn setup_motion_controller ( & self ) {
2021-12-14 16:36:28 -05:00
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-30 19:01:59 -05:00
let handle = & imp . cursor_handle . get ( ) . unwrap ( ) ;
2021-12-22 17:23:18 -05:00
let ev = EventControllerMotion ::builder ( )
2021-12-15 12:19:01 -05:00
. propagation_limit ( gtk4 ::PropagationLimit ::None )
. propagation_phase ( gtk4 ::PropagationPhase ::Capture )
. build ( ) ;
2021-12-22 17:23:18 -05:00
handle . add_controller ( & ev ) ;
2021-12-30 15:35:10 -05:00
imp . cursor_motion_controller
2021-12-22 17:23:18 -05:00
. set ( ev )
2021-12-14 16:36:28 -05:00
. expect ( " Could not set event controller " ) ;
2021-12-14 11:53:18 -05:00
}
2021-12-30 15:35:10 -05:00
fn setup_click_controller ( & self ) {
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-30 19:01:59 -05:00
let saved_app_list_view = & imp . saved_app_list_view . get ( ) . unwrap ( ) ;
2021-12-30 15:35:10 -05:00
let controller = GestureClick ::builder ( )
. button ( 0 )
. propagation_limit ( gtk4 ::PropagationLimit ::None )
. propagation_phase ( gtk4 ::PropagationPhase ::Capture )
. build ( ) ;
saved_app_list_view . add_controller ( & controller ) ;
imp . saved_click_controller
. set ( controller )
. expect ( " Could not set event controller " ) ;
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-30 19:01:59 -05:00
let active_app_list_view = & imp . active_app_list_view . get ( ) . unwrap ( ) ;
2021-12-30 15:35:10 -05:00
let controller = GestureClick ::builder ( )
. button ( 0 )
. propagation_limit ( gtk4 ::PropagationLimit ::None )
. propagation_phase ( gtk4 ::PropagationPhase ::Capture )
. build ( ) ;
active_app_list_view . add_controller ( & controller ) ;
imp . active_click_controller
. set ( controller )
. expect ( " Could not set event controller " ) ;
}
fn setup_click_callbacks ( & self ) {
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-30 16:54:35 -05:00
let window = self . clone ( ) . upcast ::< gtk4 ::Window > ( ) ;
2021-12-30 15:35:10 -05:00
let saved_click_controller = imp
. saved_click_controller
. get ( )
. expect ( " Failed to get saved click controller " ) ;
let saved_app_model = imp
. saved_app_model
. get ( )
. expect ( " Failed to get saved_app_model " ) ;
2021-12-30 19:01:59 -05:00
let saved_app_list_view = & imp . saved_app_list_view . get ( ) . unwrap ( ) ;
2021-12-30 15:35:10 -05:00
saved_click_controller . connect_released ( glib ::clone! ( @ weak saved_app_model , @ weak saved_app_list_view , @ weak window = > move | self_ , _ , x , _y | {
let max_x = saved_app_list_view . allocated_width ( ) ;
let n_buckets = saved_app_model . n_items ( ) ;
let index = ( x * n_buckets as f64 / ( max_x as f64 + 0.1 ) ) as u32 ;
dbg! ( self_ . current_button ( ) ) ;
dbg! ( self_ . last_event ( self_ . current_sequence ( ) . as_ref ( ) ) ) ;
let click_modifier = if let Some ( event ) = self_ . last_event ( self_ . current_sequence ( ) . as_ref ( ) ) {
dbg! ( & event ) ;
Some ( event . modifier_state ( ) )
}
else {
None
} ;
dbg! ( click_modifier ) ;
// Launch the application when an item of the list is activated
let focus_window = move | first_focused_item : & Item | {
let entity = first_focused_item . entity . clone ( ) ;
glib ::MainContext ::default ( ) . spawn_local ( async move {
if let Some ( tx ) = TX . get ( ) {
let mut tx = tx . clone ( ) ;
let _ = tx . send ( Event ::Activate ( entity ) ) . await ;
}
} ) ;
} ;
if let Some ( item ) = saved_app_model . item ( index ) {
if let Ok ( dock_object ) = item . downcast ::< DockObject > ( ) {
let active = dock_object . property ( " active " ) . expect ( " DockObject must have active property " ) . get ::< BoxedWindowList > ( ) . expect ( " Failed to convert value to WindowList " ) ;
let app_info = dock_object . property ( " appinfo " ) . expect ( " DockObject must have appinfo property " ) . get ::< Option < DesktopAppInfo > > ( ) . expect ( " Failed to convert value to DesktopAppInfo " ) ;
match ( self_ . current_button ( ) , click_modifier , active . 0. iter ( ) . next ( ) , app_info ) {
( click , Some ( click_modifier ) , Some ( first_focused_item ) , _ ) if click = = 1 & & ! click_modifier . contains ( ModifierType ::CONTROL_MASK ) = > focus_window ( first_focused_item ) ,
( click , None , Some ( first_focused_item ) , _ ) if click = = 1 = > focus_window ( first_focused_item ) ,
( click , _ , _ , Some ( app_info ) ) | ( click , _ , None , Some ( app_info ) ) if click ! = 3 = > {
let context = window . display ( ) . app_launch_context ( ) ;
if let Err ( err ) = app_info . launch ( & [ ] , Some ( & context ) ) {
2021-12-30 16:54:35 -05:00
gtk4 ::MessageDialog ::builder ( )
2021-12-30 15:35:10 -05:00
. text ( & format! ( " Failed to start {} " , app_info . name ( ) ) )
. secondary_text ( & err . to_string ( ) )
2021-12-30 16:54:35 -05:00
. message_type ( gtk4 ::MessageType ::Error )
2021-12-30 15:35:10 -05:00
. modal ( true )
. transient_for ( & window )
. build ( )
. show ( ) ;
}
}
( click , _ , _ , _ ) if click = = 3 = > {
println! ( " handling right click " ) ;
}
_ = > println! ( " Failed to process click. " )
}
}
}
} ) ) ;
}
2021-12-16 09:38:20 -05:00
fn setup_drop_target ( & self ) {
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-30 19:01:59 -05:00
let drop_target_widget = & imp . saved_app_list_view . get ( ) . unwrap ( ) ;
2021-12-27 17:59:49 -05:00
let mut drop_actions = gdk4 ::DragAction ::COPY ;
drop_actions . insert ( gdk4 ::DragAction ::MOVE ) ;
2021-12-16 17:14:44 -05:00
let drop_format = gdk4 ::ContentFormats ::for_type ( Type ::STRING ) ;
2021-12-27 20:19:59 -05:00
let drop_format = drop_format
. union ( & gdk4 ::ContentFormats ::for_type ( Type ::U32 ) )
. expect ( " couldn't make union " ) ;
2021-12-16 09:38:20 -05:00
let drop_target_controller = DropTarget ::builder ( )
. preload ( true )
. actions ( drop_actions )
2021-12-16 17:14:44 -05:00
. formats ( & drop_format )
2021-12-16 09:38:20 -05:00
. build ( ) ;
drop_target_widget . add_controller ( & drop_target_controller ) ;
imp . drop_controller
. set ( drop_target_controller )
. expect ( " Could not set dock dnd drop controller " ) ;
2021-12-17 12:22:45 -05:00
// hack for revealing hidden dock when drag enters dock window
let window_drop_target_controller = DropTarget ::builder ( )
2021-12-27 17:59:49 -05:00
. actions ( drop_actions )
2021-12-27 20:19:59 -05:00
. formats ( & drop_format )
2021-12-17 12:22:45 -05:00
. build ( ) ;
2021-12-30 19:01:59 -05:00
let enter_handle = & imp . cursor_handle . get ( ) . unwrap ( ) ;
2021-12-22 17:23:18 -05:00
enter_handle . add_controller ( & window_drop_target_controller ) ;
2021-12-17 12:22:45 -05:00
imp . window_drop_controller
. set ( window_drop_target_controller )
. expect ( " Could not set dock dnd drop controller " ) ;
2021-12-16 09:38:20 -05:00
}
2021-12-27 17:59:49 -05:00
fn setup_drag_source ( & self ) {
let imp = imp ::Window ::from_instance ( self ) ;
2021-12-30 19:01:59 -05:00
let saved_app_list_view = & imp . saved_app_list_view . get ( ) . unwrap ( ) ;
2021-12-27 17:59:49 -05:00
let saved_app_model = imp
. saved_app_model
. get ( )
. expect ( " Failed to get saved app model. " ) ;
let actions = gdk4 ::DragAction ::MOVE ;
let saved_drag_source = DragSource ::builder ( )
. name ( " dock drag source " )
. actions ( actions )
. build ( ) ;
2021-12-27 20:19:59 -05:00
let drag_end = & imp . saved_drag_end_signal ;
let drag_cancel = & imp . saved_drag_cancel_signal ;
2021-12-27 17:59:49 -05:00
saved_app_list_view . add_controller ( & saved_drag_source ) ;
2021-12-27 18:33:17 -05:00
saved_drag_source . connect_prepare ( glib ::clone! ( @ weak saved_app_model , @ weak saved_app_list_view , @ weak drag_end , @ weak drag_cancel = > @ default - return None , move | self_ , x , _y | {
2021-12-27 17:59:49 -05:00
let max_x = saved_app_list_view . allocated_width ( ) ;
// dbg!(max_x);
// dbg!(max_y);
let n_buckets = saved_app_model . n_items ( ) ;
let index = ( x * n_buckets as f64 / ( max_x as f64 + 0.1 ) ) as u32 ;
if let Some ( item ) = saved_app_model . item ( index ) {
if let Some ( old_handle ) = drag_end . replace ( Some ( self_ . connect_drag_end (
glib ::clone! ( @ weak saved_app_model = > move | _self , _drag , _delete_data | {
dbg! ( _delete_data ) ;
2021-12-27 20:19:59 -05:00
if _delete_data {
saved_app_model . remove ( index ) ;
glib ::MainContext ::default ( ) . spawn_local ( async move {
if let Some ( tx ) = TX . get ( ) {
let mut tx = tx . clone ( ) ;
let _ = tx . send ( Event ::RefreshFromCache ) . await ;
}
} ) ;
} ;
2021-12-27 17:59:49 -05:00
} ) ,
) ) ) {
glib ::signal_handler_disconnect ( self_ , old_handle ) ;
}
2021-12-27 18:33:17 -05:00
if let Some ( old_handle ) = drag_cancel . replace ( Some ( self_ . connect_drag_cancel (
glib ::clone! ( @ weak saved_app_model = > @ default - return false , move | _self , _drag , cancel_reason | {
if cancel_reason ! = gdk4 ::DragCancelReason ::UserCancelled {
saved_app_model . remove ( index ) ;
2021-12-27 20:19:59 -05:00
glib ::MainContext ::default ( ) . spawn_local ( async move {
if let Some ( tx ) = TX . get ( ) {
let mut tx = tx . clone ( ) ;
let _ = tx . send ( Event ::RefreshFromCache ) . await ;
}
} ) ;
2021-12-27 18:33:17 -05:00
true
} else {
false
}
} ) ,
) ) ) {
glib ::signal_handler_disconnect ( self_ , old_handle ) ;
}
2021-12-27 17:59:49 -05:00
if let Ok ( dock_object ) = item . downcast ::< DockObject > ( ) {
if let Ok ( Some ( app_info ) ) = dock_object . property ( " appinfo " ) . expect ( " property appinfo missing from DockObject " ) . get ::< Option < DesktopAppInfo > > ( ) {
let icon = app_info
. icon ( )
. unwrap_or ( Icon ::for_string ( " image-missing " ) . expect ( " Failed to set default icon " ) ) ;
if let Some ( default_display ) = & Display ::default ( ) {
if let Some ( icon_theme ) = IconTheme ::for_display ( default_display ) {
if let Some ( paintable_icon ) = icon_theme . lookup_by_gicon (
& icon ,
64 ,
1 ,
gtk4 ::TextDirection ::None ,
gtk4 ::IconLookupFlags ::empty ( ) ,
) {
self_ . set_icon ( Some ( & paintable_icon ) , 32 , 32 ) ;
}
}
}
2021-12-27 20:19:59 -05:00
// saved app list provides index
let p = ContentProvider ::for_value ( & index . to_value ( ) ) ;
dbg! ( p . formats ( ) . types ( ) ) ;
return Some ( p ) ;
// if let Some(file) = app_info.filename() {
// return Some(ContentProvider::for_value(&file.to_string_lossy().to_value()));
// }
2021-12-27 17:59:49 -05:00
}
}
}
None
} ) ) ;
imp . saved_drag_source
. set ( saved_drag_source )
. expect ( " Could not set saved drag source " ) ;
2021-12-30 19:01:59 -05:00
let active_app_list_view = & imp . active_app_list_view . get ( ) . unwrap ( ) ;
2021-12-27 17:59:49 -05:00
let active_app_model = imp
. active_app_model
. get ( )
. expect ( " Failed to get saved app model. " ) ;
let actions = gdk4 ::DragAction ::MOVE ;
let active_drag_source = DragSource ::builder ( )
. name ( " dock drag source " )
. actions ( actions )
. build ( ) ;
2021-12-27 20:19:59 -05:00
let drag_end = & imp . active_drag_end_signal ;
let drag_cancel = & imp . active_drag_cancel_signal ;
2021-12-27 17:59:49 -05:00
active_drag_source . connect_drag_begin ( | _self , drag | {
drag . set_selected_action ( gdk4 ::DragAction ::MOVE ) ;
} ) ;
active_app_list_view . add_controller ( & active_drag_source ) ;
2021-12-27 18:33:17 -05:00
active_drag_source . connect_prepare ( glib ::clone! ( @ weak active_app_model , @ weak active_app_list_view , @ weak drag_end , @ weak drag_cancel = > @ default - return None , move | self_ , x , _y | {
2021-12-27 17:59:49 -05:00
let max_x = active_app_list_view . allocated_width ( ) ;
// dbg!(max_x);
// dbg!(max_y);
let n_buckets = active_app_model . n_items ( ) ;
let index = ( x * n_buckets as f64 / ( max_x as f64 + 0.1 ) ) as u32 ;
if let Some ( item ) = active_app_model . item ( index ) {
if let Some ( old_handle ) = drag_end . replace ( Some ( self_ . connect_drag_end (
glib ::clone! ( @ weak active_app_model = > move | _self , _drag , _delete_data | {
dbg! ( _delete_data ) ;
2021-12-27 20:19:59 -05:00
if _delete_data {
active_app_model . remove ( index ) ;
glib ::MainContext ::default ( ) . spawn_local ( async move {
if let Some ( tx ) = TX . get ( ) {
let mut tx = tx . clone ( ) ;
let _ = tx . send ( Event ::RefreshFromCache ) . await ;
}
} ) ;
} ;
2021-12-27 17:59:49 -05:00
} ) ,
) ) ) {
glib ::signal_handler_disconnect ( self_ , old_handle ) ;
}
2021-12-27 18:33:17 -05:00
if let Some ( old_handle ) = drag_cancel . replace ( Some ( self_ . connect_drag_cancel (
glib ::clone! ( @ weak active_app_model = > @ default - return false , move | _self , _drag , cancel_reason | {
if cancel_reason ! = gdk4 ::DragCancelReason ::UserCancelled {
active_app_model . remove ( index ) ;
2021-12-27 20:19:59 -05:00
glib ::MainContext ::default ( ) . spawn_local ( async move {
if let Some ( tx ) = TX . get ( ) {
let mut tx = tx . clone ( ) ;
let _ = tx . send ( Event ::RefreshFromCache ) . await ;
}
} ) ;
2021-12-27 18:33:17 -05:00
true
} else {
false
}
} ) ,
) ) ) {
glib ::signal_handler_disconnect ( self_ , old_handle ) ;
}
2021-12-27 17:59:49 -05:00
if let Ok ( dock_object ) = item . downcast ::< DockObject > ( ) {
if let Ok ( Some ( app_info ) ) = dock_object . property ( " appinfo " ) . expect ( " property appinfo missing from DockObject " ) . get ::< Option < DesktopAppInfo > > ( ) {
let icon = app_info
. icon ( )
. unwrap_or ( Icon ::for_string ( " image-missing " ) . expect ( " Failed to set default icon " ) ) ;
if let Some ( default_display ) = & Display ::default ( ) {
if let Some ( icon_theme ) = IconTheme ::for_display ( default_display ) {
if let Some ( paintable_icon ) = icon_theme . lookup_by_gicon (
& icon ,
64 ,
1 ,
gtk4 ::TextDirection ::None ,
gtk4 ::IconLookupFlags ::empty ( ) ,
) {
self_ . set_icon ( Some ( & paintable_icon ) , 32 , 32 ) ;
}
}
}
if let Some ( file ) = app_info . filename ( ) {
return Some ( ContentProvider ::for_value ( & file . to_string_lossy ( ) . to_value ( ) ) ) ;
}
}
}
}
None
} ) ) ;
imp . active_drag_source
. set ( active_drag_source )
. expect ( " Could not set saved drag source " ) ;
}
2021-12-14 11:53:18 -05:00
fn setup_factory ( & self ) {
2021-12-21 13:01:32 -05:00
let saved_app_factory = SignalListItemFactory ::new ( ) ;
saved_app_factory . connect_setup ( move | _ , list_item | {
2021-12-15 11:37:28 -05:00
let dock_item = DockItem ::new ( ) ;
list_item . set_child ( Some ( & dock_item ) ) ;
} ) ;
2021-12-16 09:38:20 -05:00
let imp = imp ::Window ::from_instance ( self ) ;
let saved_app_model = imp
. saved_app_model
. get ( )
. expect ( " Failed to get saved app model. " ) ;
2021-12-21 13:01:32 -05:00
saved_app_factory . connect_bind ( glib ::clone! ( @ weak saved_app_model = > move | _ , list_item | {
2021-12-15 11:37:28 -05:00
let application_object = list_item
. item ( )
. expect ( " The item has to exist. " )
2021-12-20 23:52:59 -05:00
. downcast ::< DockObject > ( )
. expect ( " The item has to be a `DockObject` " ) ;
2021-12-15 11:37:28 -05:00
let dock_item = list_item
. child ( )
. expect ( " The list item child needs to exist. " )
. downcast ::< DockItem > ( )
. expect ( " The list item type needs to be `DockItem` " ) ;
2021-12-27 18:33:17 -05:00
dock_item . set_app_info ( & application_object ) ;
2021-12-16 09:38:20 -05:00
} ) ) ;
2021-12-14 11:53:18 -05:00
// Set the factory of the list view
2021-12-21 13:01:32 -05:00
imp . saved_app_list_view
2021-12-30 19:01:59 -05:00
. get ( )
. unwrap ( )
2021-12-21 13:01:32 -05:00
. set_factory ( Some ( & saved_app_factory ) ) ;
let active_app_model = imp
. active_app_model
. get ( )
. expect ( " Failed to get saved app model. " ) ;
let active_factory = SignalListItemFactory ::new ( ) ;
active_factory . connect_setup ( move | _ , list_item | {
let dock_item = DockItem ::new ( ) ;
list_item . set_child ( Some ( & dock_item ) ) ;
} ) ;
active_factory . connect_bind ( glib ::clone! ( @ weak active_app_model = > move | _ , list_item | {
let application_object = list_item
. item ( )
. expect ( " The item has to exist. " )
. downcast ::< DockObject > ( )
. expect ( " The item has to be a `DockObject` " ) ;
let dock_item = list_item
. child ( )
. expect ( " The list item child needs to exist. " )
. downcast ::< DockItem > ( )
. expect ( " The list item type needs to be `DockItem` " ) ;
2021-12-27 18:33:17 -05:00
dock_item . set_app_info ( & application_object ) ;
2021-12-21 13:01:32 -05:00
} ) ) ;
// Set the factory of the list view
2021-12-30 19:01:59 -05:00
imp . active_app_list_view
. get ( )
. unwrap ( )
. set_factory ( Some ( & active_factory ) ) ;
2021-12-14 11:53:18 -05:00
}
2021-12-27 17:59:49 -05:00
fn restore_saved_apps ( & self ) {
if let Ok ( file ) = File ::open ( data_path ( ) ) {
2021-12-27 18:33:17 -05:00
if let Ok ( saved_data ) = serde_json ::from_reader ::< _ , Vec < String > > ( file ) {
// dbg!(&saved_data);
let dock_objects : Vec < Object > = saved_data
2021-12-27 17:59:49 -05:00
. into_iter ( )
. filter_map ( | d | {
DockObject ::from_app_info_path ( & d )
. map ( | dockobject | dockobject . upcast ::< Object > ( ) )
} )
. collect ( ) ;
2021-12-27 18:33:17 -05:00
// dbg!(&dock_objects);
2021-12-27 17:59:49 -05:00
let saved_app_model = self . saved_app_model ( ) ;
saved_app_model . splice ( saved_app_model . n_items ( ) , 0 , & dock_objects ) ;
2021-12-27 18:33:17 -05:00
return ;
2021-12-27 17:59:49 -05:00
}
}
2021-12-27 18:33:17 -05:00
println! ( " Error loading saved apps! " ) ;
let saved_app_model = & self . saved_app_model ( ) ;
xdg ::BaseDirectories ::new ( )
. expect ( " could not access XDG Base directory " )
. get_data_dirs ( )
. iter_mut ( )
. for_each ( | xdg_data_path | {
let defaults = [ " Firefox Web Browser " , " Files " , " Terminal " , " Pop!_Shop " ] ;
xdg_data_path . push ( " applications " ) ;
// dbg!(&xdg_data_path);
if let Ok ( dir_iter ) = std ::fs ::read_dir ( xdg_data_path ) {
dir_iter . for_each ( | dir_entry | {
if let Ok ( dir_entry ) = dir_entry {
if let Some ( path ) = dir_entry . path ( ) . file_name ( ) {
if let Some ( path ) = path . to_str ( ) {
if let Some ( app_info ) = gio ::DesktopAppInfo ::new ( path ) {
if app_info . should_show ( )
& & defaults . contains ( & app_info . name ( ) . as_str ( ) )
{
saved_app_model . append ( & DockObject ::new ( app_info ) ) ;
} else {
// println!("Ignoring {}", path);
}
} else {
// println!("error loading {}", path);
}
}
}
}
} )
}
} ) ;
2021-12-27 17:59:49 -05:00
}
fn store_saved_apps ( saved_app_model : & gio ::ListStore ) {
// Store todo data in vector
let mut backup_data = Vec ::new ( ) ;
2021-12-27 18:33:17 -05:00
let mut i = 0 ;
while let Some ( item ) = saved_app_model . item ( i ) {
2021-12-27 17:59:49 -05:00
// Get `AppGroup` from `glib::Object`
let dock_object = item
. downcast_ref ::< DockObject > ( )
. expect ( " The object needs to be of type `AppGroupData`. " ) ;
// Add todo data to vector and increase position
if let Ok ( Some ( app_info ) ) = dock_object
. property ( " appinfo " )
. expect ( " DockObject must have appinfo property " )
. get ::< Option < DesktopAppInfo > > ( )
{
if let Some ( f ) = app_info . filename ( ) {
backup_data . push ( f ) ;
}
}
2021-12-27 18:33:17 -05:00
i + = 1 ;
2021-12-27 17:59:49 -05:00
}
2021-12-27 18:33:17 -05:00
// dbg!(&backup_data);
2021-12-27 17:59:49 -05:00
// Save state in file
let file = File ::create ( data_path ( ) ) . expect ( " Could not create json file. " ) ;
serde_json ::to_writer_pretty ( file , & backup_data )
. expect ( " Could not write data to json file " ) ;
}
2021-12-14 11:53:18 -05:00
}