diff --git a/Cargo.lock b/Cargo.lock index 1729fab..625335a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.76" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "arrayvec" @@ -97,9 +97,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cairo-rs" -version = "0.18.3" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33613627f0dea6a731b0605101fad59ba4f193a52c96c4687728d822605a8a1" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ "bitflags 2.4.1", "cairo-sys-rs", @@ -131,9 +131,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.5" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" dependencies = [ "smallvec", "target-lexicon", @@ -171,7 +171,7 @@ dependencies = [ "deluxe-macros", "once_cell", "proc-macro2", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -184,7 +184,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -199,7 +199,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -274,25 +274,41 @@ dependencies = [ ] [[package]] -name = "futures-channel" -version = "0.3.29" +name = "futures" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -301,42 +317,44 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -346,9 +364,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.18.3" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446f32b74d22c33b7b258d4af4ffde53c2bf96ca2e29abdf1a785fe59bd6c82c" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -462,9 +480,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951bbd7fdc5c044ede9f05170f05a3ae9479239c3afdfe2d22d537a3add15c4e" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ "bitflags 2.4.1", "futures-channel", @@ -485,16 +503,16 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.18.3" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck", "proc-macro-crate 2.0.1", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -554,7 +572,7 @@ dependencies = [ "quick-xml", "quote", "serde", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -837,9 +855,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linux-raw-sys" @@ -877,9 +895,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memoffset" @@ -920,6 +938,9 @@ dependencies = [ name = "mod-manager" version = "0.1.0" dependencies = [ + "bytes", + "futures", + "gdk-pixbuf", "gettext-rs", "gsettings-macro", "gtk4", @@ -999,9 +1020,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -1014,9 +1035,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -1035,7 +1056,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -1046,9 +1067,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -1157,9 +1178,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -1176,9 +1197,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1295,11 +1316,11 @@ checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1327,35 +1348,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -1419,9 +1440,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "ccbca6f34534eb78dbee83f6b2c9442fea7113f43d9e80ea320f0972ae5dc08d" [[package]] name = "syn" @@ -1436,9 +1457,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -1481,47 +1502,47 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" [[package]] name = "temp-dir" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab" +checksum = "dd16aa9ffe15fe021c6ee3766772132c6e98dfa395a167e16864f61a9cfb71d6" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -1659,7 +1680,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -1795,7 +1816,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -1829,7 +1850,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2006,9 +2027,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.30" +version = "0.5.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" +checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 600532b..df50e79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,9 @@ tracing-subscriber = "0.3" reqwest = { version = "0.11", features = ["json", "blocking"] } serde = { version = "1.0", features = ["derive"] } gsettings-macro = "0.1.20" +gdk-pixbuf = "0.18.5" +bytes = "1.5.0" +futures = "0.3.30" [dependencies.adw] package = "libadwaita" diff --git a/data/resources/meson.build b/data/resources/meson.build index 436dec4..621bf0d 100644 --- a/data/resources/meson.build +++ b/data/resources/meson.build @@ -5,6 +5,7 @@ blueprints = custom_target('blueprints', 'ui/windows/main/pages/welcome.blp', 'ui/windows/main/pages/games_and_mods.blp', 'ui/windows/add_new_game/add_new_game.blp', + 'ui/components/card.blp', ), output: '.', command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'], diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml index ddb11bc..f1a4e22 100644 --- a/data/resources/resources.gresource.xml +++ b/data/resources/resources.gresource.xml @@ -6,6 +6,7 @@ ui/windows/main/pages/welcome.ui ui/windows/main/pages/games_and_mods.ui ui/windows/add_new_game/add_new_game.ui + ui/components/card.ui style.css - \ No newline at end of file + diff --git a/data/resources/style.css b/data/resources/style.css index e69de29..e142005 100644 --- a/data/resources/style.css +++ b/data/resources/style.css @@ -0,0 +1,4 @@ +.card image { + min-width: 500px; + min-height: 500px; +} diff --git a/data/resources/ui/components/card.blp b/data/resources/ui/components/card.blp new file mode 100644 index 0000000..77ef6f0 --- /dev/null +++ b/data/resources/ui/components/card.blp @@ -0,0 +1,21 @@ +using Gtk 4.0; +using Adw 1; + +template $Card: Box { + orientation: horizontal; + styles ["card"] + + Image image {} + + Box contents { + orientation: vertical; + + Label heading { + styles ["heading"] + } + + Label body { + styles ["body"] + } + } +} diff --git a/data/resources/ui/windows/main/pages/games_and_mods.blp b/data/resources/ui/windows/main/pages/games_and_mods.blp index 06924d6..61b3c41 100644 --- a/data/resources/ui/windows/main/pages/games_and_mods.blp +++ b/data/resources/ui/windows/main/pages/games_and_mods.blp @@ -28,7 +28,7 @@ template $GamesAndMods: Adw.Bin { content: Adw.NavigationPage{ title: "Mods"; tag: "content"; - + Adw.ToolbarView { [top] Adw.HeaderBar { @@ -36,7 +36,7 @@ template $GamesAndMods: Adw.Bin { SearchEntry search { } } - + content: Adw.Clamp { orientation: vertical; @@ -59,4 +59,4 @@ template $GamesAndMods: Adw.Bin { } }; } -} \ No newline at end of file +} diff --git a/src/api/loaders.rs b/src/api/loaders.rs new file mode 100644 index 0000000..4dc83f4 --- /dev/null +++ b/src/api/loaders.rs @@ -0,0 +1,55 @@ +// Inspired by https://github.com/xou816/spot/blob/aa1c57842c3f4e424eb62c69365438f904b2dc63/src/app/loader.rs +// Without the caching. +// +use bytes::Bytes; +use gdk_pixbuf::traits::PixbufLoaderExt; +use gdk_pixbuf::{Pixbuf, PixbufLoader}; +use std::io::{Error, ErrorKind, Write}; + +use crate::api::*; + +// A wrapper to be able to implement the Write trait on a PixbufLoader +struct LocalPixbufLoader<'a>(&'a PixbufLoader); + +impl<'a> Write for LocalPixbufLoader<'a> { + fn write(&mut self, buf: &[u8]) -> Result { + self.0 + .write(buf) + .map_err(|e| Error::new(ErrorKind::Other, format!("glib error: {e}")))?; + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<(), Error> { + self.0 + .close() + .map_err(|e| Error::new(ErrorKind::Other, format!("glib error: {e}")))?; + Ok(()) + } +} + +pub struct ImageLoader {} + +impl ImageLoader { + pub fn new() -> Self { + Self {} + } + + fn get_image(url: &str) -> Option { + let client = base::get_curse_forge_client().unwrap(); + let response = client.get(url).send().ok(); + response.unwrap().bytes().ok() + } + + pub fn load_remote(&self, url: &str, width: i32, height: i32) -> Option { + let pixbuf_loader = PixbufLoader::new(); + pixbuf_loader.set_size(width, height); + let mut loader = LocalPixbufLoader(&pixbuf_loader); + + if let Some(mut resp) = Self::get_image(url) { + loader.write_all(&resp).ok()?; + } + + pixbuf_loader.close().ok()?; + pixbuf_loader.pixbuf() + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index 049ea3c..4e5eb7a 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -6,3 +6,6 @@ pub use games::*; pub mod mods; pub use mods::*; + +pub mod loaders; +pub use loaders::*; diff --git a/src/api/mods.rs b/src/api/mods.rs index 3b81e08..805c53c 100644 --- a/src/api/mods.rs +++ b/src/api/mods.rs @@ -182,6 +182,16 @@ struct Module { fingerprint: i32, } +#[derive(Debug, Deserialize)] +pub struct Logo { + pub id: i32, + //pub mod_id: i32, + //pub title: String, + //pub description: String, + //pub thumbnail_url: String, + pub url: String, +} + #[derive(Debug, Deserialize)] pub struct Mod { pub name: String, @@ -189,6 +199,7 @@ pub struct Mod { //pub gameSlug: String, //pub gameId: i32, pub summary: String, + pub logo: Logo, //pub defaultFileId: i32, //pub downloadCount: i32, //pub latestFiles: Vec, diff --git a/src/application.rs b/src/application.rs index df6c57c..c50fefb 100644 --- a/src/application.rs +++ b/src/application.rs @@ -25,6 +25,7 @@ use tracing::{debug, info}; use crate::config::VERSION; use crate::config::{APP_ID, PKGDATADIR, PROFILE}; +use crate::dispatch::Worker; use crate::windows::main::ModManagerWindowMain; mod imp { @@ -32,7 +33,9 @@ mod imp { use super::*; #[derive(Debug, Default)] - pub struct ModManagerApplication {} + pub struct ModManagerApplication { + worker: Option, + } #[glib::object_subclass] impl ObjectSubclass for ModManagerApplication { diff --git a/src/components/card.rs b/src/components/card.rs new file mode 100644 index 0000000..377ee14 --- /dev/null +++ b/src/components/card.rs @@ -0,0 +1,73 @@ +use adw::subclass::prelude::*; +use glib::clone; +use gtk::CompositeTemplate; +use gtk::{gio, glib}; + +use crate::api::loaders::ImageLoader; +use crate::dispatch::Worker; + +mod imp { + + use super::*; + + #[derive(Debug, Default, CompositeTemplate)] + #[template(resource = "/dev/mnts/ModManager/ui/components/card.ui")] + pub struct Card { + #[template_child] + pub heading: TemplateChild, + + #[template_child] + pub body: TemplateChild, + + #[template_child] + pub image: TemplateChild, + } + + #[glib::object_subclass] + impl ObjectSubclass for Card { + const NAME: &'static str = "Card"; + type Type = super::Card; + type ParentType = gtk::Box; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } + } + + impl ObjectImpl for Card {} + impl WidgetImpl for Card {} + impl BoxImpl for Card {} +} + +glib::wrapper! { + pub struct Card(ObjectSubclass) + @extends gtk::Widget, gtk::Box, + @implements gio::ActionGroup, gio::ActionMap; +} + +impl Card { + pub fn new() -> Self { + glib::Object::builder().build() + } + + pub fn bind(&self, worker: Worker, heading: &str, body: &str, image_url: &str) { + let widget = self.imp(); + + widget.heading.set_text(heading); + widget.body.set_text(body); + + println!("image_url: {}", image_url); + + let url = image_url.to_string(); + + worker.send_local_task(clone!(@weak widget => async move { + let loader = ImageLoader::new(); + let pixbuf = loader.load_remote(url.as_str(), 800, 800); + widget.image.set_from_pixbuf(pixbuf.as_ref()); + })); + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs new file mode 100644 index 0000000..01a0d48 --- /dev/null +++ b/src/components/mod.rs @@ -0,0 +1,2 @@ +mod card; +pub use card::*; diff --git a/src/dispatch.rs b/src/dispatch.rs new file mode 100644 index 0000000..9973e6c --- /dev/null +++ b/src/dispatch.rs @@ -0,0 +1,41 @@ +use futures::channel::mpsc::{unbounded, UnboundedSender}; +use futures::future::Future; +use futures::stream::StreamExt; +use glib::source::Priority; +use gtk::glib; +use std::pin::Pin; + +pub type FutureTask = Pin + Send>>; +pub type FutureLocalTask = Pin>>; + +// The Worker (see below) is a glorified way to send an async task to the GLib(rs) future executor +pub fn spawn_task_handler(context: &glib::MainContext) -> Worker { + let (future_local_sender, future_local_receiver) = unbounded::(); + context.spawn_local_with_priority( + Priority::DEFAULT_IDLE, + future_local_receiver.for_each(|t| t), + ); + + let (future_sender, future_receiver) = unbounded::(); + context.spawn_with_priority(Priority::DEFAULT_IDLE, future_receiver.for_each(|t| t)); + + Worker(future_local_sender, future_sender) +} + +// Again, fancy name for an MPSC sender +// Actually two of them, in case you need to send local futures (no Send needed) +#[derive(Clone, Debug)] +pub struct Worker( + UnboundedSender, + UnboundedSender, +); + +impl Worker { + pub fn send_local_task + 'static>(&self, task: T) -> Option<()> { + self.0.unbounded_send(Box::pin(task)).ok() + } + + pub fn send_task + Send + 'static>(&self, task: T) -> Option<()> { + self.1.unbounded_send(Box::pin(task)).ok() + } +} diff --git a/src/main.rs b/src/main.rs index 6abb781..b7814a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,11 +17,14 @@ along with this program. If not, see . mod api; mod application; +mod components; mod config; +mod dispatch; mod settings; mod windows; use self::application::ModManagerApplication; +use self::dispatch::spawn_task_handler; use config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE}; use gettextrs::{gettext, LocaleCategory}; @@ -42,6 +45,8 @@ fn main() -> glib::ExitCode { let resources = gio::Resource::load(RESOURCES_FILE).expect("Could not load resources"); gio::resources_register(&resources); + let context = glib::MainContext::default(); + // Create a new GtkApplication. The application manages our main loop, // application windows, integration with the window manager/compositor, and // desktop features such as file opening and single-instance applications. @@ -51,5 +56,9 @@ fn main() -> glib::ExitCode { // exits. Upon return, we have our exit code to return to the shell. (This // is the code you see when you do `echo $?` after running a command in a // terminal. - app.run() + context.invoke_local(move || { + app.run(); + }); + + std::process::exit(0); } diff --git a/src/windows/main/pages/games_and_mods.rs b/src/windows/main/pages/games_and_mods.rs index 133b98b..804be47 100644 --- a/src/windows/main/pages/games_and_mods.rs +++ b/src/windows/main/pages/games_and_mods.rs @@ -15,22 +15,23 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +use adw::prelude::PreferencesRowExt; use adw::subclass::prelude::*; +use gtk::glib::clone; use gtk::prelude::*; use gtk::{gio, glib}; +use tracing::info; + +use crate::{ + api::{get_game_id, get_mods}, + components::Card, + dispatch::spawn_task_handler, + settings::{Game, ModManagerSettings}, + windows::ModManagerWindowAddNewGame, +}; mod imp { - use adw::prelude::PreferencesRowExt; - use gtk::glib::clone; - use tracing::info; - - use crate::{ - api::{games, get_game_id, get_mods}, - settings::{Game, ModManagerSettings}, - windows::ModManagerWindowAddNewGame, - }; - use super::*; #[derive(Debug, Default, gtk::CompositeTemplate)] @@ -66,6 +67,9 @@ mod imp { impl ObjectImpl for GamesAndMods { fn constructed(&self) { + let context = glib::MainContext::default(); + let worker = spawn_task_handler(&context); + self.parent_constructed(); let obj = self.obj(); @@ -101,17 +105,8 @@ mod imp { let mods = get_mods(&get_game_id(Game::TheSims4("".to_string()))) .iter() .for_each(|mod_| { - let card = gtk::Box::new(gtk::Orientation::Vertical, 10); - card.set_css_classes(&["card"]); - - let title = gtk::Label::new(Some(&mod_.name)); - title.set_css_classes(&["heading"]); - - let description = gtk::Label::new(Some(&mod_.summary)); - description.set_css_classes(&["body"]); - - card.append(&title); - card.append(&description); + let card = Card::new(); + card.bind(worker.clone(), &mod_.name, &mod_.summary, &mod_.logo.url); obj.imp().mods_list.append(&card); });