Vérifié sur macOS, Windows et Linux

Le dessin est du Keal.

Pas des liaisons vers une boîte à outils, pas une enveloppe autour d'une toile écrite en C. Le rasteriseur, le moteur TrueType, la mise en page, les widgets, le thème et le docking sont des fichiers .keal. Le C en dessous ouvre une fenêtre, rapporte ce que l'utilisateur a fait, et pose à l'écran un tampon de pixels déjà fini. Il ne dessine rien.

bonjour.keal320 × 200 pt
import "keal-view/keal-view.keal"

val clicks = state(0)

runApp("Hello", 320, 200, { ->
    column([
        label("Clicked ${clicks.get()} times").fontSize(20.0).centered(),
        button("Click me", { -> clicks.set(clicks.get() + 1) }).kindOf(primary)
    ]).gaps(12.0).padAll(24.0).aligned(mainCenter, crossCenter).grows()
})
Clicé 3 fois
Clique-moi
Chaque pixel ci-dessus est sorti d’une boucle écrite en Keal.
Un espace de travail à panneaux construit avec keal-view
Chaque pixel ci-dessus — les coins arrondis, les bordures anti-crénelées, les ombres, les glyphes — est sorti d'une boucle écrite en Keal.

87 à 89 % d'un programme keal-view qui tourne est du Keal.

Lequel des deux dépend du backend contre lequel il a été construit, et aucun des 11 à 13 % restants ne pose un pixel où que ce soit. Toute la surface C tient dans un en-tête : un tampon d'image, un bloc d'octets, une structure d'événement, et les accesseurs de chacun.

Le côté Keal, c'est tout le framework : rasteriseur, polices, mise en page, widgets, thème, docking, menus, boucle d'exécution. Le côté C, c'est une fenêtre, une file d'événements et des accesseurs inlinés : kv.h à 281 lignes plus un backend sur trois — Cocoa 409, X11 567, Win32 573. La barre montre le plus gros des trois.

Keal
5 598 lignes
C
690-854 lignes

Pourquoi c'est seulement possible

Un seul fait du compilateur décide de toute la conception. native """…""" colle son C dans la même unité de traduction que le programme compilé, donc une fonction static inline déclarée là est inlinée par le compilateur C dans le code de Keal lui-même. Un appel à travers la frontière n'est pas un appel — c'est l'instruction qu'il contient.

src/ffi.keal
extern func kvPxSet(x: Int, y: Int, argb: Int): Int = "kv_set"

Cette ligne est une seule écriture avec vérification de bornes. Mesurée à environ une demi-nanoseconde par pixel depuis une boucle Keal, ce qui est le prix d'un rasteriseur C, parce qu'après inlining c'est le rasteriseur C. Il n'y avait donc aucune raison d'écrire le dessin en C, et toutes les raisons de ne pas le faire.

Ce qu'il y a dedans

Widgets

Étiquettes, boutons, cases à cocher, boutons radio, interrupteurs, contrôles segmentés, listes déroulantes, curseurs, incréments, progression, champs, onglets, cartes, panneaux, zones défilantes — et custom, à qui l'on remet une toile déjà découpée, son rectangle, les polices, le thème et son propre état de survol.

Menus, dialogues et bulles

openMenu, openDialog, openSheet et .tip("…"). Rien à installer : la boucle compose cette couche au-dessus de la surcouche de l'application.

Mise en page

Un axe de flexbox et pas davantage. Il n'y a pas de compression : ce qui ne rentre pas est découpé plutôt que serré, parce qu'une mise en page qui compresse en silence ne casse que sur les petites fenêtres, l'endroit où personne ne regarde.

Texte

Un analyseur et un rasteriseur TrueType écrits en Keal : formats cmap 0, 4, 6 et 12, glyf avec les glyphes composites, les collections. Les glyphes sont remplis par la méthode de l'aire signée et rasterisés à la taille où ils seront réellement dessinés.

Dessin

L'anti-crénelage est analytique, pas échantillonné : la couverture d'un bord droit est son recouvrement exact avec la cellule du pixel. Il n'y a pas de réglage de qualité, ni d'image discrètement moins chère que la précédente.

Thème

Sombre et clair, construits depuis une rampe de surfaces, un accent et trois teintes d'état plutôt qu'une liste de codes hexadécimaux. useTheme(lightTheme()) est tout le changement, y compris pour les widgets écrits après.

Des points, pas des pixels

Le rasteriseur est la seule chose qui sache ce qu'est un pixel. Une mise en page est juste sur un écran Retina et sur un projecteur ; les nombres ne changent pas, l'échelle si.

Une fenêtre au repos ne coûte rien

Entre deux images, la boucle est dans kvWait, qui bloque jusqu'à ce que le système ait quelque chose à dire. Pas un faible taux d'activité — aucun. Sur un portable, ça vaut plus que la vitesse de dessin.

Un docking qui dit d'avance où ça va tomber

L'agencement est un arbre binaire à exactement deux cas — une feuille qui tient des onglets, et une coupe qui tient deux enfants et une fraction — ce qui est assez peu pour que chaque état possible soit un état sur lequel on peut raisonner.

Un panneau se traîne par son onglet ; pendant le déplacement, la zone mise en évidence sous le pointeur dit où le lâcher le poserait — à gauche, à droite, au-dessus, en dessous, ou comme onglet de plus — avant qu'on lâche. Un panneau est une fonction de vue ordinaire et ignore qu'il est ancré.

examples/studio.keal
val dock = Dock(
    beside(
        leaf(["files"]),
        above(
            beside(leaf(["editor"]), leaf(["props", "preview"]), 0.68),
            leaf(["terminal"]),
            0.72),
        0.19))

dock.add(panelOf("files", "Files", { -> filesPanel() }))

Trois plateformes, une douzaine de fonctions

Les trois backends ont été écrits contre la même douzaine de fonctions, et rien au-dessus de runtime/ n'a changé pour ajouter le deuxième et le troisième — ce qui est tout l'intérêt d'avoir tracé la frontière là.

PlateformeBackendÉtat
macOSruntime/kv_cocoa.mCocoa. Vérifié — développé ici
Windowsruntime/kv_win32.cWin32 et un DIB BI_RGB. Vérifié sur Windows 10 22H2 et 11 22H2, MinGW-w64
Linuxruntime/kv_x11.cXlib seul, aucune boîte à outils. Vérifié sur Ubuntu 26.04 aarch64, sous Wayland à travers XWayland

Windows et Linux ont chacun été vérifiés par quelqu'un sur une vraie machine, en suivant la liste de portage ligne à ligne, et à eux deux cela a coûté dix-neuf défauts — dont douze au-dessus du backend, donc présents sur toutes les plateformes, y compris celle où tout a été écrit. Quatre choses restent non vérifiées et sont consignées plutôt que passées sous silence.

Ce que cette semaine a trouvé, et ce qu'elle n'a pas pu →

Le faire tourner

keal-view a besoin du compilateur Keal à côté de lui. Il n'y a pas de système de construction ici : tools/build.sh choisit le backend de la plateforme, le compile une fois, et remet le fichier objet à keal build.

dans un terminal
# the compiler, then the framework
git clone https://github.com/geneacta/keal
git clone https://github.com/geneacta/keal-view
cd keal && cargo build --release && cd ../keal-view

tools/build.sh examples/tour.keal    && build/tour       # start here
tools/build.sh examples/gallery.keal && build/gallery    # every widget
tools/test.sh                                   # 194 checks, no display

Deux choses que tout programme keal-view comprend gratuitement

La première n'a besoin d'aucun écran, ce qui est la façon dont le framework est vérifié en intégration continue et dont chaque image de ce site a été faite.

dans un terminal
build/studio --snapshot frame.bmp 2          # one frame to a file, then exit
build/studio --snapshot frame.bmp 2 900 1900 # …at a size of your choosing
build/studio --window-id /tmp/id             # write its own window number out