4 points par GN⁺ 2024-05-26 | 1 commentaires | Partager sur WhatsApp
  • Spot est une boîte à outils GUI desktop multiplateforme simple et réactive pour créer des interfaces en Go, qui utilise des widgets natifs quand c’est possible et fournit des API cohérentes selon les plateformes
  • Quand l’état de l’application change, il reconstruit un arbre immuable de composants puis le compare à l’état précédent pour déterminer quels contrôles UI doivent être mis à jour
  • Les backends actuels sont une implémentation basée sur Cocoa pour macOS et sur FLTK pour les autres plateformes, avec la possibilité d’utiliser FLTK aussi sur macOS
  • spot est le package cœur indépendant du backend qui fournit le modèle réactif et le rendu, et spot/ui est un ensemble de contrôles GUI multiplateformes prêts à l’emploi
  • La mise en page automatique, les fenêtres multiples, les boîtes de dialogue modales, les fenêtres redimensionnables, la barre de menus, les widgets personnalisés, l’accès aux widgets natifs, le glisser-déposer et l’internationalisation ne sont pas encore pris en charge

Objectif de Spot et modèle de base

  • Spot est une boîte à outils GUI réactive pour Go, conçue pour fournir une API cohérente sur plusieurs plateformes tout en utilisant des widgets natifs là où c’est possible
  • Il peut être ajouté au projet comme une simple dépendance, et en n’écrivant que du code Go, on peut produire des binaires GUI natifs autonomes sans outils supplémentaires ni génération de code
  • L’exemple suit le flux ui.Init(), spot.MountFn(...), ui.Run() pour créer une fenêtre et un bouton, et gère l’état du nombre de clics avec spot.UseState[int](<https://github.com/roblillack/ctx, 0>)
  • Le gestionnaire de clic du bouton appelle setCounter(counter + 1) et, quand l’état change, le titre du bouton devient au format "Clicked %d times!"

Fonctionnement des mises à jour réactives

  • Dans Spot, reactive signifie que l’interface se met à jour automatiquement quand l’état de l’application change
  • Lors d’un changement d’état, un arbre immuable de composants est reconstruit puis comparé rapidement à l’état précédent pour déterminer quels contrôles UI doivent être mis à jour
  • Sur le web, cette idée est souvent appelée virtual DOM, et Spot est né comme une expérimentation visant à transposer ce concept à l’environnement desktop Go pour créer une bibliothèque GUI proche de React
  • Au lieu de mettre l’UI à jour manuellement, le développeur gère la logique applicative et l’état avec des fonctions de rendu sans effet de bord et des hooks comme UseState

Backends et organisation des packages

  • Spot choisit automatiquement au moment de la compilation le backend adapté à la plateforme d’exécution
  • Deux backends sont actuellement fournis
  • Sur macOS, Spot utilise le backend Cocoa, et sur les autres plateformes le backend basé sur FLTK
  • Il est aussi possible d’utiliser FLTK sur macOS, et l’amélioration du support Windows reste prévue pour plus tard
  • spot est le package cœur qui fournit le modèle réactif et les fonctions de rendu, et peut être utilisé avec n’importe quel ensemble de contrôles implémentant l’interface spot.Control
  • spot/ui est un package de contrôles GUI multiplateformes prêts à l’emploi utilisables avec Spot

Composants, contrôles et hooks

  • Comme avec React, il est possible de créer des hooks personnalisés
    • Il suffit d’écrire une fonction prenant *spot.RenderContext en premier argument et d’y appeler spot.UseState, spot.UseEffect, etc. pour l’intégrer au cycle de vie de Spot
    • Par convention, le nom de la fonction commence par le préfixe Use…
  • Les composants personnalisés peuvent être créés sous forme de structures implémentant l’interface spot.Component
    • Cette interface ne contient qu’une seule méthode : Render(ctx *spot.RenderContext) spot.Component
    • Les composants ainsi créés peuvent être utilisés de la même manière que les composants intégrés
  • Dans Spot, un component est une unité logique qui contient la logique métier et l’état
    • Un composant est composé d’autres composants et finit par être rendu en un ou plusieurs contrôles
  • Un control est un composant spécial monté dans l’arbre UI, qui représente un élément visuel à l’écran
    • Il repose généralement sur une implémentation native du backend GUI, comme pour les boutons, labels ou champs texte
  • Il est aussi possible d’utiliser une bibliothèque de widgets complètement différente de celle fournie
    • Il suffit de créer une structure qui implémente l’interface spot.Component et gère les widgets natifs
  • L’utilisation de spot/ui avec un backend autre que Cocoa ou FLTK n’est actuellement pas prise en charge

Terminologie du cycle de vie du rendu

  • Make : processus consistant à créer une instance de structure implémentant l’interface spot.Component, ou à créer une nouvelle instance de composant en appelant spot.Make avec une fonction de rendu
  • Render : processus qui applique l’état du composant à ses éléments constitutifs et renvoie d’autres instances de composants
  • Build : processus qui rend récursivement les composants pour construire l’arbre de contrôles
    • On peut passer une instance de composant à spot.Build, ou une fonction de rendu à spot.BuildFn pour l’exécuter
  • Mount : processus qui crée les contrôles UI réels à partir de l’arbre de contrôles virtuel
    • On peut appeler Mount sur un nœud de l’arbre, ou utiliser spot.Mount et spot.MountFn
  • Update : processus de mise à jour de l’arbre de contrôles monté
    • Il s’effectue en appelant Update sur un nœud de l’arbre

Fonctionnalités actuellement absentes

  • Spot ne fournit actuellement pas les fonctionnalités suivantes
    • Mise en page automatique

      • Fenêtres multiples
      • Boîtes de dialogue modales
      • Fenêtres redimensionnables
      • Barre de menus
      • Widgets personnalisés
      • Accès aux widgets natifs
      • Glisser-déposer
      • Internationalisation

Contrôles UI pris en charge

  • Spot fournit par défaut plusieurs contrôles UI comme des boutons, labels, champs texte, sliders et menus déroulants
  • Les statuts de prise en charge sont indiqués ainsi : ❓ non implémenté, 🚧 en cours, ⚠️ partiellement implémenté, ✅ terminé
  • Les principaux contrôles terminés sont les suivants
    • Button : bouton d’action simple, utilise Fl_Button et NSButton
    • Checkbox : contrôle permettant de choisir entre deux options, utilise Fl_Check_Button et NSButton
    • Dropdown : menu déroulant pour choisir un élément parmi plusieurs, utilise Fl_Choice et NSComboBox
    • Image : contrôle d’affichage d’image bitmap, utilise Fl_Box et un NSButton personnalisé
    • Label : label texte non éditable, utilise Fl_Box et NSTextField
    • ListBox : contrôle de liste à sélection simple ou multiple, utilise Fl_Select_Browser/Fl_Multi_Browser et NSTableView
    • ProgressBar : affiche la progression d’un travail de longue durée, utilise Fl_Progress et NSProgressIndicator
    • Slider : entrée par curseur horizontal, utilise Fl_Slider et NSSlider
    • Spinner : saisie numérique avec boutons haut/bas, utilise Fl_Spinner et NSTextField+NSStepper
    • TextField : champ de saisie texte sur une ligne, utilise Fl_Input et NSTextField
    • TextEditor : édition de texte multiligne, utilise Fl_Text_Editor et NSTextView
    • Window : contrôle de fenêtre de niveau supérieur, utilise Fl_Window et NSWindow
  • Certains contrôles sont partiellement implémentés ou non implémentés
    • Dial : contrôle circulaire d’état, actuellement en état ⚠️ partiellement implémenté
    • ComboBox : menu déroulant combiné à une saisie texte, pas encore commencé
  • Comme backend natif Windows potentiel à l’avenir, la bibliothèque de contrôles https://github.com/rodrigocfd/windigo est mentionnée

1 commentaires

 
GN⁺ 2024-05-26
Avis sur Hacker News
  • Il faut absolument que je regarde ça. Je cherchais un moyen simple de créer des outils de développement internes en Go, en gros des formulaires avec des boutons et des champs texte.
    J’ai aussi essayé Gio, mais je l’ai trouvé difficile à comprendre ; pour l’instant j’utilise wails, qui me plaît beaucoup plus. Ce projet a l’air intéressant et mérite qu’on s’y penche.

  • Je conseille vivement de réduire sérieusement l’ambition derrière « multiplateforme : en s’appuyant sur FLTK[1] et Cocoa[2], Spot fonctionne sur Mac, Linux et BSD, avec une prise en charge native de Windows prévue à l’avenir ».
    Gardez les enseignements pour préserver de la flexibilité plus tard, mais il vaut mieux commencer par être bon sur un seul toolkit. Les toolkits GUI, les bindings GUI et les GUI elles-mêmes sont déjà des domaines où l’on peut facilement se noyer dans les détails ; si vous vous imposez en plus de prendre en charge les détails de plusieurs toolkits sous-jacents, vous risquez de finir par n’en maîtriser correctement aucun.
    On dit que « les premiers 90 % représentent 90 % du travail, et les 10 % restants représentent encore 90 % », mais pour les GUI, même ça semble optimiste. Les premiers 10 % représentent 90 % du travail, les 10 % suivants coûtent dix fois plus, et les 10 % d’après encore dix fois plus. Tenter le multiplateforme peut vous étrangler.
    Je ne m’attends pas à ce que vous soyez d’accord avec ça tout de suite, mais quand vous vous retrouverez plus tard face à trois toolkits imposant trois manières contradictoires de gérer, par exemple, le texte enrichi, j’espère que vous vous autoriserez à ne garder que le toolkit sous-jacent le mieux pris en charge ou le plus populaire, et à abandonner les autres.

  • Je cherchais quelque chose comme ça en Go depuis un moment. Comme Go a un processus de build simple, je pense qu’il a une vraie chance d’offrir une excellente expérience développeur pour les UI multiplateformes.
    D’après mon expérience, la moitié de la douleur du développement multiplateforme vient de la gestion de la complexité du build, et Go l’élimine presque entièrement.
    Cela dit, les tailles par défaut des contrôles natifs diffèrent d’une plateforme à l’autre, donc je me demande comment ils vont résoudre le layout multiplateforme. Je n’ai pas vu d’autre toolkit multiplateforme régler ça particulièrement bien. En tout cas, bonne chance.

  • Je cherchais quelque chose comme ça il y a quelques années. Mais j’avais aussi besoin du support de Windows. J’ai finalement basculé vers C++ pour utiliser wxWidgets, et j’ai pu obtenir un petit binaire autonome.

    • go-fltk se build et s’exécute aussi sous Windows, et fonctionne en fait assez bien.
      Pour un toolkit natif, j’ai été impressionné de voir que FLTK prend en charge le zoom global de l’application avec Ctrl-+ et Ctrl+-, comme un navigateur. Et grâce à https://github.com/fltk-rs/fltk-theme?tab=readme-ov-file#wid..., j’ai une meilleure impression de jusqu’où on peut rendre FLTK « natif » visuellement.
      Dans le même registre, j’ai récemment découvert GoVCL https://z-kit.cc/en/ et j’aimerais bien l’essayer.
    • J’aime beaucoup WxWindows, mais aujourd’hui je suis trop investi dans Go.
      Le « Hello World » Spot autonome fait 2.3MiB sur mon Mac. Ce n’est pas très joli, mais pour moi ça fonctionne suffisamment bien.
    • Il existe bien wxGo, mais malheureusement le projet n’est plus maintenu.
  • Je me demande quels sont les avantages de l’approche par arbre de contrôles virtuel par rapport à la mise à jour directe des contrôles affichés à l’utilisateur.

    • Dans des situations complexes, par exemple quand l’utilisateur interagit avec l’UI pendant qu’une tâche longue en arrière-plan modifie elle aussi l’état de l’UI, la gestion d’état devient vite ingérable.
      Il faut écrire du code de callback un peu partout, et chaque callback peut devoir examiner soigneusement l’état actuel de toutes les autres activités avant de mettre à jour des dizaines de widgets.
      Avec une approche réactive, on écrit une seule fonction de rendu qui décrit l’interface pour un état donné, et le framework se charge de savoir quand l’appeler et quelles entrées lui fournir. C’est beaucoup plus facile à comprendre et, après avoir utilisé React, il est difficile de revenir en arrière ; c’est ce qui m’a amené à expérimenter pour voir si quelque chose de similaire était possible en Go.
  • Ça a l’air bien. Je me demande si vous pourriez ajouter les plateformes prises en charge dans le README.
    Des informations comme Windows, Linux, macOS, *BSD, Android, iOS, Web ou Tizen seraient assez intéressantes.
    Vous pourriez le présenter comme dans la documentation de Flutter : https://docs.flutter.dev/reference/supported-platforms

  • L’effort est louable, mais du multiplateforme sans support Windows ?

    • Il semble utiliser FLTK sous Windows ; c’est juste que le support natif n’est pas encore là.
    • Il suffit d’utiliser WSL.
  • Si seulement j’avais découvert ça il y a trois semaines, ou si ça avait déjà existé à l’époque d’après l’historique des commits. Cela fait longtemps que je dis qu’un React porté en Go ou un framework de type React pour Go offrirait une expérience de développement formidable, et celui-ci semble correspondre exactement.
    Je détestais assez React.js jusqu’à ce que React.lua me fasse changer d’avis.

    • J’ai eu le même problème. J’aimais vraiment le mode de composition de composants de React, et il était difficile de revenir en arrière.
      J’ai fini par trouver une manière de faire quelque chose de similaire avec seulement html/template de la bibliothèque standard de Go, et j’ai décrit l’implémentation ici : https://www.sheshbabu.com/posts/react-like-composition-using...
    • Je me demande ce que tu as utilisé à la place il y a trois semaines.
  • Le gros problème, quand on sort quelque chose sur desktop, c’est qu’on finit généralement par vouloir aussi une version web. C’est particulièrement vrai si ce n’est pas une application très de niche qui interagit beaucoup avec le système d’exploitation.
    Ou bien il faut quelque chose qui cible plusieurs plateformes, y compris le mobile.
    J’ai cherché assez longtemps, mais ce qui s’en rapprochait le plus était Qt et React Native, et les deux étaient des options douloureuses pour plusieurs raisons.

  • FLTK prend en charge Windows. Je me demande si Windows n’est pas encore pris en charge parce qu’il est prévu d’utiliser une autre solution.

    • Si l’environnement adéquat est en place, notamment un compilateur C approprié, Spot devrait fonctionner sous Windows sans modification et sélectionner le backend FLTK.
      Cela dit, mon objectif, même s’il est peu prioritaire, est d’implémenter un backend basé sur Win32, et la première étape est terminée : https://github.com/roblillack/spot/pull/4