Actuellement j'ai du code qui fait ça:

Globalement, c'est architecturé en dessinant le triangle indépendamment de l'interface (l'interface se dessine par dessus). Mais tout ce qu'il faut retenir c'est que la logique de rendu de l'interface est séparée dans un objet, et celle du triangle aussi.
Jusque là pourquoi pas ça marche plutôt bien.
Maintenant on va rajouter un peu d'interactivité: quand l'utilisateur remplit le textbox correctement, un tableau s'affiche. Là encore, ça marche très bien, parce que c'est un comportement qui se limite à l'interface:

Vous noterez que y a un bouton sur la ligne du tableau. Et maintenant, moi, je veux détecter le clic sur le bouton. Evidemment je peux ajouter ça dans l'objet qui gère le dessin de l'interface (ça fait partie de l'état de l'interface après tout), mais par contre l'action correspondante (à savoir aller lire des fichiers binaires etc) n'a rien a faire dans l'interface, vu qu'il s'agit de charger des données qui sont utilisables par n'importe quel composant de rendu (en gros, le triangle peut aussi en avoir besoin).
Du coup je me dis que je peux faire un event listener classique tout con, et en plus j'ai même pas besoin de m'emmerder avec de la synchro ou quoi puisque j'ai juste à enregistrer les listeners à l'allocation des structures; une fois que l'appli est setup visuellement ça ne bouge plus.
#[derive(Default)]
pub struct InterfaceState {
installation_path : String, // La chaîne de caractères de la textbox
active_tab : Tab,
// event handlers
event_handlers : Vec<Box<dyn Fn(&InterfaceEvent)>>,
}
impl InterfaceState {
pub fn publish_event(&mut self, event : InterfaceEvent) {
for handler in &mut self.event_handlers {
handler(&event)
}
}
pub fn add_event_listener<F>(&mut self, handler : F) where F : Fn(&InterfaceEvent) + 'static {
self.event_handlers.push(Box::new(handler));
}
}
(Il manque le Rust dans la coloration syntaxique ^^)
A priori pas de problème, par contre les emmerdes commencent quand on essaie de s'en servir.
fn setup(app : &mut Application, window : Window) -> ApplicationData {
let renderer = RendererOptions::default()
.line_width(DynamicState::Fixed(1.0f32))
.multisampling(vk::SampleCountFlags::TYPE_4);
let mut renderer = Renderer::builder(app.context.clone())
.build(renderer, window, vec![ash::khr::swapchain::NAME.to_owned()]);
let mut slf = ApplicationData {
geometry : GeometryRenderer::new(&mut renderer, false),
interface : {
let _theme = theming::themes::StandardDark{};
let style = egui::Style::default(); // _theme.custom_style();
let mut fonts = FontDefinitions::default();
load_fonts(&mut fonts, &None, "./assets/fonts");
let options = InterfaceOptions::default(|ctx, state : &mut InterfaceState| state.render(ctx))
.fonts(fonts)
.style(style);
InterfaceRenderer::new(&renderer.swapchain, &renderer.context, true, options)
},
renderer,
fs : None,
};
slf.interface.state.add_event_listener(|event| {
match event {
InterfaceEvent::InstallationSelected { path, cdn, build, .. } => {
slf.load_game_install(cdn, build, path)
},
};
});
slf
}
// Pour référence...
pub struct ApplicationData {
renderer : Renderer,
geometry : GeometryRenderer,
interface : InterfaceRenderer<InterfaceState>,
// An accessor around the game files
fs : Option<FileSystem>,
}
impl ApplicationData {
fn load_game_install(&mut self, cdn : &str, build: &str, path : &PathBuf) -> Result<(), Error> {
let fs = match FileSystem::open(path, cdn, build) {
Ok(fs) => Some(fs),
Err(err) => return Err(err),
};
self.fs = fs;
Ok(())
}
}
Pas d'erreur dans l'IDE, par contre quand on essaie de compiler...
error[E0596]: cannot borrow `slf` as mutable, as it is a captured variable in a `Fn` closure --> wowedit\src/main.rs:81:17 | 81 | slf.load_game_install(cdn, build, path) | ^^^ cannot borrow as mutable
En gros, l'appel à add_event_listener travaille sur un mutable (slf est déclaré mut, donc slf.interface.state est accédé mutablement); je stocke une lambda qui elle aussi a besoin d'accéder à slf mutablement, et c'est la merde: Rust interdit d'avoir deux borrow mutables en même temps (pour des raisons que je ne comprend pas très bien encore, mais admettons).
Sauf que moi j'ai pas particulièrement envie de stocker des états dans tous les sens, avoir des évènements comme ça ça me plaît bien, ça m'évite de stocker des trucs sans raison et d'en plus devoir gérer le cas où l'utilisateur a pas encore cliqué (globalement je devrais avoir un Option<...> et pas juste les données) L'event est lancé une seule fois, j'initialise le filesystem, et on en parle plus.
Mais non, faut que le borrow checker me casse les couilles, et là les "rustacéens" (ces gens qui se croient meilleurs que les autres, pour le peu que j'ai parlé avec eux) me sortent qu'il faut passer par un Rc<RefCell<T>> pour avoir de la "mutabilité intérieure". et moi je dis: pourquoi je dois activement m'emmerder avec ce truc là?
Bref ça m'énerve, plus j'avance plus je me rend compte que les cas simples ça marche très bien mais dès qu'on essaie un truc avec un tout petit peu de complexité c'est la merde parce que le borrow checker nous casse activement les couilles.
le code est ici
GitHub - Warpten/rust-pg: rust playing ground, don't look too much into thisGitHubrust playing ground, don't look too much into this - Warpten/rust-pg