show scrollbar if >2 rows of app groups
This commit is contained in:
parent
ce3bcd7821
commit
e160649a14
5 changed files with 102 additions and 76 deletions
|
|
@ -5,13 +5,14 @@
|
||||||
<property name="halign">center</property>
|
<property name="halign">center</property>
|
||||||
<property name="hexpand">true</property>
|
<property name="hexpand">true</property>
|
||||||
<property name="margin-top">4</property>
|
<property name="margin-top">4</property>
|
||||||
|
<property name="margin-start">4</property>
|
||||||
<property name="margin-end">4</property>
|
<property name="margin-end">4</property>
|
||||||
<property name="margin-bottom">4</property>
|
<property name="margin-bottom">4</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="image">
|
<object class="GtkImage" id="image">
|
||||||
<property name="margin-top">4</property>
|
<property name="margin-top">4</property>
|
||||||
<property name="margin-bottom">4</property>
|
<property name="margin-bottom">4</property>
|
||||||
<property name="pixel-size">80</property>
|
<property name="pixel-size">64</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use gtk4::ScrolledWindow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use gtk4::glib;
|
use gtk4::glib;
|
||||||
|
|
@ -9,3 +10,11 @@ pub fn data_path() -> PathBuf {
|
||||||
path.push("data.json");
|
path.push("data.json");
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_group_scroll_policy(scroll_window: &ScrolledWindow, group_cnt: u32) {
|
||||||
|
if scroll_window.policy().1 == gtk4::PolicyType::Never && group_cnt > 16 {
|
||||||
|
scroll_window.set_policy(gtk4::PolicyType::Never, gtk4::PolicyType::Automatic);
|
||||||
|
} else if scroll_window.policy().1 == gtk4::PolicyType::Automatic && group_cnt <= 16 {
|
||||||
|
scroll_window.set_policy(gtk4::PolicyType::Never, gtk4::PolicyType::Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use gtk4 as gtk;
|
use gtk4 as gtk;
|
||||||
|
use gtk4::ScrolledWindow;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
use glib::signal::Inhibit;
|
use glib::signal::Inhibit;
|
||||||
|
|
@ -23,6 +24,8 @@ pub struct Window {
|
||||||
pub app_model: OnceCell<gio::ListStore>,
|
pub app_model: OnceCell<gio::ListStore>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub group_grid_view: TemplateChild<GridView>,
|
pub group_grid_view: TemplateChild<GridView>,
|
||||||
|
#[template_child]
|
||||||
|
pub group_scroll_window: TemplateChild<ScrolledWindow>,
|
||||||
pub group_model: OnceCell<gio::ListStore>,
|
pub group_model: OnceCell<gio::ListStore>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +53,7 @@ impl ObjectImpl for Window {
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
obj.setup_model();
|
obj.setup_model();
|
||||||
obj.restore_data();
|
// obj.restore_data();
|
||||||
obj.setup_callbacks();
|
obj.setup_callbacks();
|
||||||
obj.setup_factory();
|
obj.setup_factory();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@ mod imp;
|
||||||
use crate::app_group::AppGroup;
|
use crate::app_group::AppGroup;
|
||||||
use crate::app_group::AppGroupData;
|
use crate::app_group::AppGroupData;
|
||||||
use crate::utils::data_path;
|
use crate::utils::data_path;
|
||||||
|
use crate::utils::set_group_scroll_policy;
|
||||||
use glib::FromVariant;
|
use glib::FromVariant;
|
||||||
use glib::Variant;
|
use glib::Variant;
|
||||||
use gtk4 as gtk;
|
use gtk4 as gtk;
|
||||||
use gtk4::Button;
|
|
||||||
use gtk4::Dialog;
|
use gtk4::Dialog;
|
||||||
use gtk4::Entry;
|
use gtk4::Entry;
|
||||||
use gtk4::Label;
|
use gtk4::Label;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::grid_item::GridItem;
|
use crate::grid_item::GridItem;
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
|
|
@ -39,7 +38,7 @@ impl Window {
|
||||||
Object::new(&[("application", app)]).expect("Failed to create `Window`.")
|
Object::new(&[("application", app)]).expect("Failed to create `Window`.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app_model(&self) -> &gio::ListStore {
|
fn _app_model(&self) -> &gio::ListStore {
|
||||||
// Get state
|
// Get state
|
||||||
let imp = imp::Window::from_instance(self);
|
let imp = imp::Window::from_instance(self);
|
||||||
imp.app_model.get().expect("Could not get model")
|
imp.app_model.get().expect("Could not get model")
|
||||||
|
|
@ -140,6 +139,7 @@ impl Window {
|
||||||
app_names: Vec::new(),
|
app_names: Vec::new(),
|
||||||
category: "Utility".to_string(),
|
category: "Utility".to_string(),
|
||||||
}),
|
}),
|
||||||
|
// Example of group with app name
|
||||||
// AppGroup::new(AppGroupData {
|
// AppGroup::new(AppGroupData {
|
||||||
// id: 0,
|
// id: 0,
|
||||||
// name: "Custom Web".to_string(),
|
// name: "Custom Web".to_string(),
|
||||||
|
|
@ -191,7 +191,14 @@ impl Window {
|
||||||
.expect("could not downcast sort list model to filter list model");
|
.expect("could not downcast sort list model to filter list model");
|
||||||
|
|
||||||
let entry = &imp.entry;
|
let entry = &imp.entry;
|
||||||
|
let scroll_window = &imp.group_scroll_window.get();
|
||||||
|
|
||||||
|
// dynamically set scroll method bc of buggy gtk scroll behavior
|
||||||
|
self.group_model().connect_items_changed(
|
||||||
|
glib::clone!(@weak scroll_window => move |scroll_list_model, _i, _rmv_cnt, _add_cnt| {
|
||||||
|
set_group_scroll_policy(&scroll_window, scroll_list_model.n_items());
|
||||||
|
}),
|
||||||
|
);
|
||||||
// Launch the application when an item of the list is activated
|
// Launch the application when an item of the list is activated
|
||||||
app_grid_view.connect_activate(move |grid_view, position| {
|
app_grid_view.connect_activate(move |grid_view, position| {
|
||||||
let model = grid_view.model().unwrap();
|
let model = grid_view.model().unwrap();
|
||||||
|
|
@ -218,14 +225,16 @@ impl Window {
|
||||||
|
|
||||||
// on activation change the group filter model to use the app names, and category
|
// on activation change the group filter model to use the app names, and category
|
||||||
group_grid_view.connect_activate(glib::clone!(@weak app_filter_model, @weak window => move |grid_view, position| {
|
group_grid_view.connect_activate(glib::clone!(@weak app_filter_model, @weak window => move |grid_view, position| {
|
||||||
|
println!("grid view activated.");
|
||||||
let group_model = grid_view.model().unwrap().downcast::<gtk::SingleSelection>()
|
let group_model = grid_view.model().unwrap().downcast::<gtk::SingleSelection>()
|
||||||
.expect("could not downcast app group view model to single selection model")
|
.expect("could not downcast app group view model to single selection model")
|
||||||
.model()
|
.model()
|
||||||
.downcast::<gio::ListStore>()
|
.downcast::<gio::ListStore>()
|
||||||
.expect("could not downcast app group view selection model to list store model");
|
.expect("could not downcast app group view selection model to list store model");
|
||||||
// if last item in the model, don't change filter, instead show dialog for adding new group!
|
|
||||||
|
// if last item in the model, don't change filter, instead show dialog for adding new group!
|
||||||
if position == group_model.n_items() - 1 {
|
if position == group_model.n_items() - 1 {
|
||||||
let entry = Entry::new();
|
let dialog_entry = Entry::new();
|
||||||
let label = Label::new(Some("Name"));
|
let label = Label::new(Some("Name"));
|
||||||
label.set_justify(gtk4::Justification::Left);
|
label.set_justify(gtk4::Justification::Left);
|
||||||
label.set_xalign(0.0);
|
label.set_xalign(0.0);
|
||||||
|
|
@ -237,12 +246,11 @@ impl Window {
|
||||||
.margin_bottom(12)
|
.margin_bottom(12)
|
||||||
.margin_end(12)
|
.margin_end(12)
|
||||||
.margin_start(12)
|
.margin_start(12)
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
vbox.append(&label);
|
vbox.append(&label);
|
||||||
vbox.append(&entry);
|
vbox.append(&dialog_entry);
|
||||||
|
|
||||||
let dialog = Dialog::builder()
|
let dialog = Dialog::builder()
|
||||||
.modal(true)
|
.modal(true)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.use_header_bar(true.into())
|
.use_header_bar(true.into())
|
||||||
|
|
@ -251,26 +259,49 @@ impl Window {
|
||||||
.title("New App Group")
|
.title("New App Group")
|
||||||
.child(&vbox)
|
.child(&vbox)
|
||||||
.build();
|
.build();
|
||||||
dialog.add_buttons(&[("Apply", gtk4::ResponseType::Apply), ("Cancel", gtk4::ResponseType::Cancel)]);
|
let app = window
|
||||||
// dialog.add_action_widget(>k4::Button::new(), gtk4::ResponseType::Apply);
|
.application()
|
||||||
dialog.connect_response(glib::clone!(@weak entry, @weak group_model => move |dialog, response_type| {
|
.expect("could not get application from window");
|
||||||
println!("dialog should be closing...");
|
|
||||||
if response_type == gtk4::ResponseType::Apply {
|
dialog.set_application(Some(&app));
|
||||||
let new_app_group = AppGroup::new(AppGroupData {
|
dialog.add_buttons(&[
|
||||||
id: 0,
|
("Apply", gtk4::ResponseType::Apply),
|
||||||
name: entry.text().to_string(),
|
("Cancel", gtk4::ResponseType::Cancel),
|
||||||
icon: "folder".to_string(),
|
]);
|
||||||
mutable: true,
|
|
||||||
app_names: vec![],
|
dialog.connect_response(
|
||||||
category: "".to_string(),
|
glib::clone!(@weak dialog_entry, @weak group_model => move |dialog, response_type| {
|
||||||
});
|
println!("dialog should be closing...");
|
||||||
group_model.insert(group_model.n_items() - 1, &new_app_group);
|
let name = dialog_entry.text().to_string();
|
||||||
|
if response_type == gtk4::ResponseType::Apply && name != "" {
|
||||||
|
let new_app_group = AppGroup::new(AppGroupData {
|
||||||
|
id: 0,
|
||||||
|
name: name,
|
||||||
|
icon: "folder".to_string(),
|
||||||
|
mutable: true,
|
||||||
|
app_names: vec![],
|
||||||
|
category: "".to_string(),
|
||||||
|
});
|
||||||
|
group_model.insert(group_model.n_items() - 1, &new_app_group);
|
||||||
|
}
|
||||||
|
dialog.emit_close();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
dialog.connect_is_active_notify(move |win| {
|
||||||
|
let app = win
|
||||||
|
.application()
|
||||||
|
.expect("could not get application from window");
|
||||||
|
let active_window = app
|
||||||
|
.active_window()
|
||||||
|
.expect("no active window available, closing app library.");
|
||||||
|
dbg!(&active_window);
|
||||||
|
if win == &active_window && !win.is_active() {
|
||||||
|
println!("no focus");
|
||||||
|
// close top level window
|
||||||
|
window.close();
|
||||||
}
|
}
|
||||||
dialog.emit_close();
|
});
|
||||||
}));
|
dialog.show();
|
||||||
// let flags = gtk4::DialogFlags::MODAL;
|
|
||||||
// let dialog = Dialog::with_buttons(Some("New App Group"), Some(&window), flags, &[("Apply", gtk4::ResponseType::Apply), ("Cancel", gtk4::ResponseType::Cancel)]);
|
|
||||||
dialog.present();
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// update the application filter
|
// update the application filter
|
||||||
|
|
@ -308,35 +339,6 @@ impl Window {
|
||||||
});
|
});
|
||||||
group_filter_model.set_filter(Some(new_filter).as_ref());
|
group_filter_model.set_filter(Some(new_filter).as_ref());
|
||||||
}));
|
}));
|
||||||
// can't listen to select signal on grid view, but this a good to know example...
|
|
||||||
// if group_grid_view.connect_local("select", true, glib::clone!(@weak group_filter_model => @default-return None, move |args| {
|
|
||||||
// let grid_view = args[0].get::<gtk::GridView>().unwrap();
|
|
||||||
// let position = args[1].get::<u32>().unwrap();
|
|
||||||
// // on activation change the group filter model to use the app names, and category
|
|
||||||
// let model = grid_view.model().unwrap();
|
|
||||||
// let app_info = model
|
|
||||||
// .item(position)
|
|
||||||
// .unwrap()
|
|
||||||
// .downcast::<AppGroup>()
|
|
||||||
// .unwrap();
|
|
||||||
// let category: String;
|
|
||||||
// if let Ok(category_prop) = app_info.property("category") {
|
|
||||||
// category = category_prop.get::<String>().unwrap_or("".to_string()).to_lowercase();
|
|
||||||
// } else {
|
|
||||||
// category = "".to_string();
|
|
||||||
// }
|
|
||||||
// let new_filter: gtk::CustomFilter = gtk::CustomFilter::new(move |obj| {
|
|
||||||
// let app = obj
|
|
||||||
// .downcast_ref::<gio::DesktopAppInfo>()
|
|
||||||
// .expect("The Object needs to be of type AppInfo");
|
|
||||||
// match app.categories() {
|
|
||||||
// Some(categories) => categories.to_string().to_lowercase().contains(&category),
|
|
||||||
// None => false,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// group_filter_model.set_filter(Some(new_filter).as_ref());
|
|
||||||
// None
|
|
||||||
// })).is_err() { println!("Failed to connect to grid view select...") };
|
|
||||||
|
|
||||||
entry.connect_changed(
|
entry.connect_changed(
|
||||||
glib::clone!(@weak app_filter_model, @weak sorted_model => move |search: >k::SearchEntry| {
|
glib::clone!(@weak app_filter_model, @weak sorted_model => move |search: >k::SearchEntry| {
|
||||||
|
|
@ -398,19 +400,18 @@ impl Window {
|
||||||
window.close();
|
window.close();
|
||||||
}));
|
}));
|
||||||
self.add_action(&action_quit);
|
self.add_action(&action_quit);
|
||||||
|
window.connect_is_active_notify(move |win| {
|
||||||
// window.connect_is_active_notify(|win| {
|
let app = win
|
||||||
// let app = win
|
.application()
|
||||||
// .application()
|
.expect("could not get application from window");
|
||||||
// .expect("could not get application from window");
|
let active_window = app
|
||||||
// let active_window = app
|
.active_window()
|
||||||
// .active_window()
|
.expect("no active window available, closing app library.");
|
||||||
// .expect("no active window available, closing app library.");
|
dbg!(&active_window);
|
||||||
// dbg!(&active_window);
|
if win == &active_window && !win.is_active() {
|
||||||
// if !active_window.is_active() {
|
win.close();
|
||||||
// win.close();
|
}
|
||||||
// }
|
});
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_factory(&self) {
|
fn setup_factory(&self) {
|
||||||
|
|
@ -465,9 +466,11 @@ impl Window {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|data| AppGroup::new(data).upcast::<Object>())
|
.map(|data| AppGroup::new(data).upcast::<Object>())
|
||||||
.collect();
|
.collect();
|
||||||
|
let scroll_window = &imp::Window::from_instance(self).group_scroll_window;
|
||||||
|
|
||||||
// Insert restored objects into model
|
// Insert restored objects into model
|
||||||
self.group_model().splice(3, 0, &app_group_objects);
|
self.group_model().splice(3, 0, &app_group_objects);
|
||||||
|
set_group_scroll_policy(&scroll_window, self.group_model().n_items());
|
||||||
} else {
|
} else {
|
||||||
println!("Backup file does not exist yet {:?}", data_path());
|
println!("Backup file does not exist yet {:?}", data_path());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,21 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGridView" id="group_grid_view">
|
<object class="GtkScrolledWindow" id="group_scroll_window">
|
||||||
<property name="min-columns">8</property>
|
<property name="hscrollbar-policy">never</property>
|
||||||
<property name="max-columns">8</property>
|
<property name="vscrollbar-policy">never</property>
|
||||||
<property name="single-click-activate">true</property>
|
<property name="propagate-natural-height">true</property>
|
||||||
<property name="enable-rubberband">false</property>
|
<property name="min-content-height">150</property>
|
||||||
|
<property name="max-content-height">300</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGridView" id="group_grid_view">
|
||||||
|
<property name="min-columns">8</property>
|
||||||
|
<property name="max-columns">8</property>
|
||||||
|
<property name="single-click-activate">true</property>
|
||||||
|
<property name="enable-rubberband">false</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue