Messing around with plugins and gtk and icons and stuff

This commit is contained in:
Asriel Camora 2023-04-29 17:42:31 -07:00
parent d3ee28c542
commit a87f227973
37 changed files with 201 additions and 72 deletions

11
Cargo.lock generated
View file

@ -297,6 +297,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fragile"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
[[package]]
name = "fs2"
version = "0.4.3"
@ -1183,10 +1189,11 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "plugins_core"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"async-trait",
"generator",
"gtk4",
"rustc_version",
"semver",
"tokio",
@ -1197,6 +1204,8 @@ dependencies = [
name = "plugins_epic"
version = "0.1.0"
dependencies = [
"build-scripts",
"fragile",
"plugins_core",
"rand",
"reqwest",

View file

@ -21,4 +21,4 @@ tokio = "*"
[build-dependencies]
embed-manifest = "*"
winres = "*"
build-scripts = { path = "build" }
build-scripts = { path = "../build" }

View file

@ -26,6 +26,11 @@ ApplicationWindow window {
icon-name: "fa-circle-info-solid-symbolic";
}
[end]
Button button-games {
icon-name: "fa-play-solid-symbolic";
}
[end]
Button button-inspector {
icon-name: "fa-bug-solid-symbolic";

View file

@ -5,19 +5,24 @@ template GtkListItem {
activatable: true;
selectable: true;
child: Box {
orientation: vertical;
Label {
label: bind GtkListItem.item as <$L4PluginModel>.name;
Box {
orientation: vertical;
Image {
paintable: bind GtkListItem.item as <$L4PluginModel>.icon-paintable;
pixel-size: 64;
}
}
Label {
label: bind GtkListItem.item as <$L4PluginModel>.id;
}
Label {
label: bind GtkListItem.item as <$L4PluginModel>.version;
}
Label {
label: bind GtkListItem.item as <$L4PluginModel>.description;
Box {
orientation: vertical;
Label {
label: bind GtkListItem.item as <$L4PluginModel>.name;
}
Label {
label: bind GtkListItem.item as <$L4PluginModel>.version;
}
}
};
}

View file

@ -2,7 +2,6 @@
mod modules;
mod widgets;
use deps::archive::{ArchiveMut, ArchiveMutTrait, StreamMutTrait};
use gtk::prelude::*;
use gtk::{gio, glib};
use widgets::Application;
@ -32,11 +31,5 @@ async fn main() -> glib::ExitCode {
glib::set_application_name("L4");
glib::set_program_name(Some("L4"));
let mut archive = ArchiveMut::new("yo.ar").unwrap();
let mut stream = archive.stream_mut(4).unwrap();
let mut iter = stream.iter_bytes_mut(63..4030).unwrap();
while let Some(_slice) = iter.next() {}
Application::from_application_id(APP_ID).run()
}

View file

@ -0,0 +1,7 @@
mod module;
mod module_list;
mod registry;
pub use module::*;
pub use module_list::*;
pub use registry::*;

View file

@ -1,7 +1,6 @@
use std::{cell::RefCell, rc::Rc};
use gtk::glib;
use gtk::prelude::*;
use std::{cell::RefCell, rc::Rc};
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum LoadPhase {

View file

@ -51,6 +51,10 @@ impl ModuleList {
this
}
pub fn application_window(&self) -> gtk::ApplicationWindow {
self.get_object::<gtk::ApplicationWindow>("window")
}
fn load(&mut self, phase: LoadPhase) {
for initializer in self.registry.iter_phase(phase) {
self.modules.push(initializer(self));

View file

@ -1,4 +1,5 @@
use super::*;
use super::{LoadPhase, Metadata, Module, ModuleList};
use crate::modules;
use once_cell::unsync::Lazy;
use std::cell::RefCell;
use std::collections::BTreeMap;
@ -19,10 +20,10 @@ impl ModuleRegistry {
fn create() -> BTreeMap<Metadata, Initializer> {
let mut reg = BTreeMap::new();
Self::register::<UIPreInit>(&mut reg);
Self::register::<UIPostInit>(&mut reg);
Self::register::<TitleButtons>(&mut reg);
Self::register::<Plugins>(&mut reg);
Self::register::<modules::UIPreInit>(&mut reg);
Self::register::<modules::UIPostInit>(&mut reg);
Self::register::<modules::TitleButtons>(&mut reg);
Self::register::<modules::Plugins>(&mut reg);
reg
}

View file

@ -1,14 +1,11 @@
mod module;
mod module_list;
mod meta;
mod plugins;
mod registry;
mod prelude;
mod title_buttons;
mod ui_postinit;
mod ui_preinit;
pub use module::*;
pub use module_list::ModuleList;
pub use meta::ModuleList;
pub use plugins::Plugins;
pub use title_buttons::TitleButtons;
pub use ui_postinit::UIPostInit;

View file

@ -1,4 +1,4 @@
use super::module::*;
use super::prelude::*;
use crate::widgets::PluginModel;
use deps::plugins::PluginRegistry;

View file

@ -0,0 +1 @@
pub use super::meta::{LoadPhase, Metadata, Module, ModuleCtx};

View file

@ -1,4 +1,4 @@
use super::module::*;
use super::prelude::*;
use deps::utils::{signal, SignalHolder};
use gtk::glib;
use gtk::prelude::*;

View file

@ -1,4 +1,4 @@
use super::module::*;
use super::prelude::*;
use deps::utils::{signal, SignalHolder, UsesDpi};
use gtk::prelude::*;

View file

@ -1,4 +1,4 @@
use super::module::*;
use super::prelude::*;
use gtk::prelude::*;
use gtk::{gdk, IconTheme};

View file

@ -1,5 +1,4 @@
use crate::modules::{ModuleCtx, ModuleList};
use crate::modules::ModuleList;
use adw::subclass::prelude::*;
use gtk::{glib, traits::GtkWindowExt};
use once_cell::unsync::OnceCell;
@ -31,7 +30,7 @@ impl ApplicationImpl for Application {
.modules
.get_or_init(|| ModuleList::new(self.obj().as_ref()));
let window = modules.get_object::<gtk::ApplicationWindow>("window");
let window = modules.application_window();
window.minimize();
window.present();
}

View file

@ -1,20 +1,25 @@
use glib::ParamSpecString;
use glib::{ParamSpec, Value};
use gtk::glib;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::{
gdk::Paintable, gdk::Texture, glib, glib::ParamSpecObject, prelude::*, subclass::prelude::*,
};
use once_cell::sync::Lazy;
use once_cell::unsync::OnceCell;
use plugins_core::Plugin;
use plugins_core::prelude::*;
use std::sync::Weak;
pub struct PluginModel {
plugin: OnceCell<Weak<dyn Plugin>>,
plugin: OnceCell<Weak<dyn core::Plugin>>,
icon_paintable: OnceCell<Paintable>,
}
impl PluginModel {
pub fn set_plugin(&self, plugin: Weak<dyn Plugin>) {
self.plugin.set(plugin).ok().unwrap();
pub fn set_plugin(&self, plugin: Weak<dyn core::Plugin>) {
self.plugin.set(plugin.clone()).ok().unwrap();
let plugin = plugin.upgrade().unwrap();
self.icon_paintable
.set(Texture::for_pixbuf(&plugin.image_with_fallback(ImageType::Icon)).into())
.unwrap();
}
}
@ -26,6 +31,7 @@ impl ObjectSubclass for PluginModel {
fn new() -> Self {
Self {
plugin: Default::default(),
icon_paintable: Default::default(),
}
}
}
@ -38,6 +44,9 @@ impl ObjectImpl for PluginModel {
ParamSpecString::builder("name").read_only().build(),
ParamSpecString::builder("description").read_only().build(),
ParamSpecString::builder("version").read_only().build(),
ParamSpecObject::builder::<Paintable>("icon-paintable")
.read_only()
.build(),
]
});
PROPERTIES.as_ref()
@ -51,6 +60,7 @@ impl ObjectImpl for PluginModel {
"name" => plugin.name().to_value(),
"description" => plugin.description().to_value(),
"version" => plugin.version().to_string().to_value(),
"icon-paintable" => self.icon_paintable.get().unwrap().to_value(),
_ => unimplemented!(),
};
}

View file

@ -3,6 +3,10 @@ use std::{env, path::Path};
use walkdir::WalkDir;
fn batch_compile<P: AsRef<Path>>(sources: &[P], input_dir: &str, output_dir: &str) {
if sources.is_empty() {
return;
}
let mut command = Command::new("blueprint-compiler");
command.arg("batch-compile").arg(output_dir).arg(input_dir);

4
deps/plugins/app.rs vendored
View file

@ -1,13 +1,13 @@
use std::path::PathBuf;
use plugins_core::{InstalledApp as InstalledAppTrait, Version};
use plugins_core::prelude::*;
pub struct InstalledApp {
archive_path: PathBuf,
//archive_metadata: Archive::Metadata,
}
impl InstalledAppTrait for InstalledApp {
impl core::InstalledApp for InstalledApp {
fn id(&self) -> &str {
""
}

View file

@ -1,10 +1,11 @@
use plugins_core::{Client as ClientTrait, Identity, Version};
use gtk::gdk_pixbuf::Pixbuf;
use plugins_core::prelude::*;
pub struct Client;
impl ClientTrait for Client {}
impl core::Client for Client {}
impl Identity for Client {
impl core::Identity for Client {
fn id(&self) -> &str {
env!("CARGO_PKG_NAME")
}
@ -32,4 +33,8 @@ impl Identity for Client {
fn license(&self) -> &str {
env!("CARGO_PKG_LICENSE")
}
fn image(&self, _image_type: ImageType) -> Option<Pixbuf> {
None
}
}

View file

@ -63,6 +63,12 @@ impl PluginHandle {
)));
}
if !decl.gresource.is_empty() {
let bytes = gtk::glib::Bytes::from_static(decl.gresource);
let resource = gtk::gio::Resource::from_data(&bytes)?;
gtk::gio::resources_register(&resource);
}
let plugin = (decl.register)(client);
Ok(Self {

View file

@ -1,6 +1,6 @@
[package]
name = "plugins_core"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -11,6 +11,7 @@ toml = "*"
tokio = {version = "*", features = ["full"]}
async-trait = "*"
generator = "*"
gtk = { package = "gtk4", version = "0.6.6", features = ["v4_10"] }
[build-dependencies]
rustc_version = "*"
rustc_version = "*"

View file

@ -1,4 +1,3 @@
fn main() {
let version = rustc_version::version().unwrap();
println!("cargo:rustc-env=RUSTC_VERSION={}", version);
println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version::version().unwrap());
}

View file

@ -2,11 +2,16 @@
#![feature(trait_alias)]
#![allow(incomplete_features)]
pub use async_trait::async_trait;
use async_trait::async_trait;
use generator::Generator;
pub use semver::Version;
use gtk::{
self,
gdk_pixbuf::{Colorspace, Pixbuf},
};
use semver::Version;
use std::path::Path;
use std::sync::Arc;
pub mod prelude;
pub static CORE_VERSION: &str = env!("CARGO_PKG_VERSION");
pub static RUSTC_VERSION: &str = env!("RUSTC_VERSION");
@ -40,6 +45,13 @@ pub enum AuthStep {
Screen(),
}
#[non_exhaustive]
#[derive(Clone, Copy)]
pub enum ImageType {
Icon, // Aspect: 1:1, Size: 256x256
Banner, // Aspect 16:9, Size: 1920x1080
}
pub type AuthSession<'a> = Generator<'a, AuthStep, AuthStep>;
pub trait Identity: Send + Sync {
@ -50,6 +62,19 @@ pub trait Identity: Send + Sync {
fn authors(&self) -> Vec<&str>;
fn repository_url(&self) -> &str;
fn license(&self) -> &str;
fn image(&self, image_type: ImageType) -> Option<Pixbuf>;
fn image_with_fallback(&self, image_type: ImageType) -> Pixbuf {
self.image(image_type)
.or_else(|| {
let image_size = match image_type {
ImageType::Icon => (256, 256),
ImageType::Banner => (1920, 1080),
};
Pixbuf::new(Colorspace::Rgb, true, 32, image_size.0, image_size.1)
})
.unwrap()
}
}
#[async_trait]
@ -68,17 +93,19 @@ pub trait Client: Identity {}
pub struct PluginDeclaration {
pub rustc_version: &'static str,
pub core_version: &'static str,
pub gresource: &'static [u8],
pub register: unsafe fn(client: Arc<dyn Client>) -> Arc<dyn Plugin>,
}
#[macro_export]
macro_rules! export_plugin {
($register:ty) => {
($register:ty, $gresource_path:expr) => {
#[doc(hidden)]
#[no_mangle]
pub static plugin_declaration: $crate::PluginDeclaration = $crate::PluginDeclaration {
rustc_version: $crate::RUSTC_VERSION,
core_version: $crate::CORE_VERSION,
gresource: include_bytes!(concat!(env!("OUT_DIR"), "/", $gresource_path)),
register: |client| Arc::new(plugin::Plugin::new(client)),
};
};

View file

@ -0,0 +1,9 @@
pub use crate::ImageType;
pub use async_trait::async_trait;
pub use gtk;
pub use gtk::prelude::*;
pub use semver::Version;
pub mod core {
pub use crate::{App, AuthSession, AuthStep, Client, Identity, InstalledApp, Plugin, User};
}

View file

@ -15,4 +15,8 @@ serde = { version = "*", features = ["derive"] }
serde-enum-str = "*"
rand = "*"
serde_json = "*"
tokio = "*"
tokio = "*"
fragile = "*"
[build-dependencies]
build-scripts = { path = "../../build" }

7
plugins/epic/build.rs Normal file
View file

@ -0,0 +1,7 @@
fn main() {
build_scripts::blueprint_compile_resources(
"resources",
"resources/resources.gresource.xml",
"epic.gresource",
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/me/workingrobot/l4/epic">
<file compressed="true" preprocess="xml-stripblanks">icon.svg</file>
<file compressed="true">banner.png</file>
<!--<file compressed="true" preprocess="xml-stripblanks">main.ui</file>-->
</gresource>
</gresources>

View file

@ -9,6 +9,6 @@ mod plugin;
mod user;
mod web;
plugins_core::export_plugin!(plugin::Plugin);
plugins_core::export_plugin!(plugin::Plugin, "epic.gresource");
pub use user::*;

View file

@ -1,26 +1,46 @@
use crate::web::ClientAuthed;
use super::User;
use plugins_core::{async_trait, App, AuthSession, Client, Version};
use fragile::Fragile;
use gtk::gdk_pixbuf::Pixbuf;
use plugins_core::prelude::*;
use std::sync::Arc;
pub struct Plugin {
client: Arc<dyn Client>,
client: Arc<dyn core::Client>,
user: Option<User>,
web_client: Option<ClientAuthed>,
image_icon: Fragile<Pixbuf>,
image_banner: Fragile<Pixbuf>,
}
impl Plugin {
pub fn new(client: Arc<dyn Client>) -> Self {
pub fn new(client: Arc<dyn core::Client>) -> Self {
Self {
client,
user: None,
web_client: None,
image_icon: Pixbuf::from_resource_at_scale(
"/me/workingrobot/l4/epic/icon.svg",
256,
256,
true,
)
.unwrap()
.into(),
image_banner: Pixbuf::from_resource_at_scale(
"/me/workingrobot/l4/epic/banner.png",
1920,
1080,
true,
)
.unwrap()
.into(),
}
}
}
impl plugins_core::Identity for Plugin {
impl core::Identity for Plugin {
fn id(&self) -> &str {
"epic"
}
@ -48,15 +68,23 @@ impl plugins_core::Identity for Plugin {
fn license(&self) -> &str {
env!("CARGO_PKG_LICENSE")
}
fn image(&self, image_type: ImageType) -> Option<Pixbuf> {
match image_type {
ImageType::Icon => Some(self.image_icon.get().clone()),
ImageType::Banner => Some(self.image_banner.get().clone()),
_ => None,
}
}
}
#[async_trait]
impl plugins_core::Plugin for Plugin {
fn client(&self) -> &dyn Client {
impl core::Plugin for Plugin {
fn client(&self) -> &dyn core::Client {
self.client.as_ref()
}
async fn get_available_apps(&self) -> Option<Vec<Box<dyn App>>> {
async fn get_available_apps(&self) -> Option<Vec<Box<dyn core::App>>> {
if self.get_user().await.is_some() {
Some(vec![])
} else {
@ -64,11 +92,11 @@ impl plugins_core::Plugin for Plugin {
}
}
async fn get_user(&self) -> Option<Box<dyn plugins_core::User>> {
async fn get_user(&self) -> Option<Box<dyn core::User>> {
unimplemented!()
}
async fn open_auth_session(&self) -> Option<AuthSession> {
async fn open_auth_session(&self) -> Option<core::AuthSession> {
unimplemented!()
}
}

View file

@ -1,4 +1,4 @@
use plugins_core::async_trait;
use plugins_core::prelude::async_trait;
use reqwest::RequestBuilder;
use serde::de::DeserializeOwned;