diff --git a/examples/dock/dock_item/imp.rs b/examples/dock/dock_item/imp.rs index 2a7ad216..12b86ae9 100644 --- a/examples/dock/dock_item/imp.rs +++ b/examples/dock/dock_item/imp.rs @@ -2,17 +2,19 @@ use glib::subclass::Signal; use gtk4::glib; use gtk4::prelude::*; use gtk4::subclass::prelude::*; -use gtk4::PopoverMenu; use once_cell::sync::Lazy; use std::cell::RefCell; use std::rc::Rc; +use crate::dock_popover::DockPopover; + #[derive(Debug, Default)] pub struct DockItem { pub image: Rc>, pub dots: Rc>, pub item_box: Rc>, - pub popover_menu: Rc>>, + pub popover: Rc>, + pub popover_menu: Rc>, } #[glib::object_subclass] diff --git a/examples/dock/dock_item/mod.rs b/examples/dock/dock_item/mod.rs index 86961c75..c06e3dbb 100644 --- a/examples/dock/dock_item/mod.rs +++ b/examples/dock/dock_item/mod.rs @@ -1,8 +1,6 @@ use cascade::cascade; use gio::DesktopAppInfo; use gio::Icon; -use gio::Menu; -use gio::MenuItem; use gtk4::glib; use gtk4::prelude::*; use gtk4::subclass::prelude::*; @@ -11,9 +9,10 @@ use gtk4::Box; use gtk4::Image; use gtk4::Label; use gtk4::Orientation; -use gtk4::PopoverMenu; +use gtk4::Popover; use crate::dock_object::DockObject; +use crate::dock_popover::DockPopover; use crate::utils::BoxedWindowList; mod imp; @@ -50,23 +49,29 @@ impl DockItem { }; item_box.append(&image); item_box.append(&dots); - let popover_menu = cascade! { - PopoverMenu::from_model_full(&Menu::new(), gtk4::PopoverMenuFlags::NESTED); + let popover = cascade! { + Popover::new(); ..set_autohide(true); }; - item_box.append(&popover_menu); + item_box.append(&popover); let self_clone = self_.clone(); - popover_menu.connect_closed(move |_| { + popover.connect_closed(move |_| { self_clone .emit_by_name::<&str>("popover-closed", &[]) .unwrap(); }); + let popover_menu = cascade! { + DockPopover::new(); + }; + popover.set_child(Some(&popover_menu)); + let imp = imp::DockItem::from_instance(&self_); imp.image.replace(image); imp.dots.replace(dots); imp.item_box.replace(item_box); - imp.popover_menu.replace(Some(popover_menu)); + imp.popover.replace(popover); + imp.popover_menu.replace(popover_menu); self_ } @@ -114,70 +119,17 @@ impl DockItem { pub fn add_popover(&self, item: &DockObject) { let imp = imp::DockItem::from_instance(self); - if let Some(popover_menu) = imp.popover_menu.borrow().as_ref() { - let menu = if let Some(m) = popover_menu.menu_model() { - m.downcast::().unwrap() - } else { - Menu::new() - }; - menu.remove_all(); + let popover = imp.popover.borrow(); + let popover_menu = imp.popover_menu.borrow(); - let appinfo = item - .property("appinfo") - .expect("property appinfo missing from DockObject") - .get::>(); - let window_list = item - .property("active") - .expect("property appinfo missing from DockObject") - .get::(); - - if let Ok(Some(_appinfo)) = appinfo { - let launch_menu = Menu::new(); - let launch_subsection = MenuItem::new_section(None, &launch_menu); - launch_menu.append(Some("New Window"), None); - menu.append_item(&launch_subsection); - } - - let favorites_menu = Menu::new(); - let favorites_subsection = MenuItem::new_section(None, &favorites_menu); - match item.property("saved").unwrap().get::().unwrap() { - true => favorites_menu.append(Some("Remove from Favorites"), None), - false => favorites_menu.append(Some("Add to Favorites"), None), - }; - menu.append_item(&favorites_subsection); - - if let Ok(window_list) = window_list { - let window_list = window_list.0; - if window_list.len() > 0 { - let all_windows_submenu_menu = Menu::new(); - for w in window_list { - all_windows_submenu_menu.append(Some(w.name.as_str()), None); - } - - let all_windows_menu = Menu::new(); - let all_windows_submenu = - MenuItem::new_submenu(Some("All Windows"), &all_windows_submenu_menu); - all_windows_menu.append_item(&all_windows_submenu); - let all_windows_subsection = MenuItem::new_section(None, &all_windows_menu); - - let quit_windows_menu = Menu::new(); - quit_windows_menu.append(Some("Quit All"), None); - let quit_windows_subsection = MenuItem::new_section(None, &quit_windows_menu); - - menu.prepend_item(&all_windows_subsection); - menu.append_item(&quit_windows_subsection); - } - } - popover_menu.popup(); - } + popover_menu.set_dock_object(item, true); + popover.popup(); } pub fn clear_popover(&self) { let imp = imp::DockItem::from_instance(self); - let popover_menu = imp.popover_menu.borrow(); + let popover = imp.popover.borrow(); - if let Some(popover) = popover_menu.as_ref() { - popover.popdown(); - } + popover.popdown(); } } diff --git a/examples/dock/dock_popover/imp.rs b/examples/dock/dock_popover/imp.rs index e69de29b..8d1eec41 100644 --- a/examples/dock/dock_popover/imp.rs +++ b/examples/dock/dock_popover/imp.rs @@ -0,0 +1,33 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use gtk4::glib; +use gtk4::subclass::prelude::*; +use gtk4::{Box, Button, ListBox, Revealer}; + +use crate::dock_object::DockObject; + +#[derive(Debug, Default)] +pub struct DockPopover { + pub menu_handle: Rc>, + pub all_windows_item_revealer: Rc>, + pub window_list: Rc>, + pub launch_new_item: Rc>, + pub favorite_item: Rc>, + pub quit_all_item: Rc>, + //TODO figure out how to use lifetimes with glib::wrapper! macro + pub dock_object: Rc>>, +} + +#[glib::object_subclass] +impl ObjectSubclass for DockPopover { + const NAME: &'static str = "DockPopover"; + type Type = super::DockPopover; + type ParentType = Box; +} + +impl ObjectImpl for DockPopover {} + +impl WidgetImpl for DockPopover {} + +impl BoxImpl for DockPopover {} diff --git a/examples/dock/dock_popover/mod.rs b/examples/dock/dock_popover/mod.rs index e69de29b..f7cb09a2 100644 --- a/examples/dock/dock_popover/mod.rs +++ b/examples/dock/dock_popover/mod.rs @@ -0,0 +1,184 @@ +use cascade::cascade; +use gtk4::ffi::gtk_window_close; +use gtk4::subclass::prelude::*; +use gtk4::{gio, glib}; +use gtk4::{prelude::*, Label}; +use gtk4::{Box, Button, Image, ListBox, Orientation, Revealer}; + +use crate::dock_object::DockObject; +use crate::utils::BoxedWindowList; + +mod imp; + +glib::wrapper! { + pub struct DockPopover(ObjectSubclass) + @extends gtk4::Widget, gtk4::Box, + @implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget, gtk4::Orientable; +} + +impl Default for DockPopover { + fn default() -> Self { + Self::new() + } +} + +impl DockPopover { + pub fn new() -> Self { + let self_: DockPopover = glib::Object::new(&[]).expect("Failed to create DockList"); + self_.layout(); + //dnd behavior is different for each type, as well as the data in the model + self_ + } + + pub fn set_dock_object(&self, dock_object: &DockObject, update_layout: bool) { + let imp = imp::DockPopover::from_instance(&self); + imp.dock_object.replace(Some(dock_object.clone())); + if update_layout { + self.update_layout(); + } + } + + pub fn update_layout(&self) { + let imp = imp::DockPopover::from_instance(&self); + let dock_object = imp.dock_object.borrow(); + + // reset menu + let menu_handle = cascade! { + Box::new(Orientation::Vertical, 4); + }; + self.append(&menu_handle); + + // build menu + if let Some(dock_object) = dock_object.as_ref() { + cascade! { + &self; + ..set_spacing(4); + ..set_orientation(Orientation::Vertical); + ..set_hexpand(true); + }; + + let all_windows_item_container = cascade! { + Box::new(Orientation::Vertical, 4); + }; + menu_handle.append(&all_windows_item_container); + + let all_windows_item_header = cascade! { + Button::new(); + ..set_hexpand(true); + }; + all_windows_item_container.append(&all_windows_item_header); + + let all_windows_item_header_box = cascade! { + Box::new(Orientation::Horizontal, 4); + ..set_hexpand(true); + }; + all_windows_item_header.set_child(Some(&all_windows_item_header_box)); + + let all_windows_item_header_title = cascade! { + Label::new(Some("All Windows")); + ..add_css_class("header-5"); + ..set_halign(gtk4::Align::Start); + }; + all_windows_item_header_box.append(&all_windows_item_header_title); + + let all_windows_item_header_icon = cascade! { + Image::from_icon_name(Some("go-down")); + ..set_halign(gtk4::Align::End); + ..set_pixel_size(16); + }; + all_windows_item_header_box.append(&all_windows_item_header_icon); + + if let Ok(window_list) = dock_object + .property("active") + .unwrap() + .get::() + { + if window_list.0.len() == 0 { + all_windows_item_container.hide(); + } else { + let window_list_revealer = cascade! { + Revealer::new(); + ..set_reveal_child(true); + ..set_transition_type(gtk4::RevealerTransitionType::SlideDown); + }; + all_windows_item_container.append(&window_list_revealer); + let window_listbox = cascade! { + ListBox::new(); + }; + window_list_revealer.set_child(Some(&window_listbox)); + for w in window_list.0 { + let window_box = cascade! { + Box::new(Orientation::Vertical, 4); + }; + window_listbox.append(&window_box); + + let window_title = cascade! { + Label::new(Some(w.name.as_str())); + }; + + // TODO investigate Xembed + let window_image = cascade! { + //TODO fill with image of window + Image::from_pixbuf(None); + }; + window_box.append(&window_image); + window_box.append(&window_title); + } + } + } + + let launch_item_container = cascade! { + Box::new(Orientation::Vertical, 4); + ..set_hexpand(true); + }; + menu_handle.append(&launch_item_container); + + let launch_new_item = cascade! { + Button::with_label("New Window"); + }; + launch_item_container.append(&launch_new_item); + + let favorite_item = cascade! { + Button::with_label(if dock_object.property("saved").unwrap().get::().unwrap() {"Remove from Favorites"} else {"Add to Favorites"}); + }; + menu_handle.append(&favorite_item); + + if let Ok(window_list) = dock_object + .property("active") + .unwrap() + .get::() + { + if window_list.0.len() > 1 { + let quit_all_item = cascade! { + Button::with_label(format!("Quit {} Windows", window_list.0.len()).as_str()); + }; + menu_handle.append(&quit_all_item); + } else { + let quit_all_item = cascade! { + Button::with_label("Quit"); + }; + menu_handle.append(&quit_all_item); + if window_list.0.len() == 0 { + quit_all_item.hide(); + } + } + } + self.setup_handlers(); + } + let old_menu_handle = imp.menu_handle.replace(menu_handle); + self.remove(&old_menu_handle); + } + + fn layout(&self) { + let imp = imp::DockPopover::from_instance(&self); + let menu_handle = cascade! { + Box::default(); + }; + self.append(&menu_handle); + imp.menu_handle.replace(menu_handle); + } + + fn setup_handlers(&self) { + // todo!(); + } +} diff --git a/examples/dock/main.rs b/examples/dock/main.rs index 0b60c605..7bb668a8 100644 --- a/examples/dock/main.rs +++ b/examples/dock/main.rs @@ -27,6 +27,7 @@ use self::window::Window; mod dock_item; mod dock_list; mod dock_object; +mod dock_popover; mod utils; mod window;