basic plugin!

This commit is contained in:
Ashley Wulber 2022-01-11 00:05:57 -05:00
parent 7a8e3fe492
commit 537539f43d
13 changed files with 332 additions and 224 deletions

View file

@ -1,3 +1,12 @@
use crate::dock_item::DockItem;
use crate::dock_object::DockObject;
use crate::plugin;
use crate::utils::data_path;
use crate::BoxedWindowList;
use crate::Event;
use crate::Item;
use crate::PLUGINS;
use crate::TX;
use cascade::cascade;
use gdk4::ContentProvider;
use gdk4::Display;
@ -17,17 +26,12 @@ use gtk4::SignalListItemFactory;
use gtk4::Window;
use gtk4::{gio, glib};
use gtk4::{DragSource, GestureClick};
use std::ffi::CStr;
use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::path::Path;
use crate::dock_item::DockItem;
use crate::dock_object::DockObject;
use crate::utils::data_path;
use crate::BoxedWindowList;
use crate::Event;
use crate::Item;
use crate::TX;
mod imp;
glib::wrapper! {
@ -97,41 +101,122 @@ impl DockList {
let model = self.model();
model.splice(model.n_items(), 0, &dock_objects);
return;
}
}
eprintln!("Error loading saved apps!");
let model = &self.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())
{
model.append(&DockObject::new(app_info));
} else {
eprintln!("Error loading saved apps!");
let model = &self.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())
{
model.append(&DockObject::new(app_info));
} else {
// println!("Ignoring {}", path);
}
} else {
// println!("Ignoring {}", path);
// println!("error loading {}", path);
}
} else {
// println!("error loading {}", path);
}
}
}
}
})
}
});
})
}
});
}
// TODO load saved plugins here... for now, load the hardcoded example.
// TODO unload plugin library before the dynamic library is changed, otherwise, it will crash after segfault
// TODO unload plugin on removal from model
// TODO dnd for plugin? I think they should either be at the start or end of the dock and not draggable
// TODO call plugin click handler on click or if it is not provided by the library, open the popover menu instead
let mut path_dir = glib::user_data_dir();
path_dir.push(crate::ID);
std::fs::create_dir_all(&path_dir).expect("Could not create directory.");
path_dir.push("plugins");
std::fs::create_dir_all(&path_dir).expect("Could not create directory.");
let mut path = path_dir.clone();
path.push("dock_plugin_uwu.so");
let mut path_css = path_dir.clone();
path_css.push("dock_plugin_uwu.css");
let path = path
.as_os_str()
.to_str()
.expect("plugin path needs to be a valid string");
let provider = gtk4::CssProvider::new();
if let Ok(f) = File::open(path_css) {
let mut reader = BufReader::new(f);
let mut buffer = Vec::new();
if reader.read_to_end(&mut buffer).is_ok() {
provider.load_from_data(&buffer);
// Add the provider to the default screen
gtk4::StyleContext::add_provider_for_display(
&gdk4::Display::default().expect("Error initializing GTK CSS provider."),
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
} else {
eprintln!("loading plugin css failed");
}
} else {
eprintln!("loading plugin css failed");
}
let (popover_menu, image, name, lib) = unsafe {
let lib = libloading::Library::new(path).unwrap();
// store library until unloading the plugin
let image_func: libloading::Symbol<unsafe extern "C" fn() -> *mut gtk4_sys::GtkWidget> =
lib.get(b"dock_plugin_image").unwrap();
let popover_func: libloading::Symbol<
unsafe extern "C" fn() -> *mut gtk4_sys::GtkWidget,
> = lib.get(b"dock_plugin_popover_menu").unwrap();
let name_func: libloading::Symbol<
unsafe extern "C" fn() -> *const std::os::raw::c_char,
> = lib.get(b"dock_plugin_name").unwrap();
// click handler is optional
(popover_func(), image_func(), name_func(), lib)
};
if let Ok(ref mut mutex) = PLUGINS.try_lock() {
mutex.insert(String::from(path), lib);
}
let name = if !name.is_null() {
unsafe { String::from(CStr::from_ptr(name).to_str().unwrap_or_default()) }
} else {
String::new()
};
let image = if !image.is_null() {
unsafe { gtk4::glib::translate::from_glib_none::<_, gtk4::Widget>(image).unsafe_cast() }
} else {
gtk4::Image::new()
};
let popover_menu = if !popover_menu.is_null() {
unsafe {
gtk4::glib::translate::from_glib_none::<_, gtk4::Widget>(popover_menu).unsafe_cast()
}
} else {
gtk4::Box::new(Orientation::Vertical, 4)
};
let boxed_plugin = plugin::BoxedDockPlugin {
path: String::from(path),
name,
image,
popover_menu,
};
let model = self.model();
model.append(&DockObject::from_plugin(boxed_plugin).upcast::<Object>());
}
fn store_data(model: &gio::ListStore) {
@ -160,6 +245,7 @@ impl DockList {
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");
// TODO save plugins here for now examples are hardcoded and don't need to be saved
}
fn layout(&self) {
@ -420,7 +506,6 @@ impl DockList {
if type_ == DockListType::Saved {
if let Some(old_handle) = drag_end.replace(Some(self_.connect_drag_end(
glib::clone!(@weak model => move |_self, _drag, _delete_data| {
dbg!(_delete_data);
if _delete_data {
model.remove(index);
glib::MainContext::default().spawn_local(async move {
@ -526,7 +611,7 @@ impl DockList {
}),
);
factory.connect_bind(move |_, list_item| {
let application_object = list_item
let dock_object = list_item
.item()
.expect("The item has to exist.")
.downcast::<DockObject>()
@ -536,7 +621,7 @@ impl DockList {
.expect("The list item child needs to exist.")
.downcast::<DockItem>()
.expect("The list item type needs to be `DockItem`");
dock_item.set_app_info(&application_object);
dock_item.set_dock_object(&dock_object);
});
// Set the factory of the list view
imp.list_view.get().unwrap().set_factory(Some(&factory));