Spot - toolkit de GUI para desktop em Go, semelhante ao React
(github.com/roblillack)- 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, enquantospot/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 comspot.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
- Implementação baseada em FLTK: usa go-fltk
- Implementação baseada em Cocoa: usa uma versão modificada de gocoa
- 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 interfacespot.Controlspot/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.RenderContextcomo primeiro argumento e chamarspot.UseState,spot.UseEffectetc. para integrá-la ao ciclo de vida do Spot - Por convenção, o nome da função usa o prefixo
Use…
- Basta criar uma função que receba
- 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
- Essa interface tem um único método:
- 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.Componente gerencie os widgets nativos
- Basta criar uma struct que implemente a interface
- No momento,
spot/uinã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 chamandospot.Makecom 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.Buildou uma função de renderização paraspot.BuildFn
- Pode ser feito passando uma instância de componente para
- Mount: processo de criar os controles reais de UI a partir da árvore virtual de controles
- É possível chamar
Mountno nó da árvore ou usarspot.Mountespot.MountFn
- É possível chamar
- Update: processo de atualizar a árvore de controles já montada
- Isso é feito chamando
Updateno nó da árvore
- Isso é feito chamando
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_ButtoneNSButton - Checkbox: controle para escolher uma entre duas opções, usa
Fl_Check_ButtoneNSButton - Dropdown: menu suspenso para escolher um item entre vários, usa
Fl_ChoiceeNSComboBox - Image: controle para exibir imagens bitmap, usa
Fl_BoxeNSButtoncustomizado - Label: label de texto não editável, usa
Fl_BoxeNSTextField - ListBox: controle de lista com seleção única ou múltipla, usa
Fl_Select_Browser/Fl_Multi_BrowsereNSTableView - ProgressBar: exibe o progresso de tarefas longas, usa
Fl_ProgresseNSProgressIndicator - Slider: entrada por slider horizontal, usa
Fl_SlidereNSSlider - Spinner: entrada numérica com botões de aumentar/diminuir, usa
Fl_SpinnereNSTextField+NSStepper - TextField: campo de texto de uma linha, usa
Fl_InputeNSTextField - TextEditor: edição de texto em várias linhas, usa
Fl_Text_EditoreNSTextView - Window: controle de janela de nível superior, usa
Fl_WindoweNSWindow
- Button: botão para executar uma ação simples, usa
- 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
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.
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.
Um “Hello World” do Spot autocontido fica com 2.3MiB no meu Mac. Não é bonito, mas funciona o suficiente para mim.
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.
É 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?
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.
Acabei encontrando uma maneira de fazer algo parecido usando apenas o
html/templatepadrão do Go, e escrevi a implementação aqui: https://www.sheshbabu.com/posts/react-like-composition-using...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.
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