Skip to content

Commit e5a68b0

Browse files
Integrate CC version list with UI
1 parent c22c976 commit e5a68b0

File tree

4 files changed

+132
-45
lines changed

4 files changed

+132
-45
lines changed

src/cold_clear.rs

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use std::ffi::{OsStr, OsString};
1+
use std::ffi::OsString;
22
use std::io::Write;
3-
use std::fs::{self, File};
4-
use std::path::{PathBuf};
3+
use std::fs;
4+
use std::path::PathBuf;
55
use std::collections::HashMap;
66
use std::thread;
77
use std::time::{Instant, Duration};
88
use std::sync::{mpsc, Arc, Mutex};
9-
use reqwest::Url;
109
use slint::SharedString;
1110
use slint::ComponentHandle;
1211
use tokio::runtime::Runtime;
@@ -498,6 +497,9 @@ pub fn unpack_cold_clear(version: &str) -> Result<(), Box<dyn std::error::Error>
498497

499498
if let Err(_) = zip_archive {
500499
eprintln!("ColdClear zip archive at '{zip_path:#?}' seems to be invalid. Redownloading.");
500+
501+
fs::remove_file(zip_path)?;
502+
501503
download_cold_clear(version)?;
502504

503505
zip_archive = ZipArchive::new(&zip_file);
@@ -522,44 +524,50 @@ pub fn unpack_cold_clear(version: &str) -> Result<(), Box<dyn std::error::Error>
522524
return Ok(());
523525
}
524526

525-
pub fn get_available_offline_versions() -> Result<Vec<String>, std::io::Error> {
526-
let path = paths::get_cold_clear_download_path("")
527-
.parent()?;
527+
pub fn get_available_offline_versions() -> Vec<String> {
528+
let path = paths::get_cold_clear_download_path("");
529+
let path = path
530+
.parent();
531+
if path.is_none() {
532+
return vec![];
533+
}
534+
let path = path.unwrap();
528535

529-
let entries = fs::read_dir(path)?;
536+
let entries = fs::read_dir(path);
537+
if entries.is_err() {
538+
return vec![];
539+
}
540+
let entries = entries.unwrap();
530541

531542
let mut versions: Vec<String> = Vec::new();
532543

533544
for entry in entries {
534-
let entry = entry?;
535-
545+
if entry.is_err() { continue; }
546+
let entry = entry.unwrap();
536547
let path = entry.path();
537-
if !path.is_file() {
538-
continue;
539-
}
548+
if !path.is_file() { continue; }
540549

541550
let name = path.file_name();
542-
if name.is_none() {
543-
continue;
544-
}
551+
if name.is_none() { continue; }
545552
let name = name.unwrap()
546-
.to_str()
547-
.expect("Failed to get UTF-8 string from OsStr");
548-
549-
if !name.ends_with(".zip") {
550-
continue;
551-
}
553+
.to_str();
554+
if name.is_none() { continue; }
555+
let name = name.unwrap();
556+
if !name.ends_with(".zip") { continue; }
552557

553558
let version = name
554559
.replace(".zip", "");
555560

556561
versions.push(version);
557562
}
558563

559-
return Ok(versions);
564+
return versions;
560565
}
561566

562-
pub fn get_available_versions() -> Result<Vec<String>, Box<dyn std::error::Error>> {
567+
/// BLOCKING FUNCTION
568+
/// This calls the GitHub API.
569+
/// It shouldn't take *too* long, but you should wrap this in a separate thread if you can.
570+
pub fn get_available_online_versions() -> Result<Vec<String>, Box<dyn std::error::Error>> {
563571
let api_url = paths::COLD_CLEAR_RELEASES_API_URL;
564572

565573
let response = reqwest::blocking::get(api_url)?;
@@ -585,4 +593,27 @@ pub fn get_available_versions() -> Result<Vec<String>, Box<dyn std::error::Error
585593
}
586594

587595
return Ok(versions);
596+
}
597+
598+
/// BLOCKING FUNCTION
599+
/// This calls the GitHub API.
600+
/// It shouldn't take *too* long, but you should wrap this in a separate thread if you can.
601+
pub fn get_available_versions() -> Vec<String> {
602+
let mut versions = get_available_offline_versions();
603+
let online_versions = get_available_online_versions();
604+
605+
if let Err(e) = online_versions {
606+
eprintln!("Failed to get ColdClear online version list: {:#?}", e);
607+
return versions;
608+
}
609+
610+
let online_versions = online_versions.unwrap();
611+
612+
for online_version in online_versions {
613+
if !versions.contains(&online_version) {
614+
versions.push(online_version);
615+
}
616+
}
617+
618+
return versions;
588619
}

src/conf.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct Config {
1717
pub game_repo_path: String,
1818
pub use_gui: bool,
1919
pub use_cold_clear: bool,
20+
pub cold_clear_version: String,
2021
}
2122

2223
/// [See definition for flags](crate::CliInstruction::Run::flags)
@@ -55,6 +56,7 @@ impl Config {
5556
game_repo_path: "".to_string(),
5657
use_gui: true,
5758
use_cold_clear: true,
59+
cold_clear_version: "11.4.1".to_string(),
5860
}
5961
}
6062
pub fn load_from_file() -> Self {
@@ -151,6 +153,7 @@ impl From<Settings> for Config {
151153
game_repo_path: settings.game_repo_path.as_str().to_string(),
152154
use_gui: true,
153155
use_cold_clear: settings.use_cold_clear,
156+
cold_clear_version: settings.cold_clear_version.as_str().to_string(),
154157
}
155158
}
156159
}
@@ -164,6 +167,7 @@ impl From<Config> for Settings {
164167
game_repo_path: cfg.game_repo_path.clone().into(),
165168
repo_initialized: cfg.repo_initialized,
166169
use_cold_clear: cfg.use_cold_clear,
170+
cold_clear_version: cfg.cold_clear_version.clone().into(),
167171
}
168172
}
169173
}

src/main_window.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use open as file_open;
22
use copypasta::ClipboardProvider;
3+
use crate::cold_clear;
34
use crate::dirs;
45
use crate::conf::Config;
56
use crate::game;
@@ -101,6 +102,16 @@ pub fn open(cfg: &Config) -> Result<MainWindow, slint::PlatformError> {
101102
main_window.set_versions(
102103
get_versions(&cfg.game_repo_path, false)
103104
);
105+
main_window.set_cc_versions(
106+
ModelRc::new(
107+
VecModel::from(
108+
cold_clear::get_available_offline_versions()
109+
.iter()
110+
.map(|s| SharedString::from(s))
111+
.collect::<Vec<SharedString>>()
112+
)
113+
)
114+
);
104115
main_window.on_update_version_list(|include_commits| {
105116
get_versions(Config::load().game_repo_path.as_str(), include_commits)
106117
});
@@ -114,17 +125,28 @@ pub fn open(cfg: &Config) -> Result<MainWindow, slint::PlatformError> {
114125
return ModelRc::new(filtered);
115126
});
116127
main_window.on_apply_settings(|settings| {
117-
let config = Config {
118-
sandboxed: settings.sandboxed,
119-
clear_temp_dir: settings.clear_temp_dir,
120-
import_save_on_play: settings.import_save_on_play,
121-
repo_initialized: settings.repo_initialized,
122-
game_repo_path: settings.game_repo_path.as_str().to_string(),
123-
use_gui: true,
124-
use_cold_clear: settings.use_cold_clear,
125-
};
128+
let config: Config = settings.into();
126129
config.save();
127130
});
131+
132+
// Fetch for new CC version asynchronously
133+
let main_window_weak = main_window.as_weak();
134+
std::thread::spawn(move || {
135+
main_window_weak.upgrade_in_event_loop(|window| {
136+
window.set_cc_versions(
137+
ModelRc::new(
138+
VecModel::from(
139+
cold_clear::get_available_versions()
140+
.iter()
141+
.map(|s| SharedString::from(s))
142+
.collect::<Vec<SharedString>>()
143+
)
144+
)
145+
)
146+
})
147+
.expect("Failed to upgrade weak ref in event loop while fetching CC versions");
148+
});
149+
128150
main_window.run()?;
129151

130152
return Ok(main_window);

ui/main.slint

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TabWidget, Button, Switch, ScrollView, Palette, StandardButton, ProgressIndicator, LineEdit, ListView } from "std-widgets.slint";
1+
import { TabWidget, Button, Switch, ScrollView, Palette, StandardButton, ProgressIndicator, LineEdit, ListView, ComboBox } from "std-widgets.slint";
22
import "fonts/JetBrainsMono-Bold.ttf";
33

44
struct Settings {
@@ -8,6 +8,7 @@ struct Settings {
88
repo_initialized: bool,
99
game_repo_path: string,
1010
use_cold_clear: bool,
11+
cold_clear_version: string,
1112
}
1213

1314
component BoxedminoBanner {
@@ -52,9 +53,13 @@ export component MainWindow inherits Window {
5253
sandboxed: true,
5354
clear_temp_dir: true,
5455
import_save_on_play: false,
56+
repo_initialized: true,
5557
game_repo_path: "",
58+
use_cold_clear: true,
59+
cold_clear_version: "11.4.1",
5660
};
5761
in property <bool> is_wayland_used: false;
62+
in property <[string]> cc_versions: ["11.4.1", "11.4.2"];
5863
private property <[string]> matched_versions: versions;
5964
private property <string> selected_version: "";
6065
private property <string> searched_string: "";
@@ -286,25 +291,50 @@ export component MainWindow inherits Window {
286291
}
287292
}
288293
}
289-
HorizontalLayout {
290-
VerticalLayout {
291-
Text {
292-
text: "Cold Clear AI";
293-
font-size: 1.25rem;
294+
VerticalLayout {
295+
spacing: 4px;
296+
HorizontalLayout {
297+
alignment: space-between;
298+
VerticalLayout {
299+
Text {
300+
text: "Cold Clear AI [WIP]";
301+
font-size: 1.25rem;
302+
}
303+
Text {
304+
text: "Import Techmino's AI library to the sandbox.";
305+
font-size: 0.96rem;
306+
}
294307
}
295-
Text {
296-
text: "Import Techmino's AI library to the sandbox.";
297-
font-size: 0.96rem;
308+
Switch {
309+
checked: settings.use_cold_clear;
310+
toggled => {
311+
settings.use_cold_clear = self.checked;
312+
cc_ver_select.enabled = self.checked;
313+
}
298314
}
299315
}
300-
Switch {
301-
checked: settings.use_cold_clear;
302-
toggled => { settings.use_cold_clear = self.checked; }
316+
HorizontalLayout {
317+
alignment: stretch;
318+
spacing: 8px;
319+
Text {
320+
text: "Cold Clear version: ";
321+
vertical-alignment: center;
322+
font-size: 1.026rem;
323+
}
324+
cc_ver_select := ComboBox {
325+
enabled: settings.use_cold_clear;
326+
model: cc_versions;
327+
current-value: settings.cold_clear_version;
328+
selected(version) => {
329+
settings.cold_clear_version = version;
330+
}
331+
}
303332
}
304333
}
305334
VerticalLayout {
306335
padding-top: 8px;
307336
Button {
337+
// TODO: Highlight Apply button when settings are changed
308338
text: "Apply";
309339
clicked => { apply_settings(settings); }
310340
}

0 commit comments

Comments
 (0)