Skip to content

Commit 487a4c5

Browse files
committed
wip
1 parent a031983 commit 487a4c5

23 files changed

Lines changed: 203 additions & 120 deletions

src/game.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// SPDX-FileCopyrightText: 2026 Manuel Quarneti <[email protected]>
22
// SPDX-License-Identifier: GPL-3.0-only
33

4-
use crate::{Config, DiscInfo, Game, SortBy, data_dir::DATA_DIR, id_map, util::GIB};
4+
use crate::{
5+
AppWindow, Config, DiscInfo, Game, SortBy, data_dir::DATA_DIR, id_map, mirrored::Mirrored,
6+
util::GIB,
7+
};
58
use anyhow::Result;
69
use slint::{Image, SharedString, ToSharedString};
710
use std::{cell::RefCell, cmp::Ordering, fs, path::Path, rc::Rc};
@@ -59,7 +62,9 @@ impl Game {
5962
}
6063
}
6164

62-
pub fn get_compare_fn(config: Rc<RefCell<Config>>) -> Box<dyn Fn(&Game, &Game) -> Ordering> {
65+
pub fn get_compare_fn(
66+
config: Rc<Mirrored<Config, AppWindow>>,
67+
) -> Box<dyn Fn(&Game, &Game) -> Ordering> {
6368
Box::new(move |a, b| {
6469
let config = config.borrow();
6570

@@ -74,7 +79,7 @@ pub fn get_compare_fn(config: Rc<RefCell<Config>>) -> Box<dyn Fn(&Game, &Game) -
7479

7580
pub fn get_filter_fn(
7681
query_lowercase: Rc<RefCell<SharedString>>,
77-
config: Rc<RefCell<Config>>,
82+
config: Rc<Mirrored<Config, AppWindow>>,
7883
) -> Box<dyn Fn(&Game) -> bool> {
7984
Box::new(move |game| {
8085
let config = config.borrow();

src/homebrew_app.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// SPDX-FileCopyrightText: 2026 Manuel Quarneti <[email protected]>
22
// SPDX-License-Identifier: GPL-3.0-only
33

4-
use crate::{Config, HomebrewApp, HomebrewAppMeta, SortBy, util::MIB};
4+
use crate::{
5+
AppWindow, Config, HomebrewApp, HomebrewAppMeta, SortBy, mirrored::Mirrored, util::MIB,
6+
};
57
use anyhow::Result;
68
use slint::{Image, SharedString, ToSharedString};
79
use std::{cell::RefCell, cmp::Ordering, fs, path::Path, rc::Rc};
@@ -51,7 +53,7 @@ impl HomebrewApp {
5153
}
5254

5355
pub fn get_compare_fn(
54-
config: Rc<RefCell<Config>>,
56+
config: Rc<Mirrored<Config, AppWindow>>,
5557
) -> Box<dyn Fn(&HomebrewApp, &HomebrewApp) -> Ordering> {
5658
Box::new(move |a, b| {
5759
let config = config.borrow();

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod game;
1717
mod homebrew_app;
1818
mod homebrew_app_meta;
1919
mod id_map;
20+
mod mirrored;
2021
mod model;
2122
mod notification;
2223
mod osc;

src/mirrored.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-FileCopyrightText: 2026 Manuel Quarneti <[email protected]>
2+
// SPDX-License-Identifier: GPL-3.0-only
3+
4+
use slint::{ComponentHandle, StrongHandle, Weak};
5+
use std::cell::{Ref, RefCell};
6+
7+
pub struct Mirrored<T, A: StrongHandle> {
8+
inner: RefCell<T>,
9+
weak: Weak<A>,
10+
update_fn: fn(&A, T),
11+
}
12+
13+
impl<T, A> Mirrored<T, A>
14+
where
15+
T: Clone,
16+
A: StrongHandle + ComponentHandle,
17+
{
18+
pub fn new(inner: T, app: &A, update_fn: fn(&A, T)) -> Self {
19+
let m = Mirrored {
20+
inner: RefCell::new(inner),
21+
weak: app.as_weak(),
22+
update_fn,
23+
};
24+
25+
m.sync();
26+
m
27+
}
28+
29+
pub fn borrow(&self) -> Ref<'_, T> {
30+
self.inner.borrow()
31+
}
32+
33+
fn sync(&self) {
34+
let app = self.weak.upgrade().unwrap();
35+
(self.update_fn)(&app, self.inner.borrow().clone());
36+
}
37+
38+
pub fn edit<E>(&self, f: E)
39+
where
40+
E: Fn(&mut T) -> (),
41+
{
42+
f(&mut self.inner.borrow_mut());
43+
self.sync();
44+
}
45+
}

src/model.rs

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,26 @@
22
// SPDX-License-Identifier: GPL-3.0-only
33

44
use crate::{
5-
AppWindow, Config, Game, HomebrewApp, Notification, OscApp, QueuedConversion, game,
6-
homebrew_app, osc,
5+
AppWindow, Config, Game, HomebrewApp, Notification, OscApp, QueuedConversion, SortBy, game,
6+
homebrew_app, mirrored::Mirrored, osc,
7+
};
8+
use slint::{FilterModel, ModelRc, SharedString, SortModel, ToSharedString, VecModel};
9+
use std::{
10+
cell::{Ref, RefCell},
11+
cmp::Ordering,
12+
path::PathBuf,
13+
rc::Rc,
714
};
8-
use slint::{FilterModel, ModelRc, SharedString, SortModel, VecModel};
9-
use std::{cell::RefCell, cmp::Ordering, rc::Rc};
1015

1116
type SortedModel<T> = SortModel<Rc<VecModel<T>>, Box<dyn Fn(&T, &T) -> Ordering>>;
1217
type FilteredModel<T> = FilterModel<Rc<SortedModel<T>>, Box<dyn Fn(&T) -> bool>>;
1318
type JustFilteredModel<T> = FilterModel<Rc<VecModel<T>>, Box<dyn Fn(&T) -> bool>>;
1419

1520
#[derive(Clone)]
1621
pub struct AppModel {
17-
config: Rc<RefCell<Config>>,
22+
config: Rc<Mirrored<Config, AppWindow>>,
23+
status: Rc<Mirrored<SharedString, AppWindow>>,
24+
crc32_status: Rc<Mirrored<SharedString, AppWindow>>,
1825

1926
games: Rc<VecModel<Game>>,
2027
homebrew_apps: Rc<VecModel<HomebrewApp>>,
@@ -39,7 +46,17 @@ pub struct AppModel {
3946

4047
impl AppModel {
4148
pub fn new(config: Config, app: &AppWindow) -> Self {
42-
let config = Rc::new(RefCell::new(config));
49+
let config = Rc::new(Mirrored::new(config, app, AppWindow::set_config));
50+
let status = Rc::new(Mirrored::new(
51+
SharedString::new(),
52+
app,
53+
AppWindow::set_status,
54+
));
55+
let crc32_status = Rc::new(Mirrored::new(
56+
SharedString::new(),
57+
app,
58+
AppWindow::set_crc32_status,
59+
));
4360

4461
let games = Rc::new(VecModel::from(Vec::new()));
4562
let homebrew_apps = Rc::new(VecModel::from(Vec::new()));
@@ -83,6 +100,8 @@ impl AppModel {
83100

84101
Self {
85102
config,
103+
status,
104+
crc32_status,
86105
games,
87106
homebrew_apps,
88107
osc_apps,
@@ -100,26 +119,53 @@ impl AppModel {
100119
}
101120
}
102121

103-
pub fn config(&self) -> Config {
104-
self.config.borrow().clone()
122+
pub fn borrow_config(&self) -> Ref<'_, Config> {
123+
self.config.borrow()
105124
}
106125

107-
pub fn set_config(&self, config: Config) {
108-
let old_config = self.config.replace(config);
109-
let config = self.config.borrow();
126+
pub fn set_mount_point(&self, mount_point: PathBuf) {
127+
self.config.edit(|config| {
128+
config.contents.mount_point = mount_point.to_string_lossy().to_shared_string();
129+
});
110130

111-
if old_config.contents.sort_by != config.contents.sort_by {
112-
self.sorted_games.reset();
113-
self.sorted_homebrew_apps.reset();
131+
if let Err(e) = self.config.borrow().write() {
132+
self.add_notification(e.into());
114133
}
134+
}
115135

116-
if old_config.contents.show_wii != config.contents.show_wii
117-
|| old_config.contents.show_gc != config.contents.show_gc
118-
{
119-
self.filtered_games.reset();
136+
pub fn set_sort_by(&self, sort_by: SortBy) {
137+
self.config.edit(|config| {
138+
config.contents.sort_by = sort_by;
139+
});
140+
141+
self.sorted_games.reset();
142+
self.sorted_homebrew_apps.reset();
143+
144+
if let Err(e) = self.config.borrow().write() {
145+
self.add_notification(e.into());
120146
}
147+
}
148+
149+
pub fn set_show_wii(&self, show_wii: bool) {
150+
self.config.edit(|config| {
151+
config.contents.show_wii = show_wii;
152+
});
153+
154+
self.filtered_games.reset();
155+
156+
if let Err(e) = self.config.borrow().write() {
157+
self.add_notification(e.into());
158+
}
159+
}
160+
161+
pub fn set_show_gc(&self, show_gc: bool) {
162+
self.config.edit(|config| {
163+
config.contents.show_gc = show_gc;
164+
});
165+
166+
self.filtered_games.reset();
121167

122-
if let Err(e) = config.write() {
168+
if let Err(e) = self.config.borrow().write() {
123169
self.add_notification(e.into());
124170
}
125171
}

src/rust_callbacks.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,10 @@
22
// SPDX-License-Identifier: GPL-3.0-only
33

44
use crate::{Rust, dialogs, game, homebrew_app, model::AppModel, osc};
5-
use slint::ToSharedString;
65
use std::path::Path;
76

87
impl Rust<'_> {
98
pub fn register_callbacks(&self, state: &AppModel, window: &slint::Window) {
10-
let state_clone = state.clone();
11-
self.on_set_config(move |config| {
12-
state_clone.set_config(config);
13-
});
14-
159
let state_clone = state.clone();
1610
self.on_open_that(move |uri| {
1711
if let Err(e) = open::that(uri) {
@@ -22,22 +16,21 @@ impl Rust<'_> {
2216
let state_clone = state.clone();
2317
let window_handle = window.window_handle();
2418
self.on_pick_mount_point(move || {
25-
let mut config = state_clone.config();
26-
2719
if let Some(path) = dialogs::pick_mount_point(&window_handle) {
28-
config.contents.mount_point = path.to_string_lossy().to_shared_string();
29-
state_clone.set_config(config.clone());
20+
state_clone.set_mount_point(path);
3021
}
31-
32-
config
3322
});
3423

3524
let state_clone = state.clone();
3625
self.on_refresh_all(move || {
37-
let config = state_clone.config();
38-
let root_path = Path::new(&config.contents.mount_point);
39-
let new_games = game::scan_drive(root_path);
40-
let new_apps = homebrew_app::scan_drive(root_path);
26+
let (new_games, new_apps) = {
27+
let config = state_clone.borrow_config();
28+
let root_path = Path::new(&config.contents.mount_point);
29+
let new_games = game::scan_drive(root_path);
30+
let new_apps = homebrew_app::scan_drive(root_path);
31+
32+
(new_games, new_apps)
33+
};
4134

4235
state_clone.set_games(new_games);
4336
state_clone.set_homebrew_apps(new_apps);

ui/app-window.slint

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ export component AppWindow inherits Window {
6060
private property <int> game-to-delete: -1;
6161
private property <int> homebrew-app-to-delete: -1;
6262

63-
in-out property <Config> config;
64-
63+
in property <Config> config;
6564
in property <string> app-version;
6665
in property <string> data-dir;
6766
in property <string> status;
@@ -104,48 +103,48 @@ export component AppWindow inherits Window {
104103

105104
if current-page == Page.Games && config.contents.view-as == ViewAs.Grid: GameGridPage {
106105
games: games;
107-
config <=> config;
106+
config: config;
108107
open-info(i) => {
109108
current-game-i = i;
110109
}
111110
}
112111
if current-page == Page.Games && config.contents.view-as == ViewAs.Table: GameTablePage {
113112
games: games;
114-
config <=> config;
113+
config: config;
115114
open-info(i) => {
116115
current-game-i = i;
117116
}
118117
}
119118
if current-page == Page.HomebrewApps && config.contents.view-as == ViewAs.Grid: HomebrewAppsGridPage {
120119
apps: homebrew-apps;
121-
config <=> config;
120+
config: config;
122121
open-info(i) => {
123122
current-homebrew-app-i = i;
124123
}
125124
}
126125
if current-page == Page.HomebrewApps && config.contents.view-as == ViewAs.Table: HomebrewAppsTablePage {
127126
apps: homebrew-apps;
128-
config <=> config;
127+
config: config;
129128
open-info(i) => {
130129
current-homebrew-app-i = i;
131130
}
132131
}
133132
if current-page == Page.Osc && config.contents.view-as == ViewAs.Grid: OscGridPage {
134133
apps: osc-apps;
135-
config <=> config;
134+
config: config;
136135
open-info(i) => {
137136
current-osc-app-i = i;
138137
}
139138
}
140139
if current-page == Page.Osc && config.contents.view-as == ViewAs.Table: OscTablePage {
141140
apps: osc-apps;
142-
config <=> config;
141+
config: config;
143142
open-info(i) => {
144143
current-osc-app-i = i;
145144
}
146145
}
147146
if current-page == Page.Settings: SettingsPage {
148-
config <=> config;
147+
config: config;
149148
}
150149
if current-page == Page.About: AboutPage {
151150
app-version: app-version;

ui/components/games-toolbar.slint

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Rust } from "../rust.slint";
1111
import { Config } from "../types.slint";
1212

1313
export component GamesToolbar inherits HorizontalLayout {
14-
in-out property <Config> config;
14+
in property <Config> config;
1515

1616
alignment: space-between;
1717
height: 38px;
@@ -36,15 +36,15 @@ export component GamesToolbar inherits HorizontalLayout {
3636
spacing: 10px;
3737

3838
ShowWiiGc {
39-
config <=> config;
39+
config: config;
4040
}
4141

4242
SortGamesAndHomebrewApps {
43-
config <=> config;
43+
config: config;
4444
}
4545

4646
ViewAs {
47-
config <=> config;
47+
config: config;
4848
}
4949

5050
VerticalLayout {

ui/components/homebrew-apps-toolbar.slint

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Rust } from "../rust.slint";
1010
import { Config } from "../types.slint";
1111

1212
export component HomebrewAppsToolbar inherits HorizontalLayout {
13-
in-out property <Config> config;
13+
in property <Config> config;
1414

1515
alignment: space-between;
1616
height: 38px;
@@ -35,11 +35,11 @@ export component HomebrewAppsToolbar inherits HorizontalLayout {
3535
spacing: 10px;
3636

3737
SortGamesAndHomebrewApps {
38-
config <=> config;
38+
config: config;
3939
}
4040

4141
ViewAs {
42-
config <=> config;
42+
config: config;
4343
}
4444

4545
VerticalLayout {

0 commit comments

Comments
 (0)