diff --git a/data/resources/ui/components/card.blp b/data/resources/ui/components/card.blp index bacc1a2..6f4046c 100644 --- a/data/resources/ui/components/card.blp +++ b/data/resources/ui/components/card.blp @@ -11,13 +11,20 @@ template $Card: Box { Box contents { orientation: vertical; + margin-start: 12; + margin-end: 12; + margin-top: 12; + margin-bottom: 12; Label heading { styles ["heading"] } Label body { + vexpand: true; styles ["body"] } + + Box footer {} } } 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 61b3c41..7d27e8e 100644 --- a/data/resources/ui/windows/main/pages/games_and_mods.blp +++ b/data/resources/ui/windows/main/pages/games_and_mods.blp @@ -37,24 +37,29 @@ template $GamesAndMods: Adw.Bin { } } - content: Adw.Clamp { + content: Box { orientation: vertical; Label title { - label: "Mods"; - styles ["title-1"] + label: "List of Mods"; + styles ["title-1"] } - Adw.Carousel { + Label { + halign: start; + margin-bottom: 12; + label: "Latest mods"; + styles ["title-2"] } - Adw.CarouselIndicatorDots {} - ScrolledWindow { - vexpand: true; - ListBox mods_list { - } + vexpand: true; + hexpand: true; + FlowBox mods_list { + row-spacing: 12; + column-spacing: 12; + homogeneous: true; + } } - }; } }; diff --git a/src/api/mods.rs b/src/api/mods.rs index 805c53c..ab91adc 100644 --- a/src/api/mods.rs +++ b/src/api/mods.rs @@ -193,7 +193,22 @@ pub struct Logo { } #[derive(Debug, Deserialize)] -pub struct Mod { +pub struct Category { + pub id: i32, + // game_id: i32, + pub name: String, + pub slug: String, + // url: String, + // pub icon_url: String, + // date_modified: DateTime, + // is_class: bool, + // class_id: i32, + // parent_category_id: i32, + // display_index: i32, +} + +#[derive(Debug, Deserialize)] +pub struct GameMod { pub name: String, //pub modId: i32, //pub gameSlug: String, @@ -212,6 +227,7 @@ pub struct Mod { //pub gamePopularityRank: i32, //pub isAvailable: bool, //pub thumbsUpCount: i32, + pub categories: Vec, } #[derive(Debug, Deserialize)] @@ -260,7 +276,7 @@ struct LatestEarlyAccessFileIndex { #[derive(Debug, Deserialize)] struct GetModsResponse { - data: Vec, + data: Vec, pagination: Pagination, } @@ -272,7 +288,7 @@ struct Pagination { totalCount: i32, } -pub fn get_mods(game_id: &i32) -> Vec { +pub fn get_mods(game_id: &i32) -> Vec { let client = get_curse_forge_client().unwrap(); let response = client diff --git a/src/components/card.rs b/src/components/card.rs index 1517baf..2fea829 100644 --- a/src/components/card.rs +++ b/src/components/card.rs @@ -1,8 +1,10 @@ use adw::subclass::prelude::*; +use gtk::prelude::BoxExt; use gtk::CompositeTemplate; use gtk::{gio, glib}; use crate::api::loaders::ImageLoader; +use crate::api::GameMod; use crate::dispatch::Worker; mod imp { @@ -20,6 +22,9 @@ mod imp { #[template_child] pub image: TemplateChild, + + #[template_child] + pub footer: TemplateChild, } #[glib::object_subclass] @@ -53,21 +58,47 @@ impl Card { glib::Object::builder().build() } - pub fn bind(&self, worker: Worker, heading: &str, body: &str, image_url: &str) { + fn set_heading(&self, heading: &str) { let widget = self.imp(); - widget.heading.set_text(heading); + } + + fn set_body(&self, body: &str) { + let widget = self.imp(); widget.body.set_text(body); + } - println!("image_url: {}", image_url); + fn set_image_from_url(&self, worker: Worker, url: String) { + let card = self.imp(); + let image = card.image.clone(); - let url = image_url.to_string(); - - let image = widget.image.clone(); worker.send_local_task(async move { let loader = ImageLoader::new(); let pixbuf = loader.from_url(url).await; image.set_from_pixbuf(pixbuf.as_ref()); }); } + + pub fn new_for_game_mod(worker: Worker, game_mod: &GameMod) -> Self { + let card = Self::new(); + let widget = card.imp(); + + card.set_image_from_url(worker, game_mod.logo.url.to_string()); + card.set_heading(&game_mod.name); + card.set_body(&game_mod.summary); + + let categories = gtk::Box::new(gtk::Orientation::Horizontal, 0); + categories.append(>k::Label::new(Some("Categories: "))); + + for category in &game_mod.categories { + let button = gtk::Button::builder() + .label(&category.name) + .css_classes(["pill", "flat"]) + .build(); + categories.append(&button); + } + widget.footer.append(&categories); + + card + } } diff --git a/src/windows/main/pages/games_and_mods.rs b/src/windows/main/pages/games_and_mods.rs index 804be47..3fec707 100644 --- a/src/windows/main/pages/games_and_mods.rs +++ b/src/windows/main/pages/games_and_mods.rs @@ -47,7 +47,7 @@ mod imp { pub games_list: TemplateChild, #[template_child] - pub mods_list: TemplateChild, + pub mods_list: TemplateChild, } #[glib::object_subclass] @@ -102,12 +102,10 @@ mod imp { } })); - let mods = get_mods(&get_game_id(Game::TheSims4("".to_string()))) + get_mods(&get_game_id(Game::TheSims4("".to_string()))) .iter() - .for_each(|mod_| { - let card = Card::new(); - card.bind(worker.clone(), &mod_.name, &mod_.summary, &mod_.logo.url); - + .for_each(|item| { + let card = Card::new_for_game_mod(worker.clone(), item); obj.imp().mods_list.append(&card); }); }