Escolha o popup certo
Um popup interrompe. Antes de escolher um, responda a uma única pergunta: o jogador precisa responder antes de o jogo continuar? Se sim, bloqueie. Se não, nunca bloqueie — um cartão que para o jogo por uma notícia que o jogador não pediu é o jeito mais rápido de fazê-lo fechar o seu jogo.
- Uma pergunta com resposta — comprar, apagar, sair, sobrescrever →
GoDialogs.confirm(). Algo que o jogador só precisa ver e confirmar →alert(). Uma entre poucas opções →choose(). - Uma tela própria — configurações, uma ficha de personagem, um item da loja →
GoSurfaceno meio da tela. - Uma lista longa ou uma página de gerenciamento — a mochila, o correio, os amigos →
GoSheetvindo de baixo no celular,GoDrawervindo do lado em uma tela larga. - Algo sobre o que acabou de ser tocado — os atributos de um item, as ações dele →
GoPopoverouGoContextMenuao lado. - Notícias com as quais o jogo pode seguir — salvo, descartado, convidado →
GoSnackbar,GoNoticeouGoPromptCard. Eles nunca tomam a tela. - Ensinar os controles →
GoCoachMark. Cheats enquanto você desenvolve →GoConsole.
Todos os popups de relance
| Popup | Para o jogo | Voltar / Escape | Um toque fora | Use para |
|---|---|---|---|---|
GoDialogs confirm · alert | Sim | Fecha — confirm responde false, alert retorna | Fecha só com dismiss_on_scrim | Perguntas que precisam de resposta |
GoDialogs.choose | Sim | Fecha — responde -1 | Fecha só com dismiss_on_scrim | Escolher uma entre poucas opções |
GoSurface | Sim | Pede para fechar a de cima | Pede para fechar com dismiss_on_scrim | Janelas suas |
GoSheet | Sim | Fecha a folha inteira | Fecha | A mochila, uma lista da loja, o correio |
GoDrawer | Sim | Só o Voltar do Android | Fecha | Uma lista lateral em uma tela larga |
GoPopover | Até fechar | Fecha | Fecha — e não vai adiante | Detalhes de itens e habilidades |
Lista do GoCombobox | Até fechar | Fecha | Fecha | Uma lista de escolha com busca |
GoContextMenu · lista suspensa · seletor | O menu da engine | O menu da engine | Fecha | Ações, listas curtas |
GoSnackbar | Não | — | Chega ao jogo | Salvo · Desfazer · Tentar de novo |
GoPromptCard | Não | — | Chega ao jogo | Um convite, um pedido de troca |
GoNotice | Nunca | — | Chega ao jogo | Uma linha fixa no HUD |
GoBanner | Não — fica dentro da página | — | — | "Você está offline. Tentar de novo" |
GoCoachMark | Só o cartão dele | Encerra o tour | Chega ao jogo | Um tour de primeira execução |
GoConsole | Só o painel dele | A tecla Escape o fecha | Chega ao jogo | Comandos de depuração |
Em qual camada cada um abre, e por que nessa ordem, está em Camadas, Voltar e entrada.

GoDialogs
GoSurface
GoSheet
GoDrawer
GoPopover
GoContextMenu
GoSnackbar
GoPromptCard
GoCoachMark
GoConsoleTodas as imagens desta página são o mesmo joguinho — vida e mana no canto superior esquerdo, moedas no superior direito, um joystick e três
slots rápidos embaixo — em default_dark, com um popup aberto por cima. O mesmo
diálogo nos catorze visuais →
Anatomia de um popup
Quase todo popup que bloqueia no gohud — diálogos, folhas, popovers, a lista do combobox e as suas próprias
janelas — é um mesmo nó, GoSurface. Aprenda as partes dele uma vez e você conhece todos.

| Parte | O que é | Como acessar |
|---|---|---|
| Camada | O CanvasLayer em que o popup vive. O número dela decide o que ele cobre. | Fica por sua conta em um GoSurface; já vem embutida nos demais (tabela) |
| Véu | O véu sobre tudo o que fica atrás. Ele pega os toques que não acertam o cartão. | scrim_transparent, dismiss_on_scrim, a cor scrim do tema |
| Cartão | A janela em si — a face de painel do tema, arredondada, cortada, forjada ou de gelatina, conforme o visual. | card, alpha |
| Cabeçalho | Um botão de voltar opcional, o título (duas linhas no máximo, depois "…") e o botão de fechar. | set_title() · set_title_key() · set_back() · close_enabled · show_header |
| Barra de ferramentas | Uma linha fixa abaixo do cabeçalho — um campo de busca, chips de filtro. Fica escondida até você usá-la. | toolbar |
| Corpo | A parte que rola. Coloque o conteúdo aqui. | body · scroll_body · clear() |
| Linha de status | Uma linha fixa entre o corpo e o rodapé — "Falha na verificação do nome", "Salvando…". | set_status_text() · set_status_key() · clear_status() |
| Rodapé | Botões fixos embaixo. Fica escondido até você usá-lo. | footer |
toolbar, a linha de status e footer; eles ficam no lugar enquanto o corpo rola.
Diálogos que você aguarda com await
GoDialogs pergunta e espera. Um nó, três perguntas — e a resposta volta na mesma
linha, então o código se lê na ordem em que o jogador vive a cena.
var dialogs := GoDialogs.new()
add_child(dialogs)
if await dialogs.confirm("Buy the starter pack?", "300 gems for a sword, a shield and 10 potions.",
"Buy", "Not now"):
buy_starter_pack()

confirm()
confirm(…, true) — destrutiva
alert()
choose()
ActionLayout.HORIZONTALAs três perguntas
confirm(title, body, ok_text := "", cancel_text := "", extra := "",
args := {}, destructive := false) -> bool
alert(title, body, ok_text := "", extra := "", args := {}) # use await para esperar
choose(title, options: Array, cancel_text := "", translate := false) -> int
confirm_key(…) · alert_key(…) # chaves de tradução
| Chamada | Retorna | Use para |
|---|---|---|
confirm() | true ou false | Sim ou não — comprar, apagar, sair, sobrescrever. |
alert() | nada — use await para esperar | Algo que o jogador precisa ver: uma recompensa, uma conexão perdida. |
choose() | o índice da opção, ou -1 | Uma entre poucas. Uma opção é uma String ou {text, icon, danger, subtitle}. |
confirm_key() · alert_key() | como acima | O mesmo com chaves de tradução — o texto acompanha uma troca de idioma enquanto o diálogo está aberto. |
Rótulos de botão vazios usam as palavras do próprio kit (confirm, cancel em GoConfig.text_keys).
extra é acrescentado ao corpo em uma nova linha. args preenche os {placeholders} do título
e do corpo: confirm("Buy {item}?", "It costs {price} gold.", "Buy", "", "", {"item": name, "price": 300}).
Toda saída tem uma resposta
| O jogador… | confirm | alert | choose |
|---|---|---|---|
| toca em OK / em uma opção | true | retorna | o índice da opção |
| toca em Cancelar | false | — | -1 |
| toca em ×, aperta Escape ou o Voltar do Android | false | retorna | -1 |
toca no véu (só com dismiss_on_scrim) | false | retorna | -1 |
o nó GoDialogs sai da árvore | false | retorna | -1 |
Então o código depois do await sempre roda — nenhuma resposta fica pendurada, nem quando a cena muda por baixo do diálogo.
Ações destrutivas
Passe destructive = true para algo que não pode ser desfeito. O botão OK vira o botão preenchido
Tone.DANGER_SOLID — branco sobre vermelho em todo visual, enquanto um rótulo de perigo apenas tonalizado teria de escurecer até
quase preto em um tema claro para continuar legível. O tom é redefinido a cada chamada, porque a janela é reaproveitada.
if await dialogs.confirm("Delete this save?", "Slot 2 — Aria, level 12. This cannot be undone.",
"Delete", "Keep it", "", {}, true):
delete_save(2)
Arranjo dos botões
action_layout | O que faz |
|---|---|
VERTICAL (padrão) | Botões empilhados, na largura toda. Seguro para qualquer idioma — um rótulo longo em alemão ou russo nunca fica espremido. |
HORIZONTAL | Uma linha, com metade da largura para cada um. |
AUTO | Uma linha só quando os dois rótulos cabem em metade do cartão em uma única linha; empilhados nos demais casos. Decidido de novo a cada troca de idioma ou redimensionamento. |
action_gap é o espaço entre os botões, body_gap o espaço acima deles — deixe-o maior que
o espaço entre os botões para que a pergunta e a escolha se leiam como duas partes. Para um diálogo só, chame
set_next_action_layout() antes de abri-lo. O único botão de um alert sempre ocupa a largura toda.
Um de cada vez
Um segundo confirm() enquanto outro está aberto devolve false na hora — um diálogo nunca se empilha sobre outro por
acidente. Ligue queue_when_busy e ele passa a esperar a vez dele; alert() sempre espera.
queue_limit (8) limita a fila e, quando ela estoura, o pedido mais antigo recebe false.
pending() conta os pedidos em espera e clear_pending() responde false a todos eles.
choose() nunca entra na fila: devolve -1 quando já há um diálogo aberto.
Celular e desktop
choose() abre como uma folha vinda de baixo no Android e no iOS — onde o polegar está — e como um cartão no
meio da tela no desktop. Uma confirmação é limitada a max_width (420dp) e centralizada. Defina layer_index,
max_width e os outros exports antes do add_child; eles são aplicados quando o nó entra na árvore.
var picked := await dialogs.choose("Sort the bag by", [
{"text": "Newest first", "icon": GoIconSet.CLOCK},
{"text": "Rarity", "icon": GoIconSet.STAR},
{"text": "Drop everything", "icon": GoIconSet.TRASH, "danger": true}])
if picked >= 0:
sort_bag(picked)
GoSurface — uma janela sua
Quando um popup é mais do que uma pergunta — configurações, uma ficha de personagem, um item para comprar —, construa-o sobre o
GoSurface. Área segura, teclado, linhas fixas, rolagem, Voltar, foco e redimensionamento já estão resolvidos.
var layer := CanvasLayer.new()
layer.layer = 50
add_child(layer)
var window := GoSurface.new()
window.set_title("Settings")
window.close_requested.connect(layer.queue_free)
window.body.add_child(GoStyle.toggle("Music", false))
window.body.add_child(GoStyle.slider(0.0, 1.0, 0.05))
var row := GoStyle.row()
row.add_child(GoStyle.button("Cancel", window.request_close))
row.add_child(GoStyle.button("Save", save_and_close, GoStyle.Tone.PRIMARY))
window.footer.add_child(row)
window.footer.visible = true
layer.add_child(window)
close_requested. Esconder ou liberar
a janela é tarefa da tela que é dona dela — só ela sabe se "fechar" quer dizer salvar, descartar ou perguntar antes.
Três posicionamentos

CENTER — confirmações, configurações
BOTTOM — listas, páginas de gerenciamento
ANCHOR — ao lado de um controle
GoDatePicker em uma janela CENTERANCHOR precisa de anchor_control. O cartão vai para a direita dele se anchor_min_width couber;
senão, para o lado mais largo; senão, para dentro da tela, e abre para cima quando há mais espaço acima. Ele acompanha uma
âncora em movimento a cada frame. anchor_width (320) e anchor_max_height (520) definem o tamanho.
Até que tamanho ele chega
| Medida | Regra | Como mudar |
|---|---|---|
| Espaço | A área segura, menos o teclado, menos a margem da tela (16dp, nunca mais de 10% do lado menor). | — |
| Largura | 94% do espaço em retrato, 72% em paisagem, no máximo 480dp. | max_width · GoConfig.surface_width_ratio_* |
| Altura | Tão alto quanto o conteúdo (fit_content), até 68% do espaço, com teto de 72%. Um cartão centralizado que não é redimensionável pode crescer até 94% para evitar rolagem. | height_ratio · max_height_ratio · max_height |
| Densidade | Compacto quando compact está ligado, quando a tela tem menos de 420dp de altura (um celular deitado) ou quando o conteúdo não cabe: menos respiro, espaços menores, um título menor. Ele mede as duas versões e fica com a mais folgada que couber — sem tremedeira. | compact |
| Redimensionar | Com resizable, o título vira uma alça: arraste-o entre 30% e o teto. height_changed(ratio) informa o resultado. | resizable |
Uma proporção acima do teto é cortada, com um único aviso numa build de debug. O teclado empurra o cartão para cima e rola o campo em foco até ele aparecer; a área segura o mantém longe do notch e da barra de gestos no Android e no iOS.
Propriedades
| Propriedade | Padrão | O que faz |
|---|---|---|
placement | CENTER | CENTER, BOTTOM ou ANCHOR |
dismiss_on_scrim | GoConfig.dismiss_on_scrim (false) | Um toque no véu pede para fechar. Um arraste que começou dentro do cartão, não. |
scrim_transparent | false | Mantém o jogo visível — o véu continua pegando os toques de fora. |
close_enabled | true | Mostra o × e deixa Escape e Voltar pedirem para fechar. |
show_header · scroll_body · resizable | true · true · false | A linha do cabeçalho, o corpo rolável, a alça de arraste. |
fit_content · compact | true · false | Ajustar o tamanho ao conteúdo; começar compacto. |
fade_in | GoConfig.surface_fade_in (false) | O cartão aparece com fade ao abrir. Instantâneo com reduce_motion. |
alpha | -1 (o do tema) | A opacidade da face do cartão, como proporção. Só a face esmaece — nunca o texto. |
initial_focus | nenhum | O controle que recebe o foco ao abrir. |
add_child: scrim_transparent, show_header, resizable e
scroll_body são aplicadas quando a janela entra na árvore. dismiss_on_scrim, close_enabled,
fade_in e initial_focus são lidas ao vivo.
Sinais e chamadas
| Nome | O que é |
|---|---|
close_requested | ×, Escape, Voltar do Android ou um toque no véu — e request_close(). Emitido só pela janela de cima. |
back_requested · set_back(action) | O botão de voltar do cabeçalho, para uma janela com páginas. Um Callable vazio o esconde. |
height_changed(ratio) | Depois de um arraste na alça. |
is_top() · GoSurface.is_any_open() | Se esta é a janela que recebe o Voltar — se há alguma janela aberta. |
relayout() | Refaz o layout agora. Mudar uma propriedade de tamanho já faz isso por você. |
Folhas e painéis laterais
Uma lista que precisa de espaço sobe de baixo em um celular na vertical — onde o polegar está — e entra pelo lado em um tablet ou desktop, onde uma folha cobriria o jogo.

GoSheet
GoDrawer · LEFT
GoDrawer · RIGHTGoSheet — páginas vindas de baixo
var sheet := GoSheet.new()
add_child(sheet)
func show_bag(items: Array) -> void:
sheet.open("Bag")
var search := GoStyle.line_edit("Search the bag…")
search.text_changed.connect(filter_bag)
sheet.toolbar().add_child(search)
sheet.toolbar().visible = true
for item in items:
sheet.body.add_child(GoStyle.list_button(GoIconSet.BOX, item.name, show_item.bind(item),
Color.TRANSPARENT, item.description, false, GoIconSet.CHEVRON_RIGHT))
sheet.add_footer(GoStyle.button("Close", sheet.close, GoStyle.Tone.PRIMARY))
- É um
CanvasLayer(camada 10) que contém umGoSurfaceBOTTOMredimensionável — então fica acima do HUD, e o jogador pode arrastá-lo para deixá-lo mais alto. open(title)começa uma página nova: limpa o corpo, a barra de ferramentas e o botão de voltar e libera o que oadd_footer()acrescentou. Chame de novo para mostrar outra página na mesma folha;set_back()mostra um botão de voltar para o caminho de volta.open_key()recebe uma chave de tradução.close()a esconde — ela não é liberada, então o próximoopen()é instantâneo.closedepage_changedavisam quando isso acontece.height_ratio(0.6) é a altura com que ela abre;max_height_ratiosobe o teto;dismissable(true) a fecha com um toque no véu.
set_back() — só o botão de voltar na tela
volta uma página. Se o Voltar deve voltar uma página, trate você mesmo o close_requested em sheet.surface.
GoDrawer — pelo lado
var drawer := GoDrawer.new()
drawer.side = GoDrawer.Side.LEFT
drawer.follow_text_direction = true # o lado de início: esquerda, ou direita em árabe
add_child(drawer)
drawer.body.add_child(GoNavBar.drawer_list([{"icon": GoIconSet.HOME, "text": "Town"},
{"icon": GoIconSet.MAP, "text": "World map"}], 0, go_to))
drawer.open("Menu")
- Camada 80. 42% da largura da tela (
width_ratio), entremin_width(320dp) emax_width(420dp) — num celular ele cobre a maior parte da tela, e é por isso que ali uma folha é a melhor escolha. - O painel chega até a borda da tela; só o conteúdo dele recua para dentro da área segura, então um canto arredondado nunca corta a primeira linha.
LEFTsignifica esquerda da tela até em árabe. Liguefollow_text_directionquando o que importa é "o lado de início";effective_side()diz para onde ele foi.open(title)mantém o corpo — preencha-o uma vez só.set_title("")esconde o cabeçalho.motion_seconds(0.2) controla o deslize;reduce_motiono torna instantâneo.
drawer.close() a partir do seu próprio atalho de teclado — e note que GoSurface.is_any_open() não conta painéis laterais;
pergunte a drawer.is_open().
Cartões e menus ao lado de um controle
O que o jogador acabou de tocar merece uma resposta bem ao lado do dedo, não no meio da tela.

GoPopover
GoContextMenu
GoStyle.dropdown()
GoStyle.select()
GoComboboxGoPopover — um cartão sobre o que foi tocado
slot.pressed.connect(func() -> void:
var card := GoStyle.item_card({"icon": GoIconSet.POTION, "title": "Health potion",
"subtitle": "Heals 50 HP · 12 left"}, false)
GoPopover.open(slot, card, {"width": 280}))
Uma superfície ANCHOR com a camada, a âncora e o desmonte já ligados — as dez linhas que todo jogo escreve
de novo e de novo. Um de cada vez: abrir um segundo fecha o primeiro, porque cartões empilhados soterram a tela e ninguém
consegue dizer qual pertence a qual slot. O véu é transparente — um cartão aberto para comparar equipamentos não pode escurecer os equipamentos —,
mas ainda pega o primeiro toque de fora, que fecha o cartão e não vai adiante.
| Opção | Padrão | O que faz |
|---|---|---|
title | "" | Um cabeçalho com ×. Vazio quer dizer sem cabeçalho. |
width · max_height | 320 · 520 | O tamanho do cartão. |
dismissable | true | Um toque fora o fecha. |
compact · alpha · translate | false · -1 · false | Espaçamento compacto, opacidade da face, título como chave de tradução. |
layer | 95 | O CanvasLayer em que ele abre. |
GoPopover.close() e GoPopover.is_open() são estáticos. open() devolve a superfície — ou
null quando a âncora não está na árvore, então confira antes de fazer await shown.close_requested.
dismissable = false e sem título, nada na tela o fecha — só Escape, Voltar ou a sua própria chamada.
GoContextMenu — toque longo e clique direito
var menu := [
{"text": "Use", "icon": GoIconSet.POTION, "action": use},
{"separator": true},
{"text": "Drop", "icon": GoIconSet.TRASH, "action": drop, "danger": true}]
GoContextMenu.attach(slot, menu)
var more := GoStyle.icon_button(GoIconSet.MORE, Callable(), -1, &"more")
more.pressed.connect(func() -> void: GoContextMenu.open_at(more, menu))
- Um clique direito o abre na hora. Um dedo pressionado por meio segundo (
HOLD_SECONDS) o abre; mover mais de 12dp cancela — então rolar uma lista nunca abre um menu. - Um item é
{text, action, icon, disabled, checked, separator, danger, translate}. Passe umCallableem vez do array quando os itens dependem do que foi tocado; ele é chamado toda vez que o menu abre. dangercoloca "⚠ " antes do texto — o menu da engine só consegue colorir todos os itens de uma vez.- É o
PopupMenuda engine, vestido peloGoStyle.style_popup(). Ele não conta como superfície aberta.
⋯ visível
(open_at(), como acima), ou ensine-o uma vez com um coach mark.
Listas suspensas, seletores e o botão dividido
var sort := GoStyle.dropdown("Sort", ["Highest score", "Most wins", {"text": "Friends only", "disabled": true}],
func(index: int) -> void: sort_board(index))
var quality := GoStyle.select(["Low", "Medium", "High", "Ultra"], "Quality")
quality.item_selected.connect(func(index: int) -> void: settings.quality = index)
var send := GoSplitButton.make("Send", send_now, ["Send later", "Save as draft"])
Os três abrem o menu popup da engine: dropdown() é um MenuButton que executa uma ação,
select() um OptionButton que guarda um valor, GoSplitButton uma ação principal mais um menu na
seta dele (chosen(index)). Os menus ficam totalmente opacos de propósito — um menu mostrado na própria janela do sistema operacional
deixa pretos os pixels translúcidos.
GoCombobox — uma lista com busca
var friend := GoCombobox.make(friend_names, -1, "Find a friend")
friend.picked.connect(func(index: int) -> void: invite(friend_names[index]))
Ao ser tocado, ele abre uma lista ancorada na camada 96 com um véu transparente. A partir de search_threshold (8) itens
aparece um campo de busca que recebe o foco; ele encontra o termo em qualquer ponto do texto ou da dica, sem diferenciar maiúsculas, e um resultado vazio
avisa isso. list_width (0 = a largura do botão, no mínimo 180dp) define a largura da lista.
Popups que nunca param o jogo
Notícias que o jogador pode ler enquanto joga. Nenhum deles tem véu; um toque em qualquer lugar fora do próprio cartão vai para o jogo.

GoSnackbar
GoNotice
GoPromptCard
GoStyle.tooltip_node()GoSnackbar — uma mensagem com Desfazer
var snack := GoSnackbar.new()
add_child(snack)
if await snack.post({"text": "Dropped {name}", "args": {"name": item.name}, "icon": GoIconSet.TRASH,
"actions": ["Undo"], "seconds": 5.0}) == 0:
restore(item)
Chave do post() | O que faz |
|---|---|
text · title · args · translate | A mensagem, uma linha opcional em negrito, os {placeholders}, as chaves de tradução. |
tone · icon | A cor de destaque (GoTheme.SUCCESS, WARNING…) e um ícone. |
actions · closable | Botões — Strings ou {text, action} — e um ×. |
seconds | Quanto tempo ele fica. Negativo: o notice_duration_ms do tema. 0: até ser dispensado — nesse caso, dê a ele um botão. |
post() devolve o índice do botão tocado, ou -1 quando o tempo acabou ou ele foi dispensado. As mensagens
entram na fila (queue_limit 4; a mais antiga é descartada com -1), e o mesmo texto postado de novo enquanto aparece só renova o
timer. Ele fica embaixo (ou no topo, placement), acima da área segura e do teclado, na camada 90 — legível
por cima de um painel lateral. Com tap_to_dismiss (ligado), um toque em um cartão sem botões o fecha; um toque fora do cartão sempre
chega ao jogo. show_text() e show_key() são as formas de uma linha.
GoNotice — uma linha no HUD
var notice := GoNotice.new()
hud_corner.add_child(notice)
notice.show_text("Game saved", GoTheme.SUCCESS)
notice.custom_minimum_size.x = notice.preferred_width(340)
Um painel simples que você mesmo posiciona — em um GoHudAnchor, num canto, em qualquer lugar. Ele não toma nem a entrada nem o foco,
então tocar através dele chega ao botão que está embaixo, e uma mensagem nova substitui a anterior. Dê uma largura a ele:
preferred_width(limit) devolve a largura natural de um texto curto e quebra um texto longo dentro de limit.
set_content() mostra qualquer controle no lugar do texto.
GoPromptCard — uma pergunta que pode esperar
var spot := GoHudAnchor.new()
spot.spot = GoHudAnchor.Spot.TOP_CENTER
spot.reserve_space = false
spot.avoid_peers = true # fica abaixo das barras de vida em vez de cobri-las
hud.add_child(spot)
var invite := GoPromptCard.new()
spot.add_child(invite)
invite.set_icon(GoIconSet.USERS, Color.TRANSPARENT, true)
invite.set_title("Party invite from Ann")
invite.set_subtitle("Dragon's Lair · 3 / 4 members")
invite.set_actions([{"text": "Accept", "action": accept, "primary": true},
{"text": "Decline", "action": invite.hide}])
invite.fit_width(340)
invite.show()
get_tree().create_timer(15.0).timeout.connect(invite.hide)
- Ele começa escondido — chame
show(). Não tem timer próprio; acrescente um se a pergunta expirar. - Os botões dele não o fecham — as ações decidem. O × (
set_closable(true)) emiteclosed. fit_width(width)define a largura e prevê a altura a partir da fonte, então nada posicionado em relação a ele pula um frame depois.- A face dele segue
hud_alpha— ele faz parte do HUD, não é uma janela.
GoBanner e dicas
GoBanner.make("You're offline.", [{"text": "Retry", "action": reconnect}], GoIconSet.WARNING) fica dentro da própria
página, acima do conteúdo, e nunca expira; um botão o dispensa, a menos que ele tenha "keep": true.
GoStyle.tooltip_node(text) é o que retornar de _make_custom_tooltip() — a engine desenha a placa
(TooltipPanel) e o gohud o texto, com quebra de linha a partir de 260dp. Dicas são para mouse; nunca esconda atrás de uma algo de que o jogador precisa.
Tours e o console

GoCoachMark
GoConsoleGoCoachMark — um tour pelos controles de verdade
var tour := GoCoachMark.new()
tour.keep_clear = [top_bar]
tour_layer.add_child(tour)
tour.finished.connect(func(completed: bool) -> void: save.tutorial_done = true)
tour.start([{"target": bag_button, "title": "Bag", "body": "Everything you pick up lands here."},
{"target": map_button, "title": "Map", "body": "Tap to open the full map."}])
- Um anel em volta do controle de verdade e um cartão ao lado dele. Tocar no alvo avança o tour (o sinal
presseddele, ou"signal"no passo); Próximo e Pular ficam no cartão. Um alvo escondido é pulado. - O cartão fica fora do meio da tela (
avoid_center_band), longe das âncoras do HUD e dekeep_clear. - Enquanto qualquer
GoSurfaceestiver aberto, o tour se esconde e espera. Voltar ou Escape o encerram comfinished(false). - Ele não tem camada própria — adicione-o por último, ou em uma camada acima do HUD.
GoConsole — cheats enquanto você desenvolve
var console := GoConsole.new()
add_child(console)
console.register("give", "give <item> <count>", func(args: PackedStringArray) -> String:
return "gave %s x%s" % [args[0], args[1]] if args.size() >= 2 else "give <item> <count>")
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed(&"dev_console"): console.toggle()
Camada 200 — acima de tudo, para você poder depurar por cima de um diálogo — e ele continua rodando enquanto a árvore está pausada. Uma paleta sugere comandos enquanto você digita; as setas para cima e para baixo percorrem o histórico; Escape o fecha. Não há tecla embutida: associe a sua.
debug_only (ligado) o bloqueia em uma build de release — tanto open() quanto run(). Comandos de cheat nas
mãos de um jogador acabam com a economia de um jogo no primeiro dia.
Camadas, Voltar e entrada
Três regras decidem o que o jogador pode tocar enquanto algo está aberto: o que fica por cima, de quem é o Voltar e quem recebe um toque.
O que fica por cima
| Camada | O que fica ali | Definida com |
|---|---|---|
| 5 | O seu HUD (você escolhe) | — |
| 10 | GoSheet | CanvasLayer.layer |
| ~50 | Suas próprias janelas GoSurface, um menu de pausa | o seu CanvasLayer |
| 80 | GoDrawer | CanvasLayer.layer |
| 90 | GoSnackbar | layer_index |
| 95 | GoPopover | a opção "layer" |
| 96 | A lista do GoCombobox | — |
| 100 | GoDialogs | layer_index |
| 200 | GoConsole | layer_index |
A ordem é proposital: um snackbar informa o que acabou de acontecer, então aparece por cima de um painel lateral; um popover pertence ao que acabou de ser tocado, então fica acima disso; um diálogo faz uma pergunta e cobre os dois; o console cobre tudo, porque é o que você abre quando o resto deu errado.
De quem é o Voltar
- Só a janela de cima responde. Entre as superfícies abertas, a que está na camada mais alta (e, depois, a mais recente na árvore)
recebe o Escape, o cancelar do gamepad (
ui_cancel), o Voltar do Android e os toques no véu. Janelas empilhadas fecham uma de cada vez, nunca juntas. - O Voltar do Android é tratado um frame depois, então um único toque nunca fecha duas janelas.
- Enquanto houver algo aberto que é dono do Voltar, o Voltar do Android não fecha o app (
GoBackPolicyconta os donos e restauraquit_on_go_backdepois do último). Um popup seu que recebe o Voltar deve chamarGoBackPolicy.acquire()erelease()— no_exit_treetambém. - Um painel lateral, um tour de coach marks e um formulário dão passagem enquanto uma superfície está aberta — o Voltar vai primeiro para a superfície.
GoConfig.close_on_back(ligado) é o interruptor das superfícies.
Quem recebe um toque
- Uma superfície cobre a tela inteira e bloqueia a entrada — com um véu transparente também. Nada em uma camada inferior pode ser tocado enquanto ela está aberta; camadas acima dela (um snackbar) ainda podem.
- A roda do mouse e o gesto de pinça também param na janela: rolar além do fim de uma lista nunca rola o jogo.
- A entrada do gameplay que não passa por controles —
_unhandled_inputpara o movimento, um arraste de câmera — deve esperar:if GoSurface.is_any_open(): return. Isso cobre diálogos, folhas, popovers e o combobox; para um painel lateral, pergunteis_open()à parte. - Snackbar, aviso, cartão de pergunta e coach mark só recebem entrada no próprio cartão; em todo o resto, quem recebe é o jogo.
Foco
Uma janela aberta por um ponteiro não foca nada — nenhum anel de foco pisca por causa de um toque. Aberta pelo teclado ou pelo gamepad, o botão de fechar
(ou initial_focus) recebe o foco. O foco fica dentro da janela enquanto ela está aberta e volta para o controle que o tinha
quando a janela fecha. Diálogos focam o OK toda vez.
Pausando o jogo
Pausar a árvore pausa os popups também — só o console continua funcionando. Para um menu de pausa, dê à camada dele
process_mode = PROCESS_MODE_ALWAYS; assim a janela, os botões dela e o Voltar continuam rodando enquanto o jogo está parado.
O mesmo popup em todo visual
A confirmação do topo desta página, sobre o mesmo jogo, mudando só o preset. O código é idêntico — o tema, a skin e os ícones fazem o resto.

default_dark
default_light
scifi_dark
scifi_light
medieval_dark
medieval_light
material_light Novo
material_dark Novo
kids_light Novo
kids_dark Novo
comic_light Novo
comic_dark Novo
arcade_light Novo
arcade_dark NovoO que cada visual muda
| Visual | A janela | Cabeçalho e botões |
|---|---|---|
| Padrão | Arredondada (18dp), uma sombra suave, 80% opaca sobre o jogo | Um título simples e o ×; os botões primários sobem e afundam |
| Sci-fi | Cantos cortados com borda e brilho neon, a borda tingida pela cor de destaque | Marcas de foco em colchete |
| Medieval | Uma moldura forjada — ferro e couro, ou pergaminho — com rebites e ornamentos de canto | Títulos em Cinzel |
| Material 3 | Cantos de 28dp, respiro de 24dp, totalmente opaca | Um título de 24sp em Roboto; botões em pílula de 40dp dentro de uma área de toque de 48dp |
| Infantil | Um cartão de bala de gelatina com uma sombra suave, totalmente opaco | Uma faixa de título ensolarada (menta à noite), um botão de fechar redondo de bala, botões que se amassam ao serem tocados |
| Quadrinhos | Um contorno grosso de tinta e uma sombra dura, totalmente opaca | Uma legenda de título amarela, um botão de fechar redondo com contorno de tinta, botões cuja sombra some ao serem tocados |
| Arcade | Um painel grosso — uma moldura colorida em volta de um fundo claro —, totalmente opaco | Uma faixa de título dourada com um título branco contornado de tinta, uma tecla de fechar vermelha e redonda, teclas pintadas que saltam ao serem tocadas |
Vestir um popup a partir de uma skin
Uma skin decide as partes que um Theme não alcança. Estes são os métodos que os popups chamam — sobrescreva-os na sua própria
subclasse de GoSkin; por padrão eles não fazem nada.
| Método | O que ele veste |
|---|---|
title_plate_box() · title_plate_ink() · title_plate_min_size() | Uma placa atrás do título de uma janela. O título se centraliza nela e encolhe, até o tamanho mínimo (15), para caber em uma linha. |
dress_close_button(button) | O × da janela — o visual infantil o transforma em uma bala redonda; o de quadrinhos, em um disco com contorno de tinta; o arcade, em uma tecla vermelha e redonda. |
dress_title(label) | O rótulo do título da janela — o visual arcade contorna de tinta o seu título branco. |
press_feedback(control) | Roda no instante em que um dedo pousa em um botão criado pelo gohud — o visual infantil o amassa; o arcade o faz saltar. |
floating_box() · notice_box() · drawer_box() · banner_box() · overlay_box() | As faces dos cartões de pergunta e dos cartões do tour, dos snackbars e avisos, do painel lateral, dos banners e do console. |
coach_ring_box() · draw_coach_pointer() | O anel e o ponteiro do tour. |
Quanto dá para ver através
Cada tipo de popup segue a própria opacidade, então um cartão de HUD pode ficar leve enquanto um diálogo continua sólido: janelas, folhas, diálogos,
popovers e o combobox seguem panel_alpha; o painel lateral e o cartão do tour, card_alpha; cartões de pergunta e o
console, hud_alpha; snackbars e avisos, notice_alpha; menus, popup_alpha. Material e infantil
deixam todos opacos; quadrinhos também, exceto os cartões de pergunta e o console, a 92%, e arcade também, exceto esses dois, a 94%. Sobrescreva um tipo com GoConfig.container_alpha_overrides, todos com
container_alpha (uma proporção — 0.95), ou uma janela com o alpha dela. Só a face esmaece; o texto
nunca fica transparente. Opacidade em detalhe →
Configurações que moldam todo popup
Campos de GoConfig (GoUi.config). Alguns são copiados quando uma janela é criada, então defina-os
antes de montar a UI — ou chame GoUi.refresh() depois de mudá-los.
| Campo | Padrão | O que faz |
|---|---|---|
surface_max_width · surface_max_height | 480 · 700 | O tamanho máximo que uma janela atinge. |
surface_height_ratio · surface_max_height_ratio | 0.68 · 0.72 | A altura habitual e o teto (também o limite do arraste). |
surface_fit_max_height_ratio | 0.94 | Quanto um cartão centralizado ajustado ao conteúdo pode crescer antes de rolar. |
surface_width_ratio_portrait · …_landscape | 0.94 · 0.72 | A largura como fração do espaço. |
dismiss_on_scrim | false | Um toque no véu fecha janelas e diálogos. Copiado quando a janela é criada. |
surface_fade_in · fade_seconds | false · 0.14 | Fazer as janelas aparecerem com fade, e com que velocidade. |
close_on_back | true | Escape, ui_cancel e o Voltar do Android fecham a janela de cima. |
close_button_visual | 36 | O tamanho visual do × — a área de toque dele continua com 48dp. |
suppress_pointer_focus_ring | true | Sem anel de foco quando uma janela é aberta por toque ou mouse. |
container_alpha · container_alpha_overrides | -1 · {} | A opacidade de todas as faces, ou por tipo (GoTheme.BOX_*). |
reduce_motion | false | Sem fades, deslizes, pulsos nem amassados. |
min_touch_size | 48 | A altura dos botões de cartões de pergunta e de coach marks. |
respect_safe_area | true | Mantém as janelas longe do notch e da barra de gestos. |
text_keys · text_overrides | — | As palavras do próprio kit: fechar, voltar, confirmar, cancelar, próximo, concluído, pular, buscar, vazio… |
sound_cues · feedback háptico | — | O que o GoFeedback reproduz ao abrir, fechar, confirmar, cancelar e tocar. |
Receitas
Copie, cole, troque as palavras. Toda chamada aqui foi conferida com o código-fonte.
Uma compra com o preço na mensagem
func buy(item_name: String, price: int) -> void:
if await dialogs.confirm("Buy {item}?", "It costs {price} gold.", "Buy", "", "",
{"item": item_name, "price": price}):
purchase(item_name)
Uma janela de configurações com Salvar fixo
func open_settings() -> void:
var layer := CanvasLayer.new()
layer.layer = 50
add_child(layer)
var window := GoSurface.new()
window.set_title("Settings")
window.dismiss_on_scrim = true
window.close_requested.connect(layer.queue_free)
window.body.add_child(GoStyle.section("Sound", false))
window.body.add_child(GoStyle.toggle("Music", false))
window.body.add_child(GoStyle.slider(0.0, 1.0, 0.05))
var row := GoStyle.row()
row.add_child(GoStyle.button("Cancel", window.request_close))
row.add_child(GoStyle.button("Save", func() -> void:
save_settings()
layer.queue_free(), GoStyle.Tone.PRIMARY))
window.footer.add_child(row)
window.footer.visible = true
layer.add_child(window)
Um formulário que diz o que está errado
var field := GoField.make("Name", GoStyle.line_edit(), "3–12 letters")
window.body.add_child(field)
create_button.pressed.connect(func() -> void:
if name_is_taken():
field.set_error("That name is taken")
window.set_status_text("Name check failed", GoTheme.DANGER)
else:
window.clear_status()
create_character())
Uma data em uma folha
sheet.open("Delivery date")
var picker := GoDatePicker.make({"year": 2026, "month": 10, "day": 14}, func(date: Dictionary) -> void:
delivery = date
sheet.close())
picker.min_date = GoDatePicker.today()
sheet.body.add_child(picker)
Um menu de pausa
func pause() -> void:
get_tree().paused = true
var layer := CanvasLayer.new()
layer.layer = 50
layer.process_mode = Node.PROCESS_MODE_ALWAYS # continua rodando enquanto o jogo está pausado
add_child(layer)
var menu := GoSurface.new()
menu.set_title("Paused")
menu.close_requested.connect(func() -> void:
get_tree().paused = false
layer.queue_free())
menu.body.add_child(GoStyle.button("Resume", menu.request_close, GoStyle.Tone.PRIMARY))
menu.body.add_child(GoStyle.button("Settings", open_settings))
menu.body.add_child(GoStyle.button("Quit to title", quit_to_title, GoStyle.Tone.DANGER))
layer.add_child(menu)
Gameplay que espera os popups
func _unhandled_input(event: InputEvent) -> void:
if GoSurface.is_any_open() or drawer.is_open():
return
handle_gameplay(event)
Armadilhas
- Um
GoSurfacenunca se libera sozinho. Conecteclose_requested, ou o × não faz nada. - Opções definidas depois do
add_child—scrim_transparent,show_header,resizable,scroll_body, elayer_indexemax_widthdoGoDialogs— não são aplicadas. - Um segundo
confirm()devolvefalsena hora. Liguequeue_when_busyse duas perguntas puderem se encontrar. - Enter confirma. O OK tem o foco, nos diálogos destrutivos também.
- Voltar em uma folha fecha a folha, não a subpágina.
- Escape não fecha um painel lateral, e
is_any_open()não o enxerga. - Um popover ou uma lista de combobox bloqueia o jogo até fechar, mesmo com véu transparente — e um popover com
dismissable = falsee sem título não tem nada na tela que o feche. - Um cartão de pergunta começa escondido e não tem tempo limite.
- Um snackbar com
seconds: 0e sem botão nunca sai. E, por padrão, ele pega os toques no próprio cartão. - Um toque longo que ninguém conhece é um recurso que ninguém usa. Mostre um
⋯também. - Uma árvore pausada pausa o seu popup. Use
PROCESS_MODE_ALWAYSna camada dele. - Um aviso sem largura encolhe até o mínimo — use
preferred_width(). - Nunca pergunte em um snackbar, nunca informe em um diálogo. Uma pergunta em um snackbar some se for ignorada; uma notícia não deveria parar o jogo.