4 pontos por GN⁺ 2024-05-26 | 1 comentários | Compartilhar no WhatsApp
  • Spot é um toolkit reativo simples para GUI desktop multiplataforma em Go, para criar interfaces usando widgets nativos sempre que possível e oferecendo APIs consistentes entre plataformas
  • Ele funciona reconstruindo a árvore imutável de componentes quando o estado da aplicação muda e comparando com o estado anterior para decidir quais controles de UI devem ser atualizados
  • Atualmente, os backends são uma implementação baseada em Cocoa no macOS e em FLTK nas demais plataformas, com uso opcional de FLTK também no macOS
  • spot é o pacote principal, independente de backend, que fornece o modelo reativo e a renderização, enquanto spot/ui é um conjunto de controles de GUI multiplataforma prontos para uso
  • Layout automático, múltiplas janelas, caixas de diálogo modais, janelas redimensionáveis, barra de menu, widgets personalizados, acesso a widgets nativos, drag and drop e internacionalização ainda não são oferecidos

Objetivo do Spot e modelo básico

  • Spot é um toolkit de GUI reativo para Go, projetado para oferecer uma API consistente em várias plataformas enquanto usa widgets nativos sempre que possível
  • Ele pode ser adicionado ao projeto como uma dependência simples e, escrevendo apenas código Go, permite criar binários nativos de GUI autônomos, sem ferramentas extras nem geração de código
  • O exemplo cria uma janela e um botão com o fluxo ui.Init(), spot.MountFn(...), ui.Run(), e gerencia o estado da contagem de cliques com spot.UseState[int](<https://github.com/roblillack/ctx, 0>)
  • O handler de clique do botão chama setCounter(counter + 1) e, quando o estado muda, o título do botão passa para o formato "Clicked %d times!"

Como funcionam as atualizações reativas

  • Em Spot, reactive significa que a UI é atualizada automaticamente quando o estado da aplicação muda
  • Quando o estado muda, a árvore imutável de componentes é reconstruída e comparada rapidamente com o estado anterior para decidir quais controles de UI devem ser atualizados
  • Na web, essa ideia costuma ser chamada de virtual DOM, e o Spot começou como um experimento para trazer esse conceito ao ambiente desktop em Go e implementar uma biblioteca de GUI parecida com React
  • Em vez de atualizar a UI manualmente, o desenvolvedor gerencia a lógica e o estado da aplicação com funções de renderização sem efeitos colaterais e hooks como UseState

Backends e organização dos pacotes

  • Spot seleciona automaticamente, em tempo de compilação, o backend adequado para a plataforma de execução
  • Atualmente, há dois backends disponíveis
  • No macOS, o backend usado é o Cocoa; nas demais plataformas, usa-se o backend baseado em FLTK
  • Também é possível usar FLTK opcionalmente no macOS, e melhorias no suporte ao Windows continuam como plano futuro
  • spot é o pacote principal que fornece o modelo reativo e a renderização, e pode ser usado com qualquer conjunto de controles que implemente a interface spot.Control
  • spot/ui é o pacote de controles de GUI multiplataforma prontos para uso com Spot

Componentes, controles e hooks

  • Assim como no React, é possível criar hooks personalizados
    • Basta criar uma função que receba *spot.RenderContext como primeiro argumento e chamar spot.UseState, spot.UseEffect etc. para integrá-la ao ciclo de vida do Spot
    • Por convenção, o nome da função usa o prefixo Use…
  • Componentes personalizados podem ser criados como structs que implementam a interface spot.Component
    • Essa interface tem um único método: Render(ctx *spot.RenderContext) spot.Component
    • Componentes criados dessa forma podem ser usados do mesmo jeito que os componentes embutidos
  • Em Spot, component é a unidade lógica que contém a lógica de negócio e o estado
    • Componentes são compostos por outros componentes e, no fim, renderizados em um ou mais controles
  • control é um tipo especial de componente montado na árvore de UI e representa um elemento visual na tela
    • Em geral, ele se baseia em implementações nativas do backend de GUI, como botão, label e campo de texto
  • Também é possível usar uma biblioteca de widgets completamente diferente da fornecida
    • Basta criar uma struct que implemente a interface spot.Component e gerencie os widgets nativos
  • No momento, spot/ui não é compatível com backends além de Cocoa ou FLTK

Terminologia do ciclo de vida de renderização

  • Make: processo de criar uma instância de struct que implementa a interface spot.Component, ou criar uma nova instância de componente chamando spot.Make com uma função de renderização
  • Render: processo de aplicar o estado do componente aos seus elementos e retornar outra instância de componente
  • Build: processo de renderizar componentes recursivamente para construir a árvore de controles
    • Pode ser feito passando uma instância de componente para spot.Build ou uma função de renderização para spot.BuildFn
  • Mount: processo de criar os controles reais de UI a partir da árvore virtual de controles
    • É possível chamar Mount no nó da árvore ou usar spot.Mount e spot.MountFn
  • Update: processo de atualizar a árvore de controles já montada
    • Isso é feito chamando Update no nó da árvore

Recursos ainda não oferecidos

  • Atualmente, o Spot não oferece os seguintes recursos
    • Layout automático

      • Múltiplas janelas
      • Caixas de diálogo modais
      • Janelas redimensionáveis
      • Barra de menu
      • Widgets personalizados
      • Acesso a widgets nativos
      • Drag and drop
      • Internacionalização

Controles de UI compatíveis

  • Spot oferece por padrão vários controles de UI, como botão, label, campo de texto, slider e dropdown
  • A tabela de status usa ❓ para não implementado, 🚧 para em desenvolvimento, ⚠️ para parcialmente implementado e ✅ para concluído
  • Os principais controles concluídos são os seguintes
    • Button: botão para executar uma ação simples, usa Fl_Button e NSButton
    • Checkbox: controle para escolher uma entre duas opções, usa Fl_Check_Button e NSButton
    • Dropdown: menu suspenso para escolher um item entre vários, usa Fl_Choice e NSComboBox
    • Image: controle para exibir imagens bitmap, usa Fl_Box e NSButton customizado
    • Label: label de texto não editável, usa Fl_Box e NSTextField
    • ListBox: controle de lista com seleção única ou múltipla, usa Fl_Select_Browser/Fl_Multi_Browser e NSTableView
    • ProgressBar: exibe o progresso de tarefas longas, usa Fl_Progress e NSProgressIndicator
    • Slider: entrada por slider horizontal, usa Fl_Slider e NSSlider
    • Spinner: entrada numérica com botões de aumentar/diminuir, usa Fl_Spinner e NSTextField+NSStepper
    • TextField: campo de texto de uma linha, usa Fl_Input e NSTextField
    • TextEditor: edição de texto em várias linhas, usa Fl_Text_Editor e NSTextView
    • Window: controle de janela de nível superior, usa Fl_Window e NSWindow
  • Também há controles parcialmente implementados ou ainda não implementados
    • Dial: controle circular de estado, em status ⚠️ parcialmente implementado
    • ComboBox: menu suspenso com entrada de texto combinada, ainda não foi iniciado
  • Como possível backend futuro, é citada a biblioteca de controles nativos para Windows https://github.com/rodrigocfd/windigo

1 comentários

 
GN⁺ 2024-05-26
Opiniões no Hacker News
  • Esse eu preciso ver. Eu estava procurando uma forma simples de criar ferramentas internas de desenvolvimento em Go; na prática, é algo no nível de formulários com botões e campos de texto.
    Também experimentei o Gio, mas achei difícil de entender; agora estou usando wails e gosto muito mais. Este projeto também parece interessante e vale uma olhada.

  • Eu recomendaria seriamente reduzir essa direção de “multiplataforma: usando FLTK[1] e Cocoa[2], o Spot funciona em Mac, Linux e BSD, com planos para suporte nativo a Windows no futuro”.
    Mesmo mantendo o que aprenderam para ter flexibilidade depois, é melhor primeiro fazer bem em um único toolkit. Toolkits de GUI, bindings de GUI e a própria GUI já são coisas em que é fácil morrer afogado nos detalhes; se você ainda assumir voluntariamente os detalhes de vários toolkits de base, é bem provável que acabe não fazendo nenhum deles direito.
    Existe a frase “os primeiros 90% do trabalho levam 90% do tempo, e os 10% restantes levam outros 90%”, mas em GUI até isso parece otimista. Os primeiros 10% são 90% do trabalho; os próximos 10% são dez vezes isso, e os 10% seguintes mais dez vezes. Tentar ser multiplataforma pode apertar o laço no pescoço.
    Não espero que concordem com isso agora, mas, quando no futuro se depararem com três toolkits forçando três maneiras contraditórias de lidar com algo como rich text, espero que se permitam abandonar os outros e ficar só com o toolkit de base mais bem suportado ou mais popular.

  • Eu vinha procurando algo assim em Go havia um tempo. Como Go tem um processo de build simples, acho que há uma grande oportunidade de oferecer uma ótima experiência de desenvolvimento em UI multiplataforma.
    Pela minha experiência, metade da dor do desenvolvimento multiplataforma é gerenciar a complexidade do build, e Go praticamente elimina isso.
    Dito isso, os tamanhos padrão dos controles nativos variam entre plataformas, então fico curioso para saber como vão resolver o layout multiplataforma. Não vi essa parte muito bem resolvida em outros toolkits multiplataforma. Ainda assim, boa sorte.

  • Eu estava procurando algo assim alguns anos atrás. Mas também precisava de suporte a Windows. Acabei migrando para C++ para usar wxWidgets e consegui um binário pequeno e autocontido.

    • go-fltk também compila e roda no Windows e, na prática, funciona bem até.
      Fiquei impressionado ao ver que, mesmo sendo um toolkit nativo, o FLTK oferece zoom in/out no aplicativo inteiro com Ctrl-+ e Ctrl+-, como um navegador. E, graças a https://github.com/fltk-rs/fltk-theme?tab=readme-ov-file#wid..., minha impressão melhorou sobre o quanto é possível fazer o FLTK parecer “nativo”.
      Relacionado a isso, descobri recentemente o GoVCL https://z-kit.cc/en/ e quero experimentá-lo.
    • Gosto muito do WxWindows, mas hoje estou profundamente metido em Go.
      Um “Hello World” do Spot autocontido fica com 2.3MiB no meu Mac. Não é bonito, mas funciona o suficiente para mim.
    • Existe o wxGo, mas infelizmente o projeto não é mantido.
  • Fico curioso sobre qual é a vantagem da abordagem de árvore virtual de controles em comparação com atualizar diretamente os controles exibidos ao usuário.

    • Em situações complexas, por exemplo quando o usuário interage com a UI enquanto uma tarefa demorada em background também altera o estado da UI, o gerenciamento de estado rapidamente se torna impossível de controlar.
      É preciso escrever código de callback por toda parte, e cada callback talvez tenha que verificar cuidadosamente o estado atual de todas as outras atividades antes de atualizar dezenas de widgets.
      Em uma abordagem reativa, você escreve uma única função de renderização que descreve a interface para um determinado estado, e o framework cuida de quando chamá-la e quais entradas fornecer. Isso é muito mais fácil de entender e, depois de usar React, fica difícil voltar atrás; por isso acabei experimentando se algo parecido seria possível em Go.
  • Parece bom. Fico me perguntando se poderiam colocar as plataformas suportadas no README.
    Informações como Windows, Linux, macOS, *BSD, Android, iOS, Web e Tizen seriam bem interessantes.
    Poderia ser algo no estilo da documentação do Flutter: https://docs.flutter.dev/reference/supported-platforms

  • O esforço é digno de aplausos, mas multiplataforma sem suporte a Windows?

    • Parece que no Windows usam FLTK; só que ainda não há suporte nativo.
    • É só usar WSL.
  • Eu queria ter conhecido isso três semanas atrás — ou que já existisse na época, considerando o histórico de commits. Há muito tempo venho dizendo que um React portado para Go ou um framework semelhante ao React para Go proporcionaria uma experiência de desenvolvimento incrível, e isto parece se encaixar exatamente.
    Eu detestava bastante React.js até entender a ideia com React.lua.

    • Tive o mesmo problema. Eu gostava muito da forma de composição de componentes que fazia em React, e era difícil voltar atrás.
      Acabei encontrando uma maneira de fazer algo parecido usando apenas o html/template padrão do Go, e escrevi a implementação aqui: https://www.sheshbabu.com/posts/react-like-composition-using...
    • Fico curioso sobre o que você usou em vez disso três semanas atrás.
  • O grande problema é que, quando se lança algo para desktop, normalmente também se quer uma versão web. Especialmente se não for um app muito de nicho que interage bastante com o sistema operacional.
    Ou então é preciso algo que mire várias plataformas, incluindo mobile.
    Procurei por bastante tempo, mas o mais próximo que encontrei foi Qt e React Native, e ambos eram opções dolorosas por vários motivos.

  • FLTK oferece suporte a Windows. Fico curioso se ainda não suportam Windows porque planejam usar outra solução.

    • Se o ambiente estiver preparado com um compilador C adequado etc., o Spot deve funcionar sem alterações no Windows e selecionar o backend FLTK.
      No entanto, meu objetivo — embora de baixa prioridade — é implementar um backend baseado em Win32, e o primeiro passo já foi concluído: https://github.com/roblillack/spot/pull/4