diff --git a/examples/app_library/grid_item/grid_item.ui b/examples/app_library/grid_item/grid_item.ui deleted file mode 100644 index d29d4943..00000000 --- a/examples/app_library/grid_item/grid_item.ui +++ /dev/null @@ -1,28 +0,0 @@ - - - - diff --git a/examples/app_library/grid_item/imp.rs b/examples/app_library/grid_item/imp.rs index 38383d43..3da89a4e 100644 --- a/examples/app_library/grid_item/imp.rs +++ b/examples/app_library/grid_item/imp.rs @@ -1,17 +1,14 @@ use std::cell::Cell; +use std::cell::RefCell; +use std::rc::Rc; -use gtk4::glib; -use gtk4::prelude::*; +use glib; use gtk4::subclass::prelude::*; -use gtk4::CompositeTemplate; -#[derive(Debug, Default, CompositeTemplate)] -#[template(file = "grid_item.ui")] +#[derive(Debug, Default)] pub struct GridItem { - #[template_child] - pub name: TemplateChild, - #[template_child] - pub image: TemplateChild, + pub name: Rc>, + pub image: Rc>, pub index: Cell, } @@ -20,14 +17,6 @@ impl ObjectSubclass for GridItem { const NAME: &'static str = "GridItem"; type Type = super::GridItem; type ParentType = gtk4::Box; - - fn class_init(klass: &mut Self::Class) { - Self::bind_template(klass); - } - - fn instance_init(obj: &glib::subclass::InitializingObject) { - obj.init_template(); - } } impl ObjectImpl for GridItem {} diff --git a/examples/app_library/grid_item/mod.rs b/examples/app_library/grid_item/mod.rs index f0fc538f..ce6b7830 100644 --- a/examples/app_library/grid_item/mod.rs +++ b/examples/app_library/grid_item/mod.rs @@ -1,12 +1,18 @@ +use cascade::cascade; use gdk4::ContentProvider; use gdk4::Display; use gio::File; use gio::Icon; +use gtk4::pango::EllipsizeMode; use gtk4::prelude::*; use gtk4::subclass::prelude::*; use gtk4::traits::WidgetExt; +use gtk4::Align; use gtk4::DragSource; use gtk4::IconTheme; +use gtk4::Image; +use gtk4::Label; +use gtk4::Orientation; use gtk4::{gio, glib}; use crate::app_group::AppGroup; @@ -15,8 +21,8 @@ mod imp; glib::wrapper! { pub struct GridItem(ObjectSubclass) - @extends gtk4::Widget, gtk4::Box, - @implements gtk4::Accessible, gtk4::Actionable, gtk4::Buildable, gtk4::ConstraintTarget; + @extends gtk4::Widget, gtk4::Box, + @implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget, gtk4::Orientable; } impl Default for GridItem { @@ -27,12 +33,46 @@ impl Default for GridItem { impl GridItem { pub fn new() -> Self { - glib::Object::new(&[]).expect("Failed to create GridItem") + let self_ = glib::Object::new(&[]).expect("Failed to create GridItem"); + let imp = imp::GridItem::from_instance(&self_); + + cascade! { + &self_; + ..set_orientation(Orientation::Vertical); + ..set_halign(Align::Center); + ..set_hexpand(true); + ..set_margin_top(4); + ..set_margin_bottom(4); + ..set_margin_end(4); + ..set_margin_start(4); + }; + + let image = cascade! { + Image::new(); + ..set_margin_top(4); + ..set_margin_bottom(4); + ..set_pixel_size(64); + }; + self_.append(&image); + + let name = cascade! { + Label::new(None); + ..set_halign(Align::Center); + ..set_hexpand(true); + ..set_ellipsize(EllipsizeMode::End); + ..add_css_class("title-5"); + }; + self_.append(&name); + + imp.name.replace(name); + imp.image.replace(image); + + self_ } pub fn set_app_info(&self, app_info: &gio::DesktopAppInfo) { let self_ = imp::GridItem::from_instance(self); - self_.name.set_text(&app_info.name()); + self_.name.borrow().set_text(&app_info.name()); let drag_controller = DragSource::builder() .name("application library drag source") @@ -48,7 +88,7 @@ impl GridItem { let icon = app_info .icon() .unwrap_or(Icon::for_string("image-missing").expect("Failed to set default icon")); - self_.image.set_from_gicon(&icon); + self_.image.borrow().set_from_gicon(&icon); drag_controller.connect_drag_begin(glib::clone!(@weak icon, => move |_self, drag| { drag.set_selected_action(gdk4::DragAction::MOVE); // set drag source icon if possible... @@ -72,14 +112,14 @@ impl GridItem { pub fn set_group_info(&self, app_group: AppGroup) { let self_ = imp::GridItem::from_instance(self); if let Ok(name) = app_group.property("name") { - self_.name.set_text( + self_.name.borrow().set_text( &name .get::() .expect("property name needs to be a string."), ); } if let Ok(icon) = app_group.property("icon") { - self_.image.set_from_icon_name(Some( + self_.image.borrow().set_from_icon_name(Some( &icon .get::() .expect("Property name needs to be a String."), diff --git a/examples/app_library/style.css b/examples/app_library/style.css index 5197b578..af82b888 100644 --- a/examples/app_library/style.css +++ b/examples/app_library/style.css @@ -11,7 +11,12 @@ gridview { background: #333333; } -window { +box.app_library_container { background: #333333; - border-radius: 15px; + padding: 12px; + border-radius: 12px; +} + +window.root_window { + background: rgba(50, 50, 50, 0.0); } diff --git a/examples/app_library/window/imp.rs b/examples/app_library/window/imp.rs index b36e647c..9ea504b1 100644 --- a/examples/app_library/window/imp.rs +++ b/examples/app_library/window/imp.rs @@ -1,30 +1,24 @@ use std::fs::File; use glib::signal::Inhibit; -use glib::subclass::InitializingObject; use gtk4::prelude::*; use gtk4::subclass::prelude::*; use gtk4::ScrolledWindow; use gtk4::{gio, glib}; -use gtk4::{CompositeTemplate, GridView, SearchEntry}; +use gtk4::{GridView, SearchEntry}; use once_cell::sync::OnceCell; use crate::app_group::AppGroup; use crate::utils::data_path; // Object holding the state -#[derive(CompositeTemplate, Default)] -#[template(file = "window.ui")] +#[derive(Default)] pub struct Window { - #[template_child] - pub entry: TemplateChild, - #[template_child] - pub app_grid_view: TemplateChild, + pub entry: OnceCell, + pub app_grid_view: OnceCell, pub app_model: OnceCell, - #[template_child] - pub group_grid_view: TemplateChild, - #[template_child] - pub group_scroll_window: TemplateChild, + pub group_grid_view: OnceCell, + pub group_scroll_window: OnceCell, pub group_model: OnceCell, } @@ -35,29 +29,10 @@ impl ObjectSubclass for Window { const NAME: &'static str = "LauncherWindow"; type Type = super::Window; type ParentType = gtk4::ApplicationWindow; - - fn class_init(klass: &mut Self::Class) { - Self::bind_template(klass); - } - - fn instance_init(obj: &InitializingObject) { - obj.init_template(); - } } // Trait shared by all GObjects -impl ObjectImpl for Window { - fn constructed(&self, obj: &Self::Type) { - // Call "constructed" on parent - self.parent_constructed(obj); - - // Setup - obj.setup_model(); - obj.restore_data(); - obj.setup_callbacks(); - obj.setup_factory(); - } -} +impl ObjectImpl for Window {} // Trait shared by all widgets impl WidgetImpl for Window {} diff --git a/examples/app_library/window/mod.rs b/examples/app_library/window/mod.rs index e7e9217f..6702a3cd 100644 --- a/examples/app_library/window/mod.rs +++ b/examples/app_library/window/mod.rs @@ -1,5 +1,6 @@ use std::fs::File; +use cascade::cascade; use gdk4::Rectangle; use gdk4_x11::X11Display; use gdk4_x11::X11Surface; @@ -8,9 +9,17 @@ use glib::Object; use glib::Variant; use gtk4::prelude::*; use gtk4::subclass::prelude::*; +use gtk4::Align; +use gtk4::Box; use gtk4::Dialog; use gtk4::Entry; +use gtk4::GridView; use gtk4::Label; +use gtk4::Orientation; +use gtk4::PolicyType; +use gtk4::ScrolledWindow; +use gtk4::SearchEntry; +use gtk4::Separator; use gtk4::{gio, glib}; use gtk4::{Application, SignalListItemFactory}; use x11rb::connection::Connection; @@ -43,7 +52,86 @@ impl Window { for i in 1..10 { app.set_accels_for_action(&format!("win.launch{}", i), &[&format!("{}", i)]); } - Object::new(&[("application", app)]).expect("Failed to create `Window`.") + let self_: Self = Object::new(&[("application", app)]).expect("Failed to create `Window`."); + let imp = imp::Window::from_instance(&self_); + + cascade! { + &self_; + ..set_width_request(1200); + ..set_title(Some("Cosmic App Library")); + ..set_decorated(false); + ..add_css_class("root_window"); + }; + + let app_library = cascade! { + Box::new(Orientation::Vertical, 0); + ..add_css_class("app_library_container"); + }; + self_.set_child(Some(&app_library)); + + let entry = cascade! { + SearchEntry::new(); + ..set_width_request(300); + ..set_halign(Align::Center); + ..set_margin_top(12); + ..set_margin_bottom(12); + ..set_placeholder_text(Some(" Type to search")); + }; + app_library.append(&entry); + + let library_window = cascade! { + ScrolledWindow::new(); + ..set_hscrollbar_policy(PolicyType::Never); + ..set_min_content_height(500); + ..set_vexpand(true); + ..set_margin_top(12); + }; + app_library.append(&library_window); + + let library_grid = cascade! { + GridView::default(); + ..set_min_columns(7); + ..set_max_columns(7); + }; + library_window.set_child(Some(&library_grid)); + + let separator = cascade! { + Separator::new(Orientation::Horizontal); + ..set_hexpand(true); + ..set_margin_bottom(12); + ..set_margin_top(12); + }; + app_library.append(&separator); + + let group_window = cascade! { + ScrolledWindow::new(); + ..set_hscrollbar_policy(PolicyType::Never); + ..set_vscrollbar_policy(PolicyType::Never); + ..set_propagate_natural_height(true); + ..set_min_content_height(150); + ..set_max_content_height(300); + }; + app_library.append(&group_window); + + let group_grid_view = cascade! { + GridView::default(); + ..set_min_columns(8); + ..set_max_columns(8); + }; + group_window.set_child(Some(&group_grid_view)); + + imp.entry.set(entry).unwrap(); + imp.app_grid_view.set(library_grid).unwrap(); + imp.group_scroll_window.set(group_window).unwrap(); + imp.group_grid_view.set(group_grid_view).unwrap(); + + // Setup + self_.setup_model(); + self_.restore_data(); + self_.setup_callbacks(); + self_.setup_factory(); + + self_ } fn _app_model(&self) -> &gio::ListStore { @@ -121,7 +209,10 @@ impl Window { .build(); // Wrap model with selection and pass it to the list view - imp.app_grid_view.set_model(Some(&selection_model)); + imp.app_grid_view + .get() + .unwrap() + .set_model(Some(&selection_model)); selection_model.unselect_all(); let group_model = gio::ListStore::new(AppGroup::static_type()); @@ -176,15 +267,18 @@ impl Window { group_model.append(group); }); let group_selection = gtk4::SingleSelection::new(Some(&group_model)); - imp.group_grid_view.set_model(Some(&group_selection)); + imp.group_grid_view + .get() + .unwrap() + .set_model(Some(&group_selection)); } fn setup_callbacks(&self) { // Get state let imp = imp::Window::from_instance(self); let window = self.clone().upcast::(); - let app_grid_view = &imp.app_grid_view; - let group_grid_view = &imp.group_grid_view; + let app_grid_view = &imp.app_grid_view.get().unwrap(); + let group_grid_view = &imp.group_grid_view.get().unwrap(); let app_selection_model = app_grid_view .model() .expect("List view missing selection model") @@ -210,8 +304,8 @@ impl Window { .downcast::() .expect("could not downcast listview model to single selection model"); - let entry = &imp.entry; - let scroll_window = &imp.group_scroll_window.get(); + let entry = &imp.entry.get().unwrap(); + let scroll_window = &imp.group_scroll_window.get().unwrap(); // dynamically set scroll method bc of buggy gtk scroll behavior self.group_model().connect_items_changed( @@ -498,7 +592,7 @@ impl Window { let imp = imp::Window::from_instance(self); // the bind stage is used for "binding" the data to the created widgets on the "setup" stage - let app_grid_view = &imp.app_grid_view.get(); + let app_grid_view = &imp.app_grid_view.get().unwrap(); app_factory.connect_bind( glib::clone!(@weak app_grid_view => move |_factory, grid_item| { let app_info = grid_item @@ -528,7 +622,10 @@ impl Window { child.set_group_info(group_info); }); // Set the factory of the list view - imp.group_grid_view.set_factory(Some(&group_factory)); + imp.group_grid_view + .get() + .unwrap() + .set_factory(Some(&group_factory)); } fn restore_data(&self) { @@ -541,7 +638,10 @@ impl Window { .into_iter() .map(|data| AppGroup::new(data).upcast::()) .collect(); - let scroll_window = &imp::Window::from_instance(self).group_scroll_window; + let scroll_window = &imp::Window::from_instance(self) + .group_scroll_window + .get() + .unwrap(); // Insert restored objects into model self.group_model().splice(3, 0, &app_group_objects); diff --git a/examples/app_library/window/window.ui b/examples/app_library/window/window.ui deleted file mode 100644 index 4690cb7d..00000000 --- a/examples/app_library/window/window.ui +++ /dev/null @@ -1,66 +0,0 @@ - - - - diff --git a/examples/dock/dock_item/mod.rs b/examples/dock/dock_item/mod.rs index 175978c0..c9f74a2f 100644 --- a/examples/dock/dock_item/mod.rs +++ b/examples/dock/dock_item/mod.rs @@ -5,7 +5,6 @@ use gtk4::glib; use gtk4::prelude::*; use gtk4::subclass::prelude::*; use gtk4::Align; -use gtk4::AspectFrame; use gtk4::Box; use gtk4::Image; use gtk4::Label; diff --git a/examples/dock/style.css b/examples/dock/style.css index 877834a0..c205256f 100644 --- a/examples/dock/style.css +++ b/examples/dock/style.css @@ -28,11 +28,11 @@ listview { background: #333333; } -window { - background: rgba(50, 50, 50, 0.0); -} - box.dock { border-radius: 12px; background: #333333; } + +window.root_window { + background: rgba(50, 50, 50, 0.0); +} diff --git a/examples/dock/window/mod.rs b/examples/dock/window/mod.rs index 86eec806..142f96e5 100644 --- a/examples/dock/window/mod.rs +++ b/examples/dock/window/mod.rs @@ -63,6 +63,7 @@ impl Window { ..set_title(Some("Cosmic Dock")); ..set_decorated(false); ..set_resizable(false); + ..add_css_class("root_window"); }; let cursor_handle = Box::new(Orientation::Vertical, 0); self_.set_child(Some(&cursor_handle)); diff --git a/examples/launcher/search_result_row/application_row.ui b/examples/launcher/search_result_row/application_row.ui deleted file mode 100644 index f878eccf..00000000 --- a/examples/launcher/search_result_row/application_row.ui +++ /dev/null @@ -1,61 +0,0 @@ - - - - diff --git a/examples/launcher/search_result_row/mod.rs b/examples/launcher/search_result_row/mod.rs index f86af9a7..1e6cefa7 100644 --- a/examples/launcher/search_result_row/mod.rs +++ b/examples/launcher/search_result_row/mod.rs @@ -18,7 +18,7 @@ mod imp; glib::wrapper! { pub struct SearchResultRow(ObjectSubclass) @extends gtk4::Widget, gtk4::Box, - @implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget, gtk4::Orientable; + @implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget, gtk4::Orientable; } impl Default for SearchResultRow { diff --git a/examples/launcher/style.css b/examples/launcher/style.css index fa2f1253..1380fed3 100644 --- a/examples/launcher/style.css +++ b/examples/launcher/style.css @@ -19,6 +19,6 @@ box.container { border-radius: 12px; } -window { +window.root_window { background: rgba(50, 50, 50, 0.0); } diff --git a/examples/launcher/window/mod.rs b/examples/launcher/window/mod.rs index e9ef997f..a3a9fd7b 100644 --- a/examples/launcher/window/mod.rs +++ b/examples/launcher/window/mod.rs @@ -45,6 +45,7 @@ impl Window { ..set_title(Some("Cosmic Launcher")); ..set_decorated(false); ..set_resizable(false); + ..add_css_class("root_window"); }; let container = cascade! { diff --git a/examples/launcher/window/window.ui b/examples/launcher/window/window.ui deleted file mode 100644 index 9f2e46ca..00000000 --- a/examples/launcher/window/window.ui +++ /dev/null @@ -1,26 +0,0 @@ - - - -