Et salut à tous !
J'avais présenté il y a quelques mois une lib sur laquelle je travail à mes heures perdues : lien
Je profite d'avoir un peu fait évoluer le bouzin pour vous le représenter plus convenablement.
RML est un petit framework GUI écrit en Rust, inspiré par le QML (le langage de déclaration visuel de Qt). Il permet de définir des interfaces 2D de façon déclarative, via un DSL (Domain Specific Language), tout en s’appuyant sur macroquad pour le rendu et la gestion des entrées utilisateur. Et je ne le dirais jamais assez, macroquad c'est le feu.
C'est avant tout un terrain d’expérimentation et ça n'est pas prêt pour un usage en production. J'espère l'utiliser moi même dans un projet perso, mais pour le moment il me manque trop de choses par rapport au QML ou à Slint (dont vous avez sûrement entendu parler ici) par exemple. Clairement si vous cherchez un QML like en Rust, c'est Slint que vous devez utiliser.
(Parenthèse)
En fait l’existence de Slint (anciennement sixtyfps) m'avait un peu découragé quand je l'ai découvert il y a quelques années, parce que concrètement il faisait ce que je voulait faire et surtout, mieux que moi.
Et puis, je me suis dit que comme j'avais vraiment envie de voir comment tout ça fonctionnait, et que ça me ferait progresser en Rust, je pouvais toujours essayer.
Ça fait toujours un projet perso de plus, et au pire je le laisse mourir.
Je me suis quand même posé la question de savoir si c'était bien ou mal, par rapport aux développeurs de Slint. Mais en vrai, on joue pas dans la même cour (sans rire, ça tient avec de la ficelle dedans). Et je ne pense pas que des gens prêt à payer pour Slint se tourneraient vers mon DSL, sachant que je ne fournit aucun support, que c'est ultra bancal et que ça ressemble plus à un projet étudiant.
Donc, j'ai évacué ce problème (oui je me torture la tête).
(Fin de parenthèse)
Donc RML :
DSL déclaratif : On peut déclarer des Node, comme dans QML, avec des propriétés (number, bool, string, color), un système d'ancres basique (anchors, margins).
Les propriétés sont de base non typées, et il n'y en a que très peu qui soit ajoutées automatiquement à un nœud (x,y,width,height). Toutes les autres sont crées uniquement si l'utilisateur les définies. Au moment du rendu, si la propriété "text" d'un nœud "Text" n'existe pas, on n'affiche simplement pas le texte. En prenant un autre exemple, si anchors n'est pas défini, on passe la partie ancrage.
Callbacks et fonctions Rust : Plutôt que d’utiliser un langage de script, les callbacks et les fonctions sont directement en Rust. Le DSL permet d’appeler ces fonctions, de lire/modifier les propriétés des nœuds via un sucre syntaxique avec le signe .root.counter = …), ou bien directement avec les méthode du moteur rml.
Arbre d’UI : La macro rml! construit un arbre (un arena tree) au moment de la compilation, ce qui rend la structure de l’interface statique, j'ai pour projet d'ajouter un mécanisme d’instanciation au runtime.
Événements**** : J'ai un système d’événement qui fonctionne avec mes propriétés (on_change, binding) et avec les événements de macroquad que j'ai remapé.
Types de noeuds supportés : Actuellement, les éléments Node, Rectangle, Text (texte), MouseArea et Texture sont pris en charge.
Import de composants : Il est possible d’importer des “composants” externes dans des fichiers RML, ce qui permet de structurer l’UI. J'aimerais bien étendre la logique de l'importation, pour le moment c'est vachement basique, mais ça m'a pris un moment pour la faire fonctionner.
Rendu : Le rendu est assuré par Macroquad, ce qui permet de dessiner l'UI dans une fenêtre Rust avec un backend graphique cross-plateforme. C'est une des lib graphique les plus abordables et les plus amusantes que j'ai utilisé.
C'est distribué sous licence MIT.
Bref, dans cette version 0.1.3 j'ai ajouté une étape de pré parse, pour déterminer à l'avance le type de mes propriétés, et de fil en aiguille j'ai décidé que c'était beaucoup plus simple de les typer.
Mais je n'ai pas rendu ça impératif, donc on a la possibilité de préfixer les propriétés avec un type (number, bool, string, color), mais ça n'est pas obligatoire. Ça le devient si on veut utiliser la dite propriété avec la syntaxe $ dans les blocs de code Rust embarqués.
J'ai également ajouté dans cette version le nœud Texture (pour afficher de belles textures), et une gestion des polices de caractère. Ah et le support d'une propriété radius sur les nœuds Rectangle.
Voilà mon exemple principale :
// This example demonstrates how to use the RML library to create a simple 2D GUI
use rml_core::prelude::*;
use rml_macros::rml;
fn window_conf() -> Conf {
Conf {
window_title: "RML Example".to_owned(),
window_width: 500,
window_height: 500,
window_resizable: true,
fullscreen: false,
platform: miniquad::conf::Platform {
linux_backend: miniquad::conf::LinuxBackend::WaylandOnly,
..Default::default()
},
..Default::default()
}
}
#[macroquad::main(window_conf)]
async fn main() {
let mut engine = rml!(
// Importing components from the components folder with an alias access
import "components" as UI
Node {
// Id, anchors, margins are special properties, internaly thez are string type
id: root
anchors: fill
// Color properties expect a Color return type
color color: { DARKGRAY } // color properties expect a Color return type
// A simple counter stored in the root node it's a typed property
number counter: 0
// A custom signal that can be emitted, internally it's a boolean property
signal clicked
// The callback, block initializer and functions are all in plain Rust syntax
// with the addition of the dollar syntax `$` to access nodes and properties
// more easily, it is also possible to use the full API via the `engine` variable
on_clicked: {
// When the signal fires, increment the counter
$.root.counter = $.root.counter + 1.0; // numbers are f32
$.label.text = format!("Counter: {}", $.root.counter);
}
// A function used inside UI properties
fn compute_font_size() -> u32 {
// Dynamic but constant here—just to demonstrate usage
18 + 6
}
// Background panel
Rectangle {
id: background_panel
anchors: fill
margins: 20
color color: {
// color shifts slightly as counter increases
Color::new(0.52, 0.0, ($.root.counter/20.0).min(1.0), 1.0)
}
// Wow such a beautiful texture
Texture {
id: background_texture
anchors: fill
margins: 20
source: "Adriaen"
keep_aspect_ratio: true
// if not the texture will be stretched to fill the area
}
}
// Text label
Text {
id: label
anchors: center
// Text property is typed as string
string text: { format!("Counter: {}", $.root.counter) }
// Font is not typed as string
font: "liberation"
// But they are string properties anyway.
// It's important to type the properties if they will be used somewhere with the dollar sugar syntax
// in other case, the engine will just use the property as needed without type checking.
// Because internally, properties are stored as AbstractValue enum, and can be retyped at runtime.
color color: { invert_color($.background_panel.color, 1.0) }
number font_size: { compute_font_size() } // <= will generate a callback and an initialiser
// if a property binding is detected in the callback, it is binded and re-evaluated when the property changes
}
// Button
UI::Button {
anchors: center | bottom
margins: 30
text: "Increment!"
font: "liberation"
on_click: {
// Emit a signal handled by the root node
emit!(engine, root, clicked);
}
}
}
);
// We add a font to use in the RML
let font = load_ttf_font("./LiberationSerif-Regular.ttf")
.await
.unwrap();
engine.add_font("liberation".to_string(), font);
// We add a texture to use in the RML
let texture = load_texture("./Adriaen_van_Ostade_006.png").await.unwrap();
engine.add_texture("Adriaen".to_string(), texture);
set_default_filter_mode(FilterMode::Nearest);
loop {
engine.process_events();
clear_background(BLACK);
rml_core::draw::draw_root(&mut engine);
next_frame().await
}
}
Ça donne ça :

J'espère que ça vous aura intéressé, perso je trouve ça très satisfaisant à faire.
Je tombe parfois sur des problèmes assez ardu (pour moi en tut cas), par exemple avec mes macros, je suis sur qu'on doit pouvoir faire bien plus simple et propre.
Si jamais vous avez envie de m'aider, le projet est là ;)
Sur ce, bonne journée, force et honneur !

# HS ou pas ? egui
Posté par bunam . Évalué à 1 (+0/-0).
J'ai vu ce truc ça mas tué car ne connaît pas d'autres : https://www.egui.rs/ egui is an immediate mode GUI library written in Rust. egui runs both on the web and natively on 🐧.
des démos de trucs IA ou pas toujours sur le web :
https://rerun.io/viewer
# Rustic
Posté par Christophe . Évalué à 2 (+0/-0). Dernière modification le 20 novembre 2025 à 13:51.
Attention au petit conflit de nom avec l'excellent Rustic , un gestionnaire de sauvegardes compatible avec Restic et écrit en Rust…
Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.