feat: settings refactor and initial mod listing
This commit is contained in:
parent
807f4b668c
commit
613c9032a9
18 changed files with 490 additions and 265 deletions
|
@ -13,9 +13,9 @@
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
<summary>Window maximized state</summary>
|
<summary>Window maximized state</summary>
|
||||||
</key>
|
</key>
|
||||||
<key name="games" type="as">
|
<key name="games" type="a(ss)">
|
||||||
<default>[]</default>
|
<default>[]</default>
|
||||||
<summary>Games</summary>
|
<summary>List of games. (Slug, Path)</summary>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
|
@ -5,7 +5,6 @@ blueprints = custom_target('blueprints',
|
||||||
'ui/windows/main/pages/welcome.blp',
|
'ui/windows/main/pages/welcome.blp',
|
||||||
'ui/windows/main/pages/games_and_mods.blp',
|
'ui/windows/main/pages/games_and_mods.blp',
|
||||||
'ui/windows/add_new_game/add_new_game.blp',
|
'ui/windows/add_new_game/add_new_game.blp',
|
||||||
'ui/components/mods_list.blp',
|
|
||||||
),
|
),
|
||||||
output: '.',
|
output: '.',
|
||||||
command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],
|
command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<file compressed="true">ui/windows/main/pages/welcome.ui</file>
|
<file compressed="true">ui/windows/main/pages/welcome.ui</file>
|
||||||
<file compressed="true">ui/windows/main/pages/games_and_mods.ui</file>
|
<file compressed="true">ui/windows/main/pages/games_and_mods.ui</file>
|
||||||
<file compressed="true">ui/windows/add_new_game/add_new_game.ui</file>
|
<file compressed="true">ui/windows/add_new_game/add_new_game.ui</file>
|
||||||
<file compressed="true">ui/components/mods_list.ui</file>
|
|
||||||
<file compressed="true">style.css</file>
|
<file compressed="true">style.css</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
|
@ -1,135 +0,0 @@
|
||||||
using Gtk 4.0;
|
|
||||||
using Adw 1;
|
|
||||||
|
|
||||||
template $ModsList : Adw.NavigationPage {
|
|
||||||
title: _("Main");
|
|
||||||
|
|
||||||
Adw.ToolbarView {
|
|
||||||
[top]
|
|
||||||
Adw.HeaderBar header_bar {
|
|
||||||
show-back-button: true;
|
|
||||||
}
|
|
||||||
|
|
||||||
content: Box {
|
|
||||||
orientation: vertical;
|
|
||||||
valign: center;
|
|
||||||
halign: center;
|
|
||||||
|
|
||||||
Image {
|
|
||||||
name: "logo";
|
|
||||||
icon-name: "re.sonny.Workbench";
|
|
||||||
pixel-size: 196;
|
|
||||||
margin-bottom: 30;
|
|
||||||
|
|
||||||
styles [
|
|
||||||
"icon-dropshadow"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: _("Welcome to Mod Manager");
|
|
||||||
margin-bottom: 30;
|
|
||||||
|
|
||||||
styles [
|
|
||||||
"title-1"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
Box subtitle {
|
|
||||||
orientation: vertical;
|
|
||||||
halign: center;
|
|
||||||
margin-bottom: 30;
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: "Learn and prototype with\nGNOME technologies";
|
|
||||||
justify: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box {
|
|
||||||
orientation: vertical;
|
|
||||||
homogeneous: true;
|
|
||||||
halign: center;
|
|
||||||
|
|
||||||
Box {
|
|
||||||
margin-bottom: 6;
|
|
||||||
|
|
||||||
Image {
|
|
||||||
icon-name: "update-symbolic";
|
|
||||||
margin-end: 12;
|
|
||||||
icon-size: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: _("Edit Style or UI to update the Preview");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box {
|
|
||||||
margin-bottom: 6;
|
|
||||||
|
|
||||||
Image {
|
|
||||||
icon-name: "media-playback-start-symbolic";
|
|
||||||
margin-end: 12;
|
|
||||||
icon-size: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: _("Hit");
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortcutsShortcut {
|
|
||||||
accelerator: "<Control>Return";
|
|
||||||
margin-start: 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: _("to format and run Code");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box {
|
|
||||||
margin-bottom: 6;
|
|
||||||
|
|
||||||
Image {
|
|
||||||
icon-name: "media-floppy-symbolic";
|
|
||||||
margin-end: 12;
|
|
||||||
icon-size: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: _("Changes are automatically saved and restored");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box {
|
|
||||||
margin-bottom: 6;
|
|
||||||
|
|
||||||
Image {
|
|
||||||
icon-name: "library-symbolic";
|
|
||||||
margin-end: 12;
|
|
||||||
icon-size: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: _("Browse the Library for demos and examples");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box {
|
|
||||||
margin-bottom: 6;
|
|
||||||
|
|
||||||
Image {
|
|
||||||
icon-name: "user-bookmarks-symbolic";
|
|
||||||
margin-end: 12;
|
|
||||||
icon-size: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
label: _("Checkout the Bookmarks menu to learn and get help");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,7 +12,11 @@ template $GamesAndMods: Adw.Bin {
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[end]
|
[end]
|
||||||
Button add_new_game {
|
Button add_new_game {
|
||||||
label: "Add game";
|
icon-name: "list-add-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
Button remove_all_games {
|
||||||
|
icon-name: "list-remove-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,13 +37,24 @@ template $GamesAndMods: Adw.Bin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Box {
|
content: Adw.Clamp {
|
||||||
Label {
|
orientation: vertical;
|
||||||
|
|
||||||
|
Label title {
|
||||||
label: "Mods";
|
label: "Mods";
|
||||||
|
styles ["title-1"]
|
||||||
}
|
}
|
||||||
Button remove_all_games {
|
|
||||||
label: "Remove all games";
|
Adw.Carousel {
|
||||||
}
|
}
|
||||||
|
Adw.CarouselIndicatorDots {}
|
||||||
|
|
||||||
|
ScrolledWindow {
|
||||||
|
vexpand: true;
|
||||||
|
ListBox mods_list {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,21 +7,17 @@ template $Welcome: Adw.Bin {
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Box {
|
content: Adw.Clamp {
|
||||||
orientation: vertical;
|
Adw.StatusPage {
|
||||||
halign: center;
|
title: "No games added yet.";
|
||||||
valign: center;
|
description: "Click the button below to add a new game.";
|
||||||
|
|
||||||
Label {
|
icon-name: "start-here-symbolic";
|
||||||
label: "No games added yet!";
|
|
||||||
justify: center;
|
Button add_new_game {
|
||||||
wrap: true;
|
label: "Add new game";
|
||||||
styles ["title-1"]
|
styles ["suggested-action", "pill"]
|
||||||
margin-bottom: 30;
|
}
|
||||||
}
|
|
||||||
Button add_new_game {
|
|
||||||
label: "Add new game";
|
|
||||||
styles ["suggested-action", "pill"]
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
|
use crate::settings::Game;
|
||||||
|
|
||||||
pub const API_URL: &str = "https://api.curseforge.com";
|
pub const API_URL: &str = "https://api.curseforge.com";
|
||||||
|
|
||||||
|
pub fn get_game_id(game: Game) -> i32 {
|
||||||
|
match game {
|
||||||
|
Game::TheSims4(_) => 78062,
|
||||||
|
_ => panic!("Game not supported"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_curse_forge_client() -> Result<reqwest::blocking::Client, reqwest::Error> {
|
pub fn get_curse_forge_client() -> Result<reqwest::blocking::Client, reqwest::Error> {
|
||||||
let mut api_key_header =
|
let mut api_key_header =
|
||||||
reqwest::header::HeaderValue::from_str(crate::config::API_KEY).unwrap();
|
reqwest::header::HeaderValue::from_str(crate::config::API_KEY).unwrap();
|
||||||
|
|
|
@ -3,11 +3,16 @@ use serde::Deserialize;
|
||||||
use crate::api::*;
|
use crate::api::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Response {
|
struct GetGamesResponse {
|
||||||
data: Vec<Game>,
|
data: Vec<Game>,
|
||||||
pagination: Pagination,
|
pagination: Pagination,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct GetGameResponse {
|
||||||
|
data: Game,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Game {
|
struct Game {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
@ -40,7 +45,18 @@ pub fn get_games() -> Vec<String> {
|
||||||
.get(&format!("{}/v1/games", base::API_URL))
|
.get(&format!("{}/v1/games", base::API_URL))
|
||||||
.send()
|
.send()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let json: Response = response.json().unwrap();
|
let json: GetGamesResponse = response.json().unwrap();
|
||||||
|
|
||||||
return json.data.iter().map(|game| game.name.clone()).collect();
|
return json.data.iter().map(|game| game.name.clone()).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_game(id: &i32) -> Game {
|
||||||
|
let client = base::get_curse_forge_client().unwrap();
|
||||||
|
let response = client
|
||||||
|
.get(&format!("{}/v1/games/{}", base::API_URL, id))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
let json: GetGameResponse = response.json().unwrap();
|
||||||
|
|
||||||
|
return json.data;
|
||||||
|
}
|
||||||
|
|
|
@ -3,3 +3,6 @@ pub use base::*;
|
||||||
|
|
||||||
pub mod games;
|
pub mod games;
|
||||||
pub use games::*;
|
pub use games::*;
|
||||||
|
|
||||||
|
pub mod mods;
|
||||||
|
pub use mods::*;
|
||||||
|
|
275
src/api/mods.rs
Normal file
275
src/api/mods.rs
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::{get_curse_forge_client, API_URL};
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"gameId": 0,
|
||||||
|
"name": "string",
|
||||||
|
"slug": "string",
|
||||||
|
"links": {
|
||||||
|
"websiteUrl": "string",
|
||||||
|
"wikiUrl": "string",
|
||||||
|
"issuesUrl": "string",
|
||||||
|
"sourceUrl": "string"
|
||||||
|
},
|
||||||
|
"summary": "string",
|
||||||
|
"status": 1,
|
||||||
|
"downloadCount": 0,
|
||||||
|
"isFeatured": true,
|
||||||
|
"primaryCategoryId": 0,
|
||||||
|
"categories": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"gameId": 0,
|
||||||
|
"name": "string",
|
||||||
|
"slug": "string",
|
||||||
|
"url": "string",
|
||||||
|
"iconUrl": "string",
|
||||||
|
"dateModified": "2019-08-24T14:15:22Z",
|
||||||
|
"isClass": true,
|
||||||
|
"classId": 0,
|
||||||
|
"parentCategoryId": 0,
|
||||||
|
"displayIndex": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"classId": 0,
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "string",
|
||||||
|
"url": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"logo": {
|
||||||
|
"id": 0,
|
||||||
|
"modId": 0,
|
||||||
|
"title": "string",
|
||||||
|
"description": "string",
|
||||||
|
"thumbnailUrl": "string",
|
||||||
|
"url": "string"
|
||||||
|
},
|
||||||
|
"screenshots": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"modId": 0,
|
||||||
|
"title": "string",
|
||||||
|
"description": "string",
|
||||||
|
"thumbnailUrl": "string",
|
||||||
|
"url": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mainFileId": 0,
|
||||||
|
"latestFiles": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"gameId": 0,
|
||||||
|
"modId": 0,
|
||||||
|
"isAvailable": true,
|
||||||
|
"displayName": "string",
|
||||||
|
"fileName": "string",
|
||||||
|
"releaseType": 1,
|
||||||
|
"fileStatus": 1,
|
||||||
|
"hashes": [
|
||||||
|
{
|
||||||
|
"value": "string",
|
||||||
|
"algo": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileDate": "2019-08-24T14:15:22Z",
|
||||||
|
"fileLength": 0,
|
||||||
|
"downloadCount": 0,
|
||||||
|
"fileSizeOnDisk": 0,
|
||||||
|
"downloadUrl": "string",
|
||||||
|
"gameVersions": [
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"sortableGameVersions": [
|
||||||
|
{
|
||||||
|
"gameVersionName": "string",
|
||||||
|
"gameVersionPadded": "string",
|
||||||
|
"gameVersion": "string",
|
||||||
|
"gameVersionReleaseDate": "2019-08-24T14:15:22Z",
|
||||||
|
"gameVersionTypeId": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"modId": 0,
|
||||||
|
"relationType": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exposeAsAlternative": true,
|
||||||
|
"parentProjectFileId": 0,
|
||||||
|
"alternateFileId": 0,
|
||||||
|
"isServerPack": true,
|
||||||
|
"serverPackFileId": 0,
|
||||||
|
"isEarlyAccessContent": true,
|
||||||
|
"earlyAccessEndDate": "2019-08-24T14:15:22Z",
|
||||||
|
"fileFingerprint": 0,
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"name": "string",
|
||||||
|
"fingerprint": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"latestFilesIndexes": [
|
||||||
|
{
|
||||||
|
"gameVersion": "string",
|
||||||
|
"fileId": 0,
|
||||||
|
"filename": "string",
|
||||||
|
"releaseType": 1,
|
||||||
|
"gameVersionTypeId": 0,
|
||||||
|
"modLoader": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"latestEarlyAccessFilesIndexes": [
|
||||||
|
{
|
||||||
|
"gameVersion": "string",
|
||||||
|
"fileId": 0,
|
||||||
|
"filename": "string",
|
||||||
|
"releaseType": 1,
|
||||||
|
"gameVersionTypeId": 0,
|
||||||
|
"modLoader": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dateCreated": "2019-08-24T14:15:22Z",
|
||||||
|
"dateModified": "2019-08-24T14:15:22Z",
|
||||||
|
"dateReleased": "2019-08-24T14:15:22Z",
|
||||||
|
"allowModDistribution": true,
|
||||||
|
"gamePopularityRank": 0,
|
||||||
|
"isAvailable": true,
|
||||||
|
"thumbsUpCount": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pagination": {
|
||||||
|
"index": 0,
|
||||||
|
"pageSize": 0,
|
||||||
|
"resultCount": 0,
|
||||||
|
"totalCount": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Hash {
|
||||||
|
value: String,
|
||||||
|
algo: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct SortableGameVersion {
|
||||||
|
gameVersionName: String,
|
||||||
|
gameVersionPadded: String,
|
||||||
|
gameVersion: String,
|
||||||
|
gameVersionReleaseDate: String,
|
||||||
|
gameVersionTypeId: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Dependency {
|
||||||
|
modId: i32,
|
||||||
|
relationType: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Module {
|
||||||
|
name: String,
|
||||||
|
fingerprint: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Mod {
|
||||||
|
pub name: String,
|
||||||
|
//pub modId: i32,
|
||||||
|
//pub gameSlug: String,
|
||||||
|
//pub gameId: i32,
|
||||||
|
pub summary: String,
|
||||||
|
//pub defaultFileId: i32,
|
||||||
|
//pub downloadCount: i32,
|
||||||
|
//pub latestFiles: Vec<LatestFile>,
|
||||||
|
//pub latestFilesIndexes: Vec<LatestFileIndex>,
|
||||||
|
//pub latestEarlyAccessFilesIndexes: Vec<LatestEarlyAccessFileIndex>,
|
||||||
|
//pub dateCreated: String,
|
||||||
|
//pub dateModified: String,
|
||||||
|
//pub dateReleased: String,
|
||||||
|
//pub allowModDistribution: bool,
|
||||||
|
//pub gamePopularityRank: i32,
|
||||||
|
//pub isAvailable: bool,
|
||||||
|
//pub thumbsUpCount: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct LatestFile {
|
||||||
|
fileId: i32,
|
||||||
|
displayName: String,
|
||||||
|
fileName: String,
|
||||||
|
fileDate: String,
|
||||||
|
fileLength: i32,
|
||||||
|
releaseType: i32,
|
||||||
|
fileStatus: i32,
|
||||||
|
downloadUrl: String,
|
||||||
|
gameVersions: Vec<String>,
|
||||||
|
sortableGameVersions: Vec<SortableGameVersion>,
|
||||||
|
dependencies: Vec<Dependency>,
|
||||||
|
exposeAsAlternative: bool,
|
||||||
|
parentProjectFileId: i32,
|
||||||
|
alternateFileId: i32,
|
||||||
|
isServerPack: bool,
|
||||||
|
serverPackFileId: i32,
|
||||||
|
isEarlyAccessContent: bool,
|
||||||
|
earlyAccessEndDate: String,
|
||||||
|
fileFingerprint: i32,
|
||||||
|
modules: Vec<Module>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct LatestFileIndex {
|
||||||
|
gameVersion: String,
|
||||||
|
fileId: i32,
|
||||||
|
filename: String,
|
||||||
|
releaseType: i32,
|
||||||
|
gameVersionTypeId: i32,
|
||||||
|
modLoader: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct LatestEarlyAccessFileIndex {
|
||||||
|
gameVersion: String,
|
||||||
|
fileId: i32,
|
||||||
|
filename: String,
|
||||||
|
releaseType: i32,
|
||||||
|
gameVersionTypeId: i32,
|
||||||
|
modLoader: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct GetModsResponse {
|
||||||
|
data: Vec<Mod>,
|
||||||
|
pagination: Pagination,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Pagination {
|
||||||
|
index: i32,
|
||||||
|
pageSize: i32,
|
||||||
|
resultCount: i32,
|
||||||
|
totalCount: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mods(game_id: &i32) -> Vec<Mod> {
|
||||||
|
let client = get_curse_forge_client().unwrap();
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get(&format!("{}/v1/mods/search", API_URL))
|
||||||
|
.query(&[("gameId", game_id)])
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
let json: GetModsResponse = response.json().unwrap();
|
||||||
|
|
||||||
|
return json.data;
|
||||||
|
}
|
|
@ -25,16 +25,10 @@ use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::config::VERSION;
|
use crate::config::VERSION;
|
||||||
use crate::config::{APP_ID, PKGDATADIR, PROFILE};
|
use crate::config::{APP_ID, PKGDATADIR, PROFILE};
|
||||||
use crate::settings::ModManagerSettings;
|
|
||||||
use crate::windows::main::ModManagerWindowMain;
|
use crate::windows::main::ModManagerWindowMain;
|
||||||
use crate::windows::main::Welcome;
|
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
|
|
||||||
use games_and_mods::GamesAndMods;
|
|
||||||
|
|
||||||
use crate::windows::games_and_mods;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
pub mod mods_list;
|
|
||||||
pub use mods_list::*;
|
|
|
@ -1,64 +0,0 @@
|
||||||
/* welcome.rs
|
|
||||||
*
|
|
||||||
* Copyright 2023 Tine
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
use adw::subclass::prelude::*;
|
|
||||||
use gtk::{gio, glib};
|
|
||||||
use gtk::{glib::clone, prelude::*};
|
|
||||||
|
|
||||||
mod imp {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
|
||||||
#[template(resource = "/dev/mnts/ModManager/ui/components/mods_list.ui")]
|
|
||||||
pub struct ModsList {}
|
|
||||||
|
|
||||||
#[glib::object_subclass]
|
|
||||||
impl ObjectSubclass for ModsList {
|
|
||||||
const NAME: &'static str = "ModsList";
|
|
||||||
type Type = super::ModsList;
|
|
||||||
type ParentType = adw::NavigationPage;
|
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.bind_template();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
|
||||||
obj.init_template();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for ModsList {
|
|
||||||
fn constructed(&self) {}
|
|
||||||
}
|
|
||||||
impl WidgetImpl for ModsList {}
|
|
||||||
impl NavigationPageImpl for ModsList {}
|
|
||||||
}
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct ModsList(ObjectSubclass<imp::ModsList>)
|
|
||||||
@extends gtk::Widget, gtk::Buildable, adw::NavigationPage,
|
|
||||||
@implements gio::ActionGroup, gio::ActionMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModsList {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
glib::Object::builder().build()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,7 +17,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod application;
|
mod application;
|
||||||
mod components;
|
|
||||||
mod config;
|
mod config;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod windows;
|
mod windows;
|
||||||
|
|
106
src/settings.rs
106
src/settings.rs
|
@ -1,15 +1,115 @@
|
||||||
use gio::glib;
|
use gio::glib;
|
||||||
use gsettings_macro::gen_settings;
|
use gsettings_macro::gen_settings;
|
||||||
use gtk::gio;
|
use gtk::gio;
|
||||||
use std::collections::HashMap;
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::config::APP_ID;
|
use crate::config::APP_ID;
|
||||||
|
|
||||||
#[gen_settings(file = "./data/dev.mnts.ModManager.gschema.xml.in")]
|
#[gen_settings(file = "./data/dev.mnts.ModManager.gschema.xml.in")]
|
||||||
pub struct ModManagerSettings;
|
#[gen_settings_define(
|
||||||
|
key_name = "games",
|
||||||
|
arg_type = "Vec<(String, String)>",
|
||||||
|
ret_type = "Vec<(String, String)>"
|
||||||
|
)]
|
||||||
|
pub struct Settings;
|
||||||
|
|
||||||
impl Default for ModManagerSettings {
|
impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(APP_ID)
|
Self::new(APP_ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Game {
|
||||||
|
TheSims4(String),
|
||||||
|
}
|
||||||
|
impl Game {
|
||||||
|
pub fn from_slug(slug: String, path: String) -> Self {
|
||||||
|
match slug.as_str() {
|
||||||
|
"sims4" => Game::TheSims4(path),
|
||||||
|
_ => panic!("Game not supported"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_name(name: String, path: String) -> Self {
|
||||||
|
match name.as_str() {
|
||||||
|
"The Sims 4" => Game::TheSims4(path),
|
||||||
|
_ => panic!("Game not supported"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_path(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Game::TheSims4(path) => path.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_slug(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Game::TheSims4(_) => "sims4".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_name(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Game::TheSims4(_) => "The Sims 4".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ModManagerSettings {
|
||||||
|
settings: Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModManagerSettings {
|
||||||
|
pub fn default() -> Self {
|
||||||
|
let settings = Settings::default();
|
||||||
|
Self { settings }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_supported_games(&self) -> Vec<Game> {
|
||||||
|
info!("Getting supported games");
|
||||||
|
|
||||||
|
return [Game::TheSims4("".to_string())].to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_managed_games(&self) -> Vec<Game> {
|
||||||
|
info!("Getting managed games");
|
||||||
|
|
||||||
|
let mut games_vec: Vec<Game> = Vec::new();
|
||||||
|
|
||||||
|
for (slug, path) in self.settings.games() {
|
||||||
|
info!("Found game {:?} at path {:?}", slug, path);
|
||||||
|
games_vec.push(Game::from_slug(slug, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return games_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_managed_game(&self, game: Game) {
|
||||||
|
info!("Adding game {} {}", game.to_slug(), game.to_name());
|
||||||
|
|
||||||
|
let mut games_vec: Vec<(String, String)> = Vec::new();
|
||||||
|
|
||||||
|
for (slug, path) in self.settings.games() {
|
||||||
|
games_vec.push((slug, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
games_vec.push((game.to_slug(), game.to_path()));
|
||||||
|
|
||||||
|
self.settings.set_games(games_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_managed_games(&self) {
|
||||||
|
info!("Removing all managed games");
|
||||||
|
|
||||||
|
self.settings.set_games([].to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_managed_games_changed(&self, callback: impl Fn() + 'static) {
|
||||||
|
self.settings.connect_games_changed(move |_| {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ use gtk::prelude::*;
|
||||||
use gtk::{gio, glib};
|
use gtk::{gio, glib};
|
||||||
|
|
||||||
use crate::api::*;
|
use crate::api::*;
|
||||||
use crate::settings::ModManagerSettings;
|
use crate::settings::{Game, ModManagerSettings};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -83,9 +83,12 @@ impl ModManagerWindowAddNewGame {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(&self) {
|
pub fn setup(&self) {
|
||||||
let games = games::get_games();
|
let settings = ModManagerSettings::default();
|
||||||
let games_strs: Vec<&str> = games.iter().map(|s| s.as_str()).collect();
|
|
||||||
let games_list = >k::StringList::new(&games_strs);
|
let supported_games = settings.get_supported_games();
|
||||||
|
let games_strs: Vec<String> = supported_games.iter().map(|s| s.to_name()).collect();
|
||||||
|
let games_list =
|
||||||
|
>k::StringList::new(&games_strs.iter().map(|s| s.as_str()).collect::<Vec<&str>>());
|
||||||
|
|
||||||
let obj = self.imp();
|
let obj = self.imp();
|
||||||
|
|
||||||
|
@ -94,18 +97,13 @@ impl ModManagerWindowAddNewGame {
|
||||||
let instance = self;
|
let instance = self;
|
||||||
|
|
||||||
obj.complete_button
|
obj.complete_button
|
||||||
.connect_clicked(clone!(@strong instance, @strong games_list => move |_| {
|
.connect_clicked(clone!(@strong instance, @strong games_list, @strong settings => move |_| {
|
||||||
//let selected_game = games_dropdown.selected_item();
|
//let selected_game = games_dropdown.selected_item();
|
||||||
|
|
||||||
let settings = ModManagerSettings::default();
|
|
||||||
let game_selected = games_list.string(instance.imp().games_dropdown.selected()).unwrap().to_string();
|
let game_selected = games_list.string(instance.imp().games_dropdown.selected()).unwrap().to_string();
|
||||||
println!("complete button clicked, selected game: {:?}", &game_selected);
|
println!("complete button clicked, selected game: {:?}", &game_selected);
|
||||||
|
|
||||||
let mut selected = settings.games().to_vec();
|
settings.add_managed_game(Game::from_name(game_selected, "".to_string()));
|
||||||
selected.push(game_selected);
|
|
||||||
|
|
||||||
settings.set_strv("games", selected);
|
|
||||||
|
|
||||||
|
|
||||||
//if let Some(selected_game) = selected_game {
|
//if let Some(selected_game) = selected_game {
|
||||||
instance.hide()
|
instance.hide()
|
||||||
|
|
|
@ -21,15 +21,14 @@ use gtk::{gio, glib};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::config::PROFILE;
|
use crate::config::PROFILE;
|
||||||
use crate::settings::ModManagerSettings;
|
|
||||||
use crate::windows::GamesAndMods;
|
use crate::windows::GamesAndMods;
|
||||||
|
|
||||||
use super::Welcome;
|
use super::Welcome;
|
||||||
|
use crate::settings::ModManagerSettings;
|
||||||
|
use gtk::glib::clone;
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
|
|
||||||
use gtk::glib::clone;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||||
|
@ -62,10 +61,10 @@ mod imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = ModManagerSettings::default();
|
let settings = ModManagerSettings::default();
|
||||||
settings.connect_games_changed(clone!(@strong obj, @strong settings => move |_| {
|
settings.on_managed_games_changed(clone!(@strong obj, @strong settings => move || {
|
||||||
info!("Games changed, deciding on initial page.");
|
info!("Games changed, deciding on initial page.");
|
||||||
|
|
||||||
let page: gtk::Widget = if settings.games().len() > 0 {
|
let page: gtk::Widget = if settings.get_managed_games().len() > 0 {
|
||||||
GamesAndMods::new().upcast()
|
GamesAndMods::new().upcast()
|
||||||
} else {
|
} else {
|
||||||
Welcome::new().upcast()
|
Welcome::new().upcast()
|
||||||
|
@ -74,7 +73,7 @@ mod imp {
|
||||||
obj.set_property("content", page);
|
obj.set_property("content", page);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let page: gtk::Widget = if settings.games().len() > 0 {
|
let page: gtk::Widget = if settings.get_managed_games().len() > 0 {
|
||||||
GamesAndMods::new().upcast()
|
GamesAndMods::new().upcast()
|
||||||
} else {
|
} else {
|
||||||
Welcome::new().upcast()
|
Welcome::new().upcast()
|
||||||
|
|
|
@ -25,7 +25,11 @@ mod imp {
|
||||||
use gtk::glib::clone;
|
use gtk::glib::clone;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::{api::games, settings::ModManagerSettings, windows::ModManagerWindowAddNewGame};
|
use crate::{
|
||||||
|
api::{games, get_game_id, get_mods},
|
||||||
|
settings::{Game, ModManagerSettings},
|
||||||
|
windows::ModManagerWindowAddNewGame,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -40,6 +44,9 @@ mod imp {
|
||||||
|
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub games_list: TemplateChild<gtk::ListBox>,
|
pub games_list: TemplateChild<gtk::ListBox>,
|
||||||
|
|
||||||
|
#[template_child]
|
||||||
|
pub mods_list: TemplateChild<gtk::ListBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -64,7 +71,7 @@ mod imp {
|
||||||
|
|
||||||
self.remove_all_games.connect_clicked(|_| {
|
self.remove_all_games.connect_clicked(|_| {
|
||||||
let settings = ModManagerSettings::default();
|
let settings = ModManagerSettings::default();
|
||||||
settings.set_games(&[]);
|
settings.remove_managed_games();
|
||||||
println!("Remove all games button clicked");
|
println!("Remove all games button clicked");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,23 +81,40 @@ mod imp {
|
||||||
|
|
||||||
let settings = ModManagerSettings::default();
|
let settings = ModManagerSettings::default();
|
||||||
|
|
||||||
for game in settings.games() {
|
for game in settings.get_managed_games() {
|
||||||
info!("Adding game {} to list", game);
|
info!("Adding game {:?} to list", game);
|
||||||
let row = adw::ActionRow::new();
|
let row = adw::ActionRow::new();
|
||||||
row.set_title(&game);
|
row.set_title(&game.to_name());
|
||||||
obj.imp().games_list.append(&row);
|
obj.imp().games_list.append(&row);
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.connect_games_changed(clone!(@weak obj, @strong settings => move |_| {
|
settings.on_managed_games_changed(clone!(@weak obj, @strong settings => move || {
|
||||||
info!("Games changed, modifying list");
|
info!("Games changed, modifying list");
|
||||||
obj.imp().games_list.remove_all();
|
for game in settings.get_managed_games() {
|
||||||
for game in settings.games() {
|
info!("Adding game {:?} to list", game);
|
||||||
info!("Adding game {} to list", game);
|
|
||||||
let row = adw::ActionRow::new();
|
let row = adw::ActionRow::new();
|
||||||
row.set_title(&game);
|
row.set_title(&game.to_name());
|
||||||
obj.imp().games_list.append(&row);
|
obj.imp().games_list.append(&row);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
obj.imp().mods_list.append(&card);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl WidgetImpl for GamesAndMods {}
|
impl WidgetImpl for GamesAndMods {}
|
||||||
|
|
Loading…
Reference in a new issue