- Spot ist ein einfaches reaktives Toolkit zum Erstellen von plattformübergreifenden Desktop-GUIs in Go, das nach Möglichkeit native Widgets verwendet und plattformspezifische APIs konsistent bereitstellt
- Es arbeitet, indem bei Änderungen des Anwendungszustands der unveränderliche Komponentenbaum neu erstellt und mit dem vorherigen Zustand verglichen wird, um zu bestimmen, welche UI-Controls aktualisiert werden müssen
- Die aktuellen Backends sind eine auf Cocoa basierende Implementierung für macOS und eine auf FLTK basierende Implementierung für andere Plattformen; auf macOS kann optional ebenfalls FLTK verwendet werden
spotist das backendunabhängige Core-Paket, das das reaktive Modell und das Rendering bereitstellt, undspot/uiist eine Sammlung vorgefertigter plattformübergreifender GUI-Controls- Automatisches Layout, mehrere Fenster, modale Dialoge, größenveränderbare Fenster, Menüleiste, benutzerdefinierte Widgets, Zugriff auf native Widgets, Drag-and-Drop und Internationalisierung werden noch nicht bereitgestellt
Zweck und Grundmodell von Spot
- Spot ist ein reaktives GUI-Toolkit für Go, das so entwickelt wurde, dass es nach Möglichkeit native Widgets verwendet und gleichzeitig über mehrere Plattformen hinweg eine konsistente API bietet
- Es kann dem Projekt als einfache Abhängigkeit hinzugefügt werden; wenn man nur Go-Code schreibt, lassen sich ohne zusätzliche Tools oder Codegenerierung eigenständige native GUI-Binärdateien erstellen
- Das Beispiel erstellt Fenster und Button im Ablauf
ui.Init(),spot.MountFn(...),ui.Run()und verwaltet den Zustand der Klickanzahl mitspot.UseState[int](<https://github.com/roblillack/ctx, 0>) - Der Klick-Handler des Buttons ruft
setCounter(counter + 1)auf, und wenn sich der Zustand ändert, wird der Titel des Buttons in das Format"Clicked %d times!"geändert
Reaktiver Update-Ansatz
- Reactive bedeutet in Spot, dass die UI automatisch aktualisiert wird, wenn sich der Anwendungszustand ändert
- Bei einer Zustandsänderung wird der unveränderliche Komponentenbaum neu erstellt und schnell mit dem vorherigen Zustand verglichen, um zu entscheiden, welche UI-Controls aktualisiert werden sollen
- Im Web wird diese Idee oft als virtual DOM bezeichnet; Spot brachte dieses Konzept in die Go-Desktop-Umgebung und begann als Experiment, eine React-ähnliche GUI-Bibliothek zu implementieren
- Statt die UI manuell zu aktualisieren, verwalten Entwickler die Anwendungslogik und den Zustand mit nebenwirkungsfreien Rendering-Funktionen und Hooks wie
UseState
Backends und Paketstruktur
- Spot wählt beim Kompilieren automatisch das zum Zielsystem passende Backend aus
- Derzeit werden zwei Backends bereitgestellt
- Auf FLTK basierende Implementierung: verwendet go-fltk
- Auf Cocoa basierende Implementierung: verwendet eine angepasste Version von gocoa
- Auf macOS wird das Cocoa-Backend verwendet, auf anderen Plattformen das FLTK-basierte Backend
- Auf macOS kann optional auch FLTK verwendet werden; eine Verbesserung der Windows-Unterstützung ist für die Zukunft geplant
spotist das Core-Paket, das das reaktive Modell und Rendering-Funktionen bereitstellt, und kann mit jeder Control-Sammlung verwendet werden, die das Interfacespot.Controlimplementiertspot/uiist ein Paket mit vorgefertigten plattformübergreifenden GUI-Controls, das zusammen mit Spot verwendet werden kann
Komponenten, Controls und Hooks
- Wie in React können benutzerdefinierte Hooks erstellt werden
- Man erstellt eine Funktion, die als erstes Argument
*spot.RenderContexterhält, und ruftspot.UseState,spot.UseEffectusw. auf, um sie in den Spot-Lebenszyklus einzubinden - Konventionell beginnt der Funktionsname mit dem Präfix
Use…
- Man erstellt eine Funktion, die als erstes Argument
- Benutzerdefinierte Komponenten können als Structs erstellt werden, die das Interface
spot.Componentimplementieren- Dieses Interface hat die einzelne Methode
Render(ctx *spot.RenderContext) spot.Component - So erstellte Komponenten können auf dieselbe Weise wie eingebaute Komponenten verwendet werden
- Dieses Interface hat die einzelne Methode
- Eine component ist in Spot die logische Einheit, die Business-Logik und Zustand enthält
- Komponenten setzen sich aus anderen Komponenten zusammen und werden letztlich in ein oder mehrere Controls gerendert
- Ein control ist eine spezielle Komponente, die in den UI-Baum gemountet wird und ein visuelles Element auf dem Bildschirm darstellt
- In der Regel basiert es auf nativen Implementierungen des GUI-Backends wie Buttons, Labels oder Texteingaben
- Es können auch vollständig andere Widget-Bibliotheken als die bereitgestellte Widget-Bibliothek verwendet werden
- Dazu erstellt man ein Struct, das das Interface
spot.Componentimplementiert und native Widgets verwaltet
- Dazu erstellt man ein Struct, das das Interface
- Die Verwendung von
spot/uimit anderen Backends als Cocoa oder FLTK wird derzeit nicht unterstützt
Begriffe des Rendering-Lebenszyklus
- Make: der Vorgang, eine Struct-Instanz zu erstellen, die das Interface
spot.Componentimplementiert, oder mitspot.Makezusammen mit einer Render-Funktion eine neue Komponenteninstanz zu erzeugen - Render: der Vorgang, den Komponentenstatus auf Unterelemente anzuwenden und andere Komponenteninstanzen zurückzugeben
- Build: der Vorgang, durch rekursives Rendern von Komponenten einen Control-Baum zu erzeugen
- Dies geschieht, indem man eine Komponenteninstanz an
spot.Buildübergibt oder eine Render-Funktion anspot.BuildFn
- Dies geschieht, indem man eine Komponenteninstanz an
- Mount: der Vorgang, aus dem virtuellen Control-Baum reale UI-Controls zu erzeugen
- Dies kann durch Aufruf von
Mountauf einem Baumknoten oder mitspot.Mountbzw.spot.MountFnerfolgen
- Dies kann durch Aufruf von
- Update: der Vorgang, einen gemounteten Control-Baum zu aktualisieren
- Dies geschieht durch Aufruf von
Updateauf einem Baumknoten
- Dies geschieht durch Aufruf von
Derzeit nicht bereitgestellte Funktionen
- Spot bietet derzeit die folgenden Funktionen nicht
-
Automatisches Layout
- mehrere Fenster
- modale Dialoge
- größenveränderbare Fenster
- Menüleiste
- benutzerdefinierte Widgets
- Zugriff auf native Widgets
- Drag-and-Drop
- Internationalisierung
-
Unterstützte UI-Controls
- Spot stellt standardmäßig verschiedene UI-Controls wie Buttons, Labels, Texteingaben, Slider und Dropdowns bereit
- Die Kennzeichnung des Unterstützungsstatus ist in ❓ nicht implementiert, 🚧 in Arbeit, ⚠️ teilweise implementiert und ✅ abgeschlossen unterteilt
- Die wichtigsten vollständig unterstützten Controls sind folgende
- Button: Button zum Ausführen einfacher Aktionen, verwendet
Fl_ButtonundNSButton - Checkbox: Control zur Auswahl einer von zwei Optionen, verwendet
Fl_Check_ButtonundNSButton - Dropdown: Dropdown zur Auswahl eines Eintrags aus mehreren Optionen, verwendet
Fl_ChoiceundNSComboBox - Image: Control zur Anzeige von Bitmap-Bildern, verwendet
Fl_Boxund ein benutzerdefiniertesNSButton - Label: nicht editierbares Textlabel, verwendet
Fl_BoxundNSTextField - ListBox: Listen-Control für Einzel- oder Mehrfachauswahl, verwendet
Fl_Select_Browser/Fl_Multi_BrowserundNSTableView - ProgressBar: Anzeige des Fortschritts länger laufender Aufgaben, verwendet
Fl_ProgressundNSProgressIndicator - Slider: horizontale Slider-Eingabe, verwendet
Fl_SliderundNSSlider - Spinner: numerische Eingabe mit Auf-/Ab-Buttons, verwendet
Fl_SpinnerundNSTextField+NSStepper - TextField: einzeilige Texteingabe, verwendet
Fl_InputundNSTextField - TextEditor: mehrzeilige Textbearbeitung, verwendet
Fl_Text_EditorundNSTextView - Window: Control für das oberste Fenster, verwendet
Fl_WindowundNSWindow
- Button: Button zum Ausführen einfacher Aktionen, verwendet
- Es gibt auch teilweise implementierte oder noch nicht implementierte Controls
- Dial: kreisförmiges Status-Control mit ⚠️ teilweise implementiertem Status
- ComboBox: Dropdown-Menü mit kombinierter Texteingabe, wurde noch nicht begonnen
- Als potenzieller künftiger Backend-Kandidat wird die native Windows-Control-Bibliothek
https://github.com/rodrigocfd/windigogenannt
1 Kommentare
Meinungen auf Hacker News
Das muss ich mir unbedingt ansehen. Ich war auf der Suche nach einer einfachen Möglichkeit, interne Entwicklungstools in Go zu bauen; im Grunde geht es um Formulare mit Buttons und Textfeldern.
Gio habe ich auch ausprobiert, fand es aber schwer zu verstehen; aktuell nutze ich wails, was mir deutlich besser gefällt. Dieses Projekt wirkt ebenfalls interessant und einen Blick wert.
Ich würde ernsthaft empfehlen, die Richtung „Cross-Plattform: Durch die Nutzung von FLTK[1] und Cocoa[2] läuft Spot auf Mac, Linux und BSD; native Windows-Unterstützung ist künftig geplant“ deutlich zu reduzieren.
Behaltet die Lernerfahrungen bei, um später flexibel zu bleiben, aber konzentriert euch zuerst darauf, in einem Toolkit richtig gut zu sein. GUI-Toolkits, GUI-Bindings und GUIs selbst sind ohnehin anfällig dafür, in Details zu ertrinken; wenn man sich freiwillig auch noch die Details mehrerer zugrunde liegender Toolkits auflädt, ist die Wahrscheinlichkeit groß, dass man am Ende keines davon wirklich gut unterstützt.
Es gibt den Spruch: „Die ersten 90 % machen 90 % der Arbeit aus, und die verbleibenden 10 % noch einmal 90 %.“ Bei GUIs wirkt selbst das noch optimistisch. Die ersten 10 % sind 90 % der Arbeit, die nächsten 10 % das Zehnfache davon, und die nächsten 10 % wieder das Zehnfache. Cross-Plattform kann einem schnell die Luft abschnüren.
Ich erwarte nicht, dass ihr dem sofort zustimmt, aber wenn ihr später auf Situationen stoßt, in denen drei Toolkits Dinge wie Rich-Text-Verarbeitung auf drei einander widersprechende Arten erzwingen, dann erlaubt euch selbst, alle bis auf das am besten unterstützte oder beliebteste zugrunde liegende Toolkit fallenzulassen.
Nach so etwas habe ich in Go schon eine Weile gesucht. Weil Go einen einfachen Build-Prozess hat, sehe ich hier eine große Chance für eine hervorragende Developer Experience bei Cross-Plattform-UIs.
Meiner Erfahrung nach besteht die Hälfte des Schmerzes bei Cross-Plattform-Entwicklung aus dem Management von Build-Komplexität, und Go nimmt einem das fast vollständig ab.
Allerdings frage ich mich, wie ihr Cross-Plattform-Layouts lösen wollt, da die Standardgrößen nativer Controls je nach Plattform unterschiedlich sind. Ich habe das auch bei anderen Cross-Plattform-Toolkits noch nicht wirklich gut gelöst gesehen. Trotzdem viel Erfolg.
Vor ein paar Jahren habe ich nach genau so etwas gesucht. Allerdings brauchte ich auch Windows-Unterstützung. Am Ende bin ich auf C++ umgestiegen, um wxWidgets zu verwenden, und bekam damit kleine, in sich geschlossene Binaries.
Für ein natives Toolkit fand ich es beeindruckend, dass FLTK wie ein Browser mit Strg-+ und Strg+- Zoom für die gesamte App unterstützt. Und https://github.com/fltk-rs/fltk-theme?tab=readme-ov-file#wid... hat meinen Eindruck verbessert, wie „nativ“ man FLTK aussehen lassen kann.
In dem Zusammenhang habe ich kürzlich auch GoVCL https://z-kit.cc/en/ entdeckt und möchte es einmal ausprobieren.
Ein in sich geschlossenes Spot-„Hello World“ ist auf meinem Mac 2,3 MiB groß. Es ist nicht hübsch, aber für mich funktioniert es gut genug.
Ich frage mich, welchen Vorteil der Ansatz mit einem virtuellen Control-Tree gegenüber dem direkten Aktualisieren der dem Nutzer angezeigten Controls hat.
Man muss überall Callback-Code schreiben, und jeder Callback muss möglicherweise vorsichtig den aktuellen Zustand aller anderen Aktivitäten prüfen und dann Dutzende Widgets aktualisieren.
Beim reaktiven Ansatz schreibt man eine einzelne Render-Funktion, die das Interface für einen gegebenen Zustand beschreibt; das Framework kümmert sich darum, wann sie aufgerufen wird und welche Eingaben sie bekommt. Das ist deutlich leichter zu verstehen, und nachdem man React verwendet hat, ist es schwer, zurückzugehen – deshalb habe ich experimentiert, ob etwas Ähnliches auch in Go möglich ist.
Sieht gut aus. Ich frage mich, ob ihr im README die unterstützten Plattformen aufführen könntet.
Informationen wie Windows, Linux, macOS, *BSD, Android, iOS, Web, Tizen wären ziemlich interessant.
Man könnte es auch ähnlich wie in der Flutter-Dokumentation machen: https://docs.flutter.dev/reference/supported-platforms
Der Aufwand ist lobenswert, aber Cross-Plattform ohne Windows-Unterstützung?
Wenn ich das doch vor drei Wochen gekannt hätte – oder wenn es laut Commit-Historie damals schon existiert hätte. Ich sage schon lange, dass ein nach Go portiertes React oder ein React-ähnliches Framework für Go eine großartige Developer Experience bieten würde, und das hier sieht genau passend aus.
Ich mochte React.js ziemlich wenig, bis ich durch React.lua verstanden habe, worum es geht.
Am Ende habe ich einen Weg gefunden, mit dem Go-Standardpaket
html/templateetwas Ähnliches zu machen, und die Umsetzung hier beschrieben: https://www.sheshbabu.com/posts/react-like-composition-using...Das große Problem ist: Wenn man etwas für den Desktop veröffentlicht, will man in der Regel auch eine Web-Version. Besonders dann, wenn es keine sehr nischige App ist, die stark mit dem Betriebssystem interagiert.
Oder man braucht etwas, das mehrere Plattformen einschließlich Mobile abdeckt.
Ich habe ziemlich lange gesucht; am nächsten kamen Qt und React Native, aber beide waren aus verschiedenen Gründen schmerzhafte Optionen.
FLTK unterstützt Windows. Ich frage mich, ob Windows noch nicht unterstützt wird, weil ein anderer Lösungsansatz geplant ist.
Mein Ziel ist allerdings, wenn auch mit niedriger Priorität, ein Win32-basiertes Backend zu implementieren; der erste Schritt ist erledigt: https://github.com/roblillack/spot/pull/4