Gio UI – Plattformübergreifende GUI für Go
(gioui.org)- Eine Bibliothek, mit der Go-Entwickler Immediate-Mode-GUIs für mehrere Betriebssysteme und WebAssembly erstellen können
- Unterstützt Linux, macOS, Windows, Android, iOS, FreeBSD, OpenBSD und WebAssembly und bietet damit eine breite Plattformabdeckung
- Das Design zielt darauf ab, Abhängigkeiten zu reduzieren und für Fensterverwaltung, Eingabe und GPU-Zeichnen die jeweiligen Plattformbibliotheken zu nutzen
- Das Rendering umfasst einen auf OpenGL ES und Direct3D 11 basierenden Pathfinder-Vektorrenderer und befindet sich im Übergang zu einem Compute-Shader-Renderer auf Basis von piet-gpu
- Text und Formen werden nicht als Texturen vorgerendert, sondern als Konturen gerendert, was Animationen, transformiertes Zeichnen und Unabhängigkeit von der Pixelauflösung unterstützt
Ziel und unterstützte Plattformen von Gio
- Gio ist eine Bibliothek zum Erstellen effizienter, flüssiger und portabler GUIs in Go
- Unterstützte Plattformen sind Linux, macOS, Windows, Android, iOS, FreeBSD, OpenBSD und WebAssembly
- Es gibt eine WebAssembly-Demo für eine schnelle Vorführung; zum Ausführen ist ein Browser mit WebAssembly-Unterstützung erforderlich
- Den Beispielquellcode findet man im Kitchen project
Installation und Lernpfad
- Gio ist mit dem Ziel weniger Abhängigkeiten konzipiert
- Die erforderlichen Abhängigkeiten sind in der jeweiligen plattformspezifischen Installationsdokumentation zu finden
- Nach der Installation kann man mit der Dokumentation Learn und Hello World beginnen
- Der Showcase umfasst unter anderem godcr, Tailscale, gotraceui, Sointu und Protonet
Rendering-Technologie
- Gio kombiniert die Flexibilität des Immediate-Mode-Grafikparadigmas mit moderner 2D-Grafiktechnologie
- Der Vektorrenderer basiert auf dem Pathfinder project und ist auf OpenGL ES und Direct3D 11 implementiert
- Der Renderer befindet sich im Übergang zu einem effizienteren Compute-Shader-basierten Renderer, der auf piet-gpu aufgebaut wird
- Text und Formen werden nicht vorab als Texturbilder gebacken, sondern ausschließlich über Konturen gerendert
- Unterstützt effiziente Animationen
- Eignet sich für transformiertes Zeichnen
- Erhält die Unabhängigkeit von der Pixelauflösung
Finanzierungsmodell
- Die Entwicklung von Gio wird durch Sponsoring finanziert
- Wenn das Projekt nützlich ist, kann man eine Unterstützung des Gio-Projekts auf OpenCollective oder ein direktes Sponsoring der Entwickler in Betracht ziehen
1 Kommentare
Hacker-News-Kommentare
Nach tatsächlicher Nutzung war es unmöglich, damit ernsthafte komplexe Apps zu bauen
Es fehlen Komponenten wie Video, Karten oder Rich Text, die andere Plattformen standardmäßig mitbringen, und es gibt auch keinen klaren und einfachen Weg, sie selbst hinzuzufügen
Alle paar Monate brechen APIs, und es gibt auch keine Möglichkeit, Themes anzuwenden
Immediate-Mode-Grafik ist gut, solange man noch keinen komplexen Zustand verwalten muss, aber ab dann muss man am Ende doch seine eigene Retained-Mode-Grafik implementieren und holt damit ein längst gelöstes Problem wieder hervor
Auch der schicke Renderer auf Basis von
piet-gpunimmt nur Bézier-Kontrollpunkte als Eingabe und tesselliert dann alles; das Konzept ist cool, aber um tatsächlich einen Kreis zu zeichnen, ist man auf eine Näherung mit vier Bézier-Kurven angewiesenWasm ist eher ein Proof of Concept, das das Compiler-Team noch einige Jahre technisch ausreifen lassen müsste, bevor es produktionsreif ist, und insgesamt wirkt es für Go-Entwickler brauchbar, wenn sie einfache UIs mit Listen und Eingabefeldern bauen wollen
Es gibt auch ein Material-Design-Theme sowie einen Light-/Dark-Modus
Ein großartiges Beispiel für eine gioui-App mit Light-/Dark- und benutzerdefinierten Themes ist https://github.com/chapar-rest/chapar
Auf Mac oder Windows reicht
go run .Text-Kerning, Text entlang von Bögen sowie RTL/LTR sind dank
github.com/go-text/typesettingebenfalls möglichKomplexe Widgets wie Kalender-Spinner oder Diagramme gibt es auch auf GitHub, aber es fehlt an Bemühungen, diese Dinge an einem Ort zu bündeln
Wenn das geschieht, dürfte der Anreiz für mehr Entwickler groß genug sein, einzusteigen
Es scheint keine Kultur zu geben, in der wichtig ist, dass Nutzer-Code nicht kaputtgeht
Beide sind derzeit stabil und verfügen über vergleichsweise umfangreiche Sammlungen an Controls und Bibliotheken
Im Web scheint es wie Flutter alles auf ein Canvas zu rendern; dieser Ansatz ist bekanntermaßen problematisch bei Barrierefreiheit und nativer Anmutung
Man kann nicht per Tab zwischen Radio-Buttons wechseln, und auf macOS markiert
CMD+Anicht den gesamten Inhalt eines Textfelds, währendCTRL+AfunktioniertMöglich wäre das wohl, aber der Aufwand dürfte ebenfalls beträchtlich sein
Etwas abseits des Themas, aber ich frage mich, was heutzutage der beste Weg ist, plattformübergreifende Mobile- und Web-Apps zu entwickeln
Egal ob man sowohl die Business-Logik als auch die UI teilt oder nur die Business-Logik
Ich habe zwischen Optionen wie gomobile, Rust und TypeScript geschwankt
Eine Zeit lang schien TypeScript die portabelste Technologie zu sein, sodass ich es für die gesamte Business-Logik verwenden wollte, aber dann habe ich festgestellt, dass es keinen guten Weg gibt, JavaScript auf iOS mit akzeptabler Performance auszuführen
Wenn es für dich in Ordnung ist, die UI nativ zu schreiben und nur die Business-Logik zu teilen, ist Kotlin ebenfalls eine Option: https://kotlinlang.org/docs/multiplatform.html#kotlin-multip...
Mit Compose kann man auch die UI in Kotlin erstellen: https://www.jetbrains.com/lp/compose-multiplatform/
Allerdings ist die iOS-Unterstützung noch im Alpha-Stadium und das Web ist „experimental“, daher ist Flutter, das auf allen Plattformen bereits recht stabil ist, die richtige Wahl, wenn man nicht in Kauf nehmen will, mit der Weiterentwicklung des Frameworks den Code anpassen zu müssen
Wenn du TypeScript und React bereits kennst, kannst du auch React Native in Betracht ziehen, aber zur Performance auf iOS oder anderswo würde ich keine Garantie geben: https://reactnative.dev/
Ich habe zu viele Frameworks erlebt, die versprochen haben, alles zu lösen, und dieses Versprechen nicht halten konnten
Anfangs kommt man schneller voran, aber schon bald patcht man die Kernbibliotheken, um nach einem OS-Update die FPS wieder in die Nähe von nativ zu bringen oder Systemanimationen ähnlicher wirken zu lassen
Zeit spart man nur, wenn man die UI nicht wirklich ausarbeitet
Die Kernlogik kann man gemeinsam nutzen
Ich verwende gomobile und bin im Großen und Ganzen zufrieden damit, aber der Runtime-Overhead von 3 MB macht es ungeeignet fürs Web
Kotlin Multiplatform sah gut aus, aber grundlegende Bibliotheken fehlten, und weil es sie für Kotlin Android schon gab, schien kaum jemand plattformübergreifende Entsprechungen zu bauen
Rust und Mozillas Sprach-Binding-Schicht sehen ebenfalls gut aus, aber ich habe sie noch nicht ausprobiert
Flutter ist auch nicht schlecht, aber fürs Web rendert es auf ein Canvas, was sich in Bezug auf Gefühl und Barrierefreiheit nicht gut anfühlt
Auch auf iOS gibt es selbst nach der Einführung der Impeller-Rendering-Engine weiterhin Latenzprobleme
Wenn man sich den Bluesky-Client ansieht, ist die Performance über die unterstützten Plattformen hinweg ordentlich, und er verwendet eine einzige Codebasis
https://github.com/bluesky-social/social-app
Es ist Open Source und zielt auf Android, iOS, Windows, Mac und Linux ab: https://platform.uno/platforms/
Es verwendet C# und implementiert Views und Controls automatisch mit den nativen UI-Frameworks der jeweiligen Plattform
Die IDE-Unterstützung für Visual Studio, VS Code und Rider ist ebenfalls gut, und man ist auch nicht auf andere Tools beschränkt
Es gibt auch ein Figma-Plugin für die Design-Zusammenarbeit
Ich weiß nicht, ob es auch für Consumer-Produkte ein gutes Werkzeug ist
Für unseren Einsatz passt es gut genug, hauptsächlich weil es sich um Tools für Techniker von Solarkraftwerken handelt und plattformübergreifendes TypeScript unter schlechten Internetbedingungen für ein kleines Team zu belastend geworden war
Ich entwickle mit gioui eine Streaming-App, und es ist sehr einfach, außerdem verlaufen Upgrades immer reibungslos
Das liegt an Go und daran, dass die Kernentwickler Änderungen ziemlich ernst nehmen
Wenn ich eine Web-GUI brauche, verwende ich dieses gioui-Plugin-System: https://github.com/gioui-plugins/gio-plugins
Es ist erstaunlich, dass WebView im Web, auf dem Desktop und auf Mobilgeräten funktioniert
Deep Links funktionieren ebenfalls, sodass beim Versenden von E-Mails oder Monike-Benachrichtigungslinks die App des Nutzers genau an der richtigen Stelle der GUI geöffnet wird
Es gibt auch Benachrichtigungen und Share Extensions für alle Betriebssysteme, daher würde ich sagen, dass es fast schon ein wirklich vollständiges System ist
Ich stimme zu, dass es schwierig ist, alle Betriebssysteme zu unterstützen, aber in der heutigen Welt ist Vielfalt der Standard
Mir gefällt, dass man das alles nur mit Go umsetzen kann, ohne zwischen mehreren Technologien hin- und herzuwechseln
Ich schreibe das Go-Backend immer so, dass es sowohl mit gio als auch mit HTML funktioniert
Wenn SEO oder Videowiedergabe nötig sind, wird das im WebView verarbeitet, und auch Googles SEO-Anforderungen auf der Web-Seite von gio lassen sich erfüllen
Ich packe Markdown in Hugo, damit Google es für SEO sehen kann
Aus Sicht eines Go-Anfängers ist dieser Teil der Dokumentation interessant.
Es heißt, dass man
op.ColorOp{Color: red}.Add(ops)stattops.Add(ColorOp{Color: red})verwendet, damit dieAdd-Methode kein Argument vom Interface-Typ entgegennimmt und so beim Aufruf keine Allokation entsteht; das sei ein zentraler Punkt in Gios „zero allocation“-Design.Ich würde gern verstehen, warum dabei eine Allokation entsteht, was genau allokiert wird und wie dadurch etwas eingespart wird.
Wenn eine Funktion ein Argument vom Interface-Typ entgegennimmt und man ihr ein reines Struct übergibt, baut Go darum einen Wrapper, und genau das ist die im Zitat erwähnte Allokation.
Dieser Wrapper ist ein Zeigerpaar aus Typ-/vtable-Zeiger und einem Zeiger auf die Struct-Daten.
Dadurch sind Typinferenz zur Laufzeit und implizite Interface-Erweiterung möglich.
Ein Interface zu implementieren bedeutet also einfach, die Methoden zu implementieren; man muss den Typ nicht explizit angeben wie bei
ByteReader extends Reader.Diese Kosten fallen nur bei der Verwendung an, deshalb nutzen viele schnelle Codepfade nach Möglichkeit nur Structs.
v := interfaceType(concreteTypeValue)macht, passiert auf niedriger Ebene ungefähr Folgendes:dataPtr := &concreteTypeValue,typePtr := typeData[concreteType](), und dann wird ein Interface-Wert aus Datenzeiger und Typzeiger aufgebaut.Die erste Zeile ist hier die Allokation, denn soweit ich mich an die Regeln erinnere, dürfen Pointer in Go nicht auf Stack-Werte zeigen, also muss
concreteTypeValueauf dem Heap allokiert werden.Diese Regel, dass Pointer nicht auf den Stack zeigen, dient dazu, dass Goroutine-Stacks leichter dynamisch wachsen können.
Siehe https://go.dev/doc/faq#stack_or_heap
ColorOp{Color: red}geboxt und auf dem Heap allokiert werden.Der Grund ist, dass
ops.Addim Allgemeinen einen dicken Pointer auf einen Wert erwartet, der ein bestimmtes Interface implementiert, und nicht einfach einen konkreten Typwert.op.ColorOp{Color: red}.Add(ops)seltsam.Für mich klingt es wie „addiere
opszum Ergebnis vonop.ColorOp{Color: red}“.Deshalb würde ich die Funktion gern
AddTonennen:op.ColorOp{Color: red}.AddTo(ops)Das ist zwar immer noch nicht ganz idiomatisch, signalisiert aber wenigstens, dass das Funktionsargument verändert wird.
Interessant ist, dass auf einem ziemlich gewöhnlichen PC mit Windows 10 und Chrome die WASM-Demo auf der ersten Seite nur schwarze Rechtecke dort rendert, wo eigentlich Text sein sollte.
In Chrome auf meinem Android-Handy wurde alles korrekt gerendert.
Außerdem lief es extrem langsam.
Ich habe mit Go eine kleine App mit Fyne gebaut und würde es nie wieder benutzen.
Sowohl Gio als auch Fyne fehlen deutlich die Politur und der Funktionsumfang, die Flutter bietet.
Ich habe mich entschieden, die Kernlogik in Golang zu schreiben und sie in eine Android-App einzupacken, aber die GUI sah aus wie aus dem Jahr 2003, und die Möglichkeiten, daran etwas zu ändern, waren begrenzt.
Man kann die gesamte Logik in Go schreiben und die UI in HTML erstellen, mit oder ohne Web-Framework.
Es ist ähnlich wie Electron, aber leichter, weil Chrome nicht mit ausgeliefert wird und stattdessen der Web-Viewer des Systems verwendet wird.
[1] https://github.com/wailsapp/wails
Es wäre schön, wenn du erklären könntest, wie du es in eine Android-App gepackt hast.
Warum sehen diese Cross-Platform-GUIs alle so aus, als wären sie vor 50 Jahren entworfen worden?
Die Demo funktioniert bei mir nicht.
In Chromium unter Windows 11 sehe ich ein paar Buttons, aber das meiste ist komplett schwarz.
Im Unterschied zu Fyne ist es ein gutes Zeichen, dass diese Library meinen ersten Test bestanden hat: CJK-Textrendering.
Fyne schafft das nicht, wenn man ihm nicht eine einzige Custom-Font gibt, die alles rendern kann.
Eine einzelne Font zu finden, die alle weltweit gebräuchlichen Schriftsysteme und dazu noch Emoji zufriedenstellend abdeckt, ist Glückssache.
Deshalb ist Fyne für mich sofort raus, wenn ich etwas mit nutzergenerierten Inhalten, Web-Inhalten oder auch nur der geringsten Möglichkeit von Lokalisierung bauen will.