Elige la ventana emergente adecuada
Una ventana emergente interrumpe. Antes de elegir una, responde a una sola pregunta: ¿tiene que responder el jugador antes de que el juego continúe? Si es así, bloquea. Si no, no bloquees nunca: una tarjeta que detiene el juego para dar noticias que el jugador no ha pedido es la forma más rápida de conseguir que cierre tu juego.
- Una pregunta que tiene respuesta — comprar, borrar, salir, sobrescribir →
GoDialogs.confirm(). Algo que hay que dar por visto →alert(). Una entre pocas opciones →choose(). - Una pantalla propia — los ajustes, una hoja de personaje, un objeto de la tienda →
GoSurfaceen el centro de la pantalla. - Una lista larga o una página de gestión — la mochila, el correo, los amigos →
GoSheetdesde abajo en un teléfono,GoDrawerdesde un lado en una pantalla ancha. - Algo sobre lo que se acaba de pulsar — las estadísticas de un objeto, sus acciones →
GoPopoveroGoContextMenua su lado. - Noticias con las que el juego puede seguir — guardado, soltado, invitado →
GoSnackbar,GoNoticeoGoPromptCard. Estos nunca ocupan la pantalla. - Enseñar los controles →
GoCoachMark. Trucos mientras desarrollas →GoConsole.
Todas las ventanas emergentes de un vistazo
| Ventana emergente | Detiene el juego | Atrás / Escape | Un toque fuera | Úsala para |
|---|---|---|---|---|
GoDialogs confirm · alert | Sí | Lo cierra — confirm responde false, alert retorna | Lo cierra solo con dismiss_on_scrim | Preguntas que necesitan respuesta |
GoDialogs.choose | Sí | Lo cierra — responde -1 | Lo cierra solo con dismiss_on_scrim | Elegir una entre pocas opciones |
GoSurface | Sí | Pide cerrar la de más arriba | Pide cerrar con dismiss_on_scrim | Tus propias ventanas |
GoSheet | Sí | Cierra la hoja entera | La cierra | La mochila, una lista de tienda, el correo |
GoDrawer | Sí | Solo el botón Atrás de Android | Lo cierra | Una lista lateral en una pantalla ancha |
GoPopover | Hasta que se cierra | Lo cierra | Lo cierra — y no llega más allá | Detalles de objetos y habilidades |
Lista de GoCombobox | Hasta que se cierra | La cierra | La cierra | Una lista de selección con búsqueda |
GoContextMenu · desplegable · selector | El menú del motor | El menú del motor | Lo cierra | Acciones, listas cortas |
GoSnackbar | No | — | Llega al juego | Guardado · Deshacer · Reintentar |
GoPromptCard | No | — | Llega al juego | Una invitación, una propuesta de intercambio |
GoNotice | Nunca | — | Llega al juego | Una línea fija en el HUD |
GoBanner | No — está dentro de la página | — | — | "Sin conexión. Reintentar" |
GoCoachMark | Solo su tarjeta | Termina el recorrido | Llega al juego | Un recorrido para el primer arranque |
GoConsole | Solo su panel | La tecla Escape la cierra | Llega al juego | Comandos de depuración |
En qué capa se abre cada una, y por qué en ese orden, se explica en Capas, Atrás y entrada.

GoDialogs
GoSurface
GoSheet
GoDrawer
GoPopover
GoContextMenu
GoSnackbar
GoPromptCard
GoCoachMark
GoConsoleTodas las imágenes de esta página son el mismo pequeño juego — vida y maná arriba a la izquierda, monedas arriba a la derecha, un joystick y tres
ranuras rápidas abajo — en default_dark, con una ventana emergente abierta encima. La misma
ventana emergente en los catorce aspectos →
Anatomía de una ventana emergente
Casi todas las ventanas emergentes que bloquean en gohud — diálogos, hojas, popovers, la lista del combobox y tus propias
ventanas — son un mismo nodo, GoSurface. Aprende sus partes una vez y las conocerás todas.

| Parte | Qué es | Cómo se accede a ella |
|---|---|---|
| Capa | El CanvasLayer en el que vive la ventana emergente. Su número decide qué tapa. | La pones tú en un GoSurface; viene integrada en los demás (tabla) |
| Velo | El velo sobre todo lo que queda detrás. Recoge los toques que no caen en la tarjeta. | scrim_transparent, dismiss_on_scrim, el color scrim del tema |
| Tarjeta | La propia ventana — la cara de panel del tema: redondeada, cortada, forjada o de gelatina según el aspecto. | card, alpha |
| Encabezado | Un botón atrás opcional, el título (dos líneas como máximo, después "…") y el botón de cerrar. | set_title() · set_title_key() · set_back() · close_enabled · show_header |
| Barra de herramientas | Una fila fija bajo el encabezado — un campo de búsqueda, chips de filtro. Oculta hasta que la usas. | toolbar |
| Cuerpo | La parte que se desplaza. El contenido va aquí. | body · scroll_body · clear() |
| Línea de estado | Una línea fija entre el cuerpo y el pie — "Falló la comprobación del nombre", "Guardando…". | set_status_text() · set_status_key() · clear_status() |
| Pie | Botones fijos en la parte inferior. Oculto hasta que lo usas. | footer |
toolbar, la línea de estado y footer; se quedan en su sitio mientras el cuerpo se desplaza.
Diálogos que esperas con await
GoDialogs pregunta y espera. Un nodo, tres preguntas — y la respuesta vuelve en la misma
línea, así que el código se lee en el mismo orden en que lo vive el jugador.
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) — destructiva
alert()
choose()
ActionLayout.HORIZONTALLas tres preguntas
confirm(title, body, ok_text := "", cancel_text := "", extra := "",
args := {}, destructive := false) -> bool
alert(title, body, ok_text := "", extra := "", args := {}) # espérala con await
choose(title, options: Array, cancel_text := "", translate := false) -> int
confirm_key(…) · alert_key(…) # claves de traducción
| Llamada | Devuelve | Úsala para |
|---|---|---|
confirm() | true o false | Sí o no — comprar, borrar, salir, sobrescribir. |
alert() | nada — espérala con await | Algo que el jugador tiene que ver: una recompensa, una conexión perdida. |
choose() | el índice de la opción, o -1 | Una entre pocas. Una opción es un String o {text, icon, danger, subtitle}. |
confirm_key() · alert_key() | igual que arriba | Lo mismo con claves de traducción — el texto sigue un cambio de idioma mientras el diálogo está abierto. |
Las etiquetas de botón vacías recurren a las palabras propias del kit (confirm, cancel en GoConfig.text_keys).
extra se añade al cuerpo en una línea nueva. args rellena los {placeholders} del título
y del cuerpo: confirm("Buy {item}?", "It costs {price} gold.", "Buy", "", "", {"item": name, "price": 300}).
Cada salida tiene su respuesta
| El jugador… | confirm | alert | choose |
|---|---|---|---|
| pulsa OK / una opción | true | retorna | el índice de la opción |
| pulsa Cancelar | false | — | -1 |
| pulsa ×, Escape o el botón Atrás de Android | false | retorna | -1 |
toca el velo (solo con dismiss_on_scrim) | false | retorna | -1 |
el nodo GoDialogs sale del árbol | false | retorna | -1 |
Así que el código que sigue a await siempre se ejecuta — ninguna respuesta se queda colgada, ni siquiera cuando la escena cambia por debajo del diálogo.
Acciones destructivas
Pasa destructive = true para algo que no se puede deshacer. El botón OK pasa a ser el botón relleno
Tone.DANGER_SOLID — blanco sobre rojo en todos los aspectos, mientras que una etiqueta de peligro teñida tendría que oscurecerse
hasta casi negro en un tema claro para seguir siendo legible. El tono se restablece en cada llamada, porque la ventana se reutiliza.
if await dialogs.confirm("Delete this save?", "Slot 2 — Aria, level 12. This cannot be undone.",
"Delete", "Keep it", "", {}, true):
delete_save(2)
Disposición de los botones
action_layout | Qué hace |
|---|---|
VERTICAL (por omisión) | Botones apilados, a todo el ancho. Seguro en cualquier idioma — una etiqueta larga en alemán o en ruso nunca queda apretada. |
HORIZONTAL | Una sola fila, con la mitad del ancho para cada uno. |
AUTO | Una fila solo cuando ambas etiquetas caben en la mitad de la tarjeta en una sola línea; apilados en caso contrario. Se vuelve a decidir con cada cambio de idioma o de tamaño. |
action_gap es el espacio entre los botones, body_gap el espacio por encima de ellos — hazlo más ancho que
el hueco entre botones para que la pregunta y la elección se lean como dos partes. Para un único diálogo, llama a
set_next_action_layout() antes de abrirlo. El único botón de un alert ocupa siempre todo el ancho.
De uno en uno
Un segundo confirm() mientras hay otro abierto devuelve false de inmediato — un diálogo nunca se apila sobre otro por
accidente. Activa queue_when_busy y, en su lugar, esperará su turno; alert() siempre espera.
queue_limit (8) limita la cola, y si se desborda, la petición más antigua recibe false.
pending() cuenta las peticiones en espera y clear_pending() les responde false a todas.
choose() nunca hace cola: devuelve -1 cuando ya hay un diálogo abierto.
Teléfono y escritorio
choose() se abre como una hoja desde abajo en Android e iOS — donde está el pulgar — y como una tarjeta en el
centro en un escritorio. Una confirmación se limita a max_width (420dp) y se centra. Asigna layer_index,
max_width y las demás propiedades exportadas antes de add_child; se aplican cuando el nodo entra en el árbol.
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 — una ventana propia
Cuando una ventana emergente es algo más que una pregunta — los ajustes, una hoja de personaje, un objeto que comprar —, constrúyela sobre
GoSurface. El área segura, el teclado, las filas fijas, el desplazamiento, Atrás, el foco y el redimensionado ya están resueltos.
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. Ocultar o liberar
la ventana es tarea de la pantalla que la posee — solo ella sabe si "cerrar" significa guardar, descartar o preguntar antes.
Tres colocaciones

CENTER — confirmaciones, ajustes
BOTTOM — listas, páginas de gestión
ANCHOR — junto a un control
GoDatePicker en una ventana CENTERANCHOR necesita anchor_control. La tarjeta se coloca a su derecha si cabe anchor_min_width;
si no, en el lado más ancho; si tampoco, dentro de la pantalla, y se abre hacia arriba cuando hay más espacio encima. Sigue a un
ancla en movimiento en cada fotograma. anchor_width (320) y anchor_max_height (520) fijan su tamaño.
Qué tamaño alcanza
| Medida | Regla | Se cambia con |
|---|---|---|
| Espacio | El área segura, menos el teclado, menos el margen de pantalla (16dp, nunca más del 10% del lado corto). | — |
| Ancho | El 94% del espacio en vertical, el 72% en horizontal, como máximo 480dp. | max_width · GoConfig.surface_width_ratio_* |
| Alto | Tan alta como su contenido (fit_content), hasta el 68% del espacio, con un tope del 72%. Una tarjeta centrada que no es redimensionable puede crecer hasta el 94% para evitar el desplazamiento. | height_ratio · max_height_ratio · max_height |
| Densidad | Densa cuando compact está activado, cuando la pantalla mide menos de 420dp de alto (un teléfono de lado) o cuando el contenido no cabe: menos relleno, huecos más pequeños, un título más pequeño. Mide ambas variantes y se queda con la más holgada que quepa — sin vaivenes. | compact |
| Redimensionado | Con resizable, el título hace de asa: arrástralo entre el 30% y el tope. height_changed(ratio) informa del resultado. | resizable |
Una proporción por encima del tope se recorta, con un único aviso en una compilación de depuración. El teclado empuja la tarjeta hacia arriba y desplaza el campo enfocado hasta dejarlo a la vista; el área segura la mantiene lejos de la muesca y de la barra de gestos en Android e iOS.
Propiedades
| Propiedad | Por omisión | Qué hace |
|---|---|---|
placement | CENTER | CENTER, BOTTOM o ANCHOR |
dismiss_on_scrim | GoConfig.dismiss_on_scrim (false) | Un toque en el velo pide cerrar. Un arrastre que empezó dentro de la tarjeta, no. |
scrim_transparent | false | Mantiene el juego visible — el velo sigue recogiendo los toques de fuera. |
close_enabled | true | Muestra la × y deja que Escape y Atrás pidan cerrar. |
show_header · scroll_body · resizable | true · true · false | La fila del encabezado, el cuerpo desplazable, el asa de arrastre. |
fit_content · compact | true · false | Ajustarse al contenido; empezar en modo denso. |
fade_in | GoConfig.surface_fade_in (false) | La tarjeta aparece con un fundido al abrirse. Instantáneo con reduce_motion. |
alpha | -1 (la del tema) | La opacidad de la cara de la tarjeta, como proporción. Solo se desvanece la cara — nunca el texto. |
initial_focus | ninguno | El control que recibe el foco al abrirse. |
add_child: scrim_transparent, show_header, resizable y
scroll_body se aplican cuando la ventana entra en el árbol. dismiss_on_scrim, close_enabled,
fade_in e initial_focus se leen en vivo.
Señales y llamadas
| Nombre | Qué es |
|---|---|
close_requested | La ×, Escape, el botón Atrás de Android o un toque en el velo — y request_close(). Solo lo emite la ventana de más arriba. |
back_requested · set_back(action) | El botón atrás del encabezado, para una ventana con páginas. Un Callable vacío lo oculta. |
height_changed(ratio) | Tras arrastrar el asa. |
is_top() · GoSurface.is_any_open() | Si esta es la ventana que recibe Atrás — si hay alguna ventana abierta. |
relayout() | Recoloca todo ahora mismo. Cambiar una propiedad de tamaño ya lo hace por ti. |
Hojas y paneles laterales
Una lista que necesita espacio sube desde abajo en un teléfono sujeto en vertical — donde está el pulgar — y entra desde un lado en una tableta o en un escritorio, donde una hoja taparía el juego.

GoSheet
GoDrawer · LEFT
GoDrawer · RIGHTGoSheet — páginas desde abajo
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))
- Es un
CanvasLayer(capa 10) que contiene unGoSurfaceBOTTOMredimensionable — así queda por encima del HUD, y el jugador puede arrastrarlo para hacerlo más alto. open(title)empieza una página nueva: vacía el cuerpo, la barra de herramientas y el botón atrás, y libera lo que añadióadd_footer(). Llámalo otra vez para mostrar otra página en la misma hoja;set_back()muestra un botón atrás para volver al inicio.open_key()acepta una clave de traducción.close()la oculta — no se libera, así que el siguienteopen()es instantáneo.closedypage_changedte avisan de cuándo ocurre.height_ratio(0.6) es la altura con la que se abre;max_height_ratiosube el tope;dismissable(true) la cierra con un toque en el velo.
set_back() — solo el botón atrás de la pantalla
retrocede una página. Si Atrás tiene que retroceder, gestiona tú mismo close_requested en sheet.surface.
GoDrawer — desde un lado
var drawer := GoDrawer.new()
drawer.side = GoDrawer.Side.LEFT
drawer.follow_text_direction = true # el lado de inicio: la izquierda, o la derecha en á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")
- Capa 80. El 42% del ancho de la pantalla (
width_ratio), entremin_width(320dp) ymax_width(420dp) — en un teléfono tapa casi toda la pantalla, y por eso allí una hoja es mejor opción. - El panel llega hasta el borde de la pantalla; solo su contenido se retira hasta el área segura, así que una esquina redondeada nunca recorta la primera fila.
LEFTsignifica la izquierda de la pantalla incluso en árabe. Activafollow_text_directioncuando lo que importa es "el lado de inicio";effective_side()te dice dónde ha quedado.open(title)conserva el cuerpo — llénalo una sola vez.set_title("")oculta el encabezado.motion_seconds(0.2) controla el deslizamiento;reduce_motionlo hace instantáneo.
drawer.close() desde tu propio atajo de teclado — y ten en cuenta que GoSurface.is_any_open() no cuenta los paneles laterales;
pregúntale a drawer.is_open().
Tarjetas y menús junto a un control
Lo que el jugador acaba de pulsar merece una respuesta justo al lado del dedo, no en el centro de la pantalla.

GoPopover
GoContextMenu
GoStyle.dropdown()
GoStyle.select()
GoComboboxGoPopover — una tarjeta sobre lo que se ha pulsado
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}))
Una superficie ANCHOR con la capa, el ancla y el desmontaje ya conectados — las diez líneas que todos los juegos escriben
una y otra vez. De una en una: abrir una segunda cierra la primera, porque las tarjetas apiladas entierran la pantalla y nadie
sabe cuál pertenece a qué ranura. El velo es transparente — una tarjeta abierta para comparar equipo no debe oscurecer ese equipo —,
pero aun así recoge el primer toque de fuera, que cierra la tarjeta y no llega más allá.
| Opción | Por omisión | Qué hace |
|---|---|---|
title | "" | Un encabezado con ×. Vacío significa sin encabezado. |
width · max_height | 320 · 520 | El tamaño de la tarjeta. |
dismissable | true | Un toque fuera la cierra. |
compact · alpha · translate | false · -1 · false | Espaciado denso, opacidad de la cara, título como clave de traducción. |
layer | 95 | El CanvasLayer en el que se abre. |
GoPopover.close() y GoPopover.is_open() son estáticos. open() devuelve la superficie — o
null cuando el ancla no está en el árbol, así que compruébalo antes de hacer await shown.close_requested.
dismissable = false y sin título, nada en la pantalla lo cierra — solo Escape, Atrás o tu propia llamada.
GoContextMenu — pulsación larga y clic derecho
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))
- Un clic derecho lo abre al instante. Un dedo que se mantiene medio segundo (
HOLD_SECONDS) lo abre; moverse más de 12dp lo cancela — así, desplazar una lista nunca abre un menú. - Un elemento es
{text, action, icon, disabled, checked, separator, danger, translate}. Pasa unCallableen vez del array cuando las entradas dependan de lo que se ha pulsado; se le llama cada vez que se abre el menú. dangerpone "⚠ " delante del texto — el menú del motor solo puede colorear todos sus elementos a la vez.- Es el
PopupMenudel motor, vestido porGoStyle.style_popup(). No cuenta como superficie abierta.
⋯ visible
(open_at(), como arriba), o enséñala una vez con una marca guía.
Desplegables, selectores y el botón 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"])
Los tres abren el menú emergente del motor: dropdown() es un MenuButton que ejecuta una acción,
select() un OptionButton que guarda un valor, GoSplitButton una acción principal más un menú en
su flecha (chosen(index)). Los menús se mantienen totalmente opacos a propósito — un menú mostrado en su propia ventana del sistema operativo
vuelve negros los píxeles translúcidos.
GoCombobox — una lista con búsqueda
var friend := GoCombobox.make(friend_names, -1, "Find a friend")
friend.picked.connect(func(index: int) -> void: invite(friend_names[index]))
Al pulsarlo, abre una lista anclada en la capa 96 con un velo transparente. A partir de search_threshold (8) entradas
aparece un campo de búsqueda que toma el foco; busca coincidencias en cualquier parte del texto o de la pista, sin distinguir mayúsculas, y un resultado vacío
lo indica. list_width (0 = el ancho del botón, al menos 180dp) fija el ancho de la lista.
Ventanas emergentes que nunca detienen el juego
Noticias que el jugador puede leer mientras juega. Ninguna tiene velo; un toque en cualquier sitio que no sea la propia tarjeta va al juego.

GoSnackbar
GoNotice
GoPromptCard
GoStyle.tooltip_node()GoSnackbar — un mensaje con Deshacer
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)
Clave de post() | Qué hace |
|---|---|
text · title · args · translate | El mensaje, una línea opcional en negrita, los {placeholders}, las claves de traducción. |
tone · icon | El color de acento (GoTheme.SUCCESS, WARNING…) y un icono. |
actions · closable | Botones — Strings o {text, action} — y una ×. |
seconds | Cuánto tiempo se queda. Negativo: el notice_duration_ms del tema. 0: hasta que se descarte — en ese caso, dale un botón. |
post() devuelve el índice del botón pulsado, o -1 si se agotó el tiempo o se descartó. Los mensajes
hacen cola (queue_limit 4; el más antiguo se descarta con -1), y el mismo texto publicado otra vez mientras se muestra solo reinicia su
temporizador. Se sitúa abajo (o arriba, placement), por encima del área segura y del teclado, en la capa 90 — legible
sobre un panel lateral. Con tap_to_dismiss (activado), un toque en una tarjeta sin botones la cierra; un toque fuera de la tarjeta siempre
llega al juego. show_text() y show_key() son las formas de una sola línea.
GoNotice — una línea en el 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)
Un panel sencillo que colocas tú — en un GoHudAnchor, en una esquina, donde quieras. No toma ni la entrada ni el foco,
así que pulsar a través de él llega al botón de debajo, y un mensaje nuevo sustituye al anterior. Dale un ancho:
preferred_width(limit) devuelve el ancho natural de un texto corto y ajusta un texto largo dentro de limit.
set_content() muestra cualquier control en lugar de texto.
GoPromptCard — una pregunta que puede esperar
var spot := GoHudAnchor.new()
spot.spot = GoHudAnchor.Spot.TOP_CENTER
spot.reserve_space = false
spot.avoid_peers = true # se coloca bajo las barras de vida en vez de taparlas
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)
- Empieza oculta — llama a
show(). No tiene temporizador propio; añade uno si la pregunta caduca. - Sus botones no la cierran — deciden las acciones. La × (
set_closable(true)) emiteclosed. fit_width(width)fija el ancho y predice la altura a partir de la fuente, así que nada colocado en función de ella da un salto un fotograma después.- Su cara sigue
hud_alpha— forma parte del HUD, no es una ventana.
GoBanner y tooltips
GoBanner.make("You're offline.", [{"text": "Retry", "action": reconnect}], GoIconSet.WARNING) se coloca dentro de la propia
página, encima del contenido, y nunca caduca; un botón lo descarta salvo que tenga "keep": true.
GoStyle.tooltip_node(text) es lo que hay que devolver desde _make_custom_tooltip() — el motor dibuja el panel
(TooltipPanel) y gohud el texto, que se ajusta a partir de 260dp. Los tooltips son para el ratón; nunca escondas tras uno algo que el jugador necesita.
Recorridos y la consola

GoCoachMark
GoConsoleGoCoachMark — un recorrido por los controles reales
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."}])
- Un anillo alrededor del control real y una tarjeta a su lado. Pulsar el objetivo hace avanzar el recorrido (su señal
pressed, o"signal"en el paso); Siguiente y Saltar están en la tarjeta. Un objetivo oculto se omite. - La tarjeta se mantiene fuera del centro de la pantalla (
avoid_center_band), lejos de los anclajes del HUD y dekeep_clear. - Mientras haya algún
GoSurfaceabierto, el recorrido se oculta y espera. Atrás o Escape lo terminan confinished(false). - No tiene capa propia — añádelo el último, o en una capa por encima del HUD.
GoConsole — trucos mientras desarrollas
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()
Capa 200 — por encima de todo, para que puedas depurar sobre un diálogo — y sigue funcionando mientras el árbol está en pausa. Una paleta sugiere comandos mientras escribes; Arriba y Abajo recorren el historial; Escape la cierra. No trae ninguna tecla asignada: asigna la tuya.
debug_only (activado) la bloquea en una compilación de publicación — tanto open() como run(). Los comandos de trucos en
manos de un jugador acaban con la economía de un juego el primer día.
Capas, Atrás y entrada
Tres reglas deciden qué puede tocar el jugador mientras hay algo abierto: qué queda encima, a quién pertenece Atrás y quién recibe un toque.
Qué queda encima
| Capa | Qué hay en ella | Se asigna con |
|---|---|---|
| 5 | Tu HUD (a tu elección) | — |
| 10 | GoSheet | CanvasLayer.layer |
| ~50 | Tus propias ventanas GoSurface, un menú de pausa | tu CanvasLayer |
| 80 | GoDrawer | CanvasLayer.layer |
| 90 | GoSnackbar | layer_index |
| 95 | GoPopover | la opción "layer" |
| 96 | La lista de GoCombobox | — |
| 100 | GoDialogs | layer_index |
| 200 | GoConsole | layer_index |
El orden es deliberado: un snackbar informa de lo que acaba de pasar, así que se lee por encima de un panel lateral; un popover pertenece a lo que se acaba de pulsar, así que queda por encima de eso; un diálogo hace una pregunta y tapa a ambos; la consola lo tapa todo, porque es lo que abres cuando todo lo demás ha fallado.
A quién pertenece Atrás
- Solo responde la ventana de más arriba. De las superficies abiertas, la que está en la capa más alta (y, después, la última del árbol)
recibe Escape, la cancelación del mando (
ui_cancel), el botón Atrás de Android y los toques en el velo. Las ventanas apiladas se cierran de una en una, nunca todas juntas. - El Atrás de Android se procesa un fotograma más tarde, así que una sola pulsación nunca puede cerrar dos ventanas.
- Mientras haya abierto algo que posee Atrás, el Atrás de Android no cierra la app (
GoBackPolicycuenta los propietarios y restauraquit_on_go_backtras el último). Tu propia ventana emergente que recibe Atrás debe llamar aGoBackPolicy.acquire()yrelease()— también en_exit_tree. - Un panel lateral, un recorrido de marcas guía y un formulario se hacen a un lado mientras hay una superficie abierta — Atrás va primero a la superficie.
GoConfig.close_on_back(activado) es el interruptor para las superficies.
Quién recibe un toque
- Una superficie cubre toda la pantalla y detiene la entrada — también con un velo transparente. Nada de una capa inferior se puede pulsar mientras está abierta; las capas por encima de ella (un snackbar) sí.
- La rueda del ratón y el gesto de pellizcar también se detienen en la ventana: desplazarse más allá del final de una lista nunca desplaza el juego.
- La entrada de juego que no pasa por controles —
_unhandled_inputpara el movimiento, un arrastre de cámara — debe quedar en espera:if GoSurface.is_any_open(): return. Eso cubre diálogos, hojas, popovers y el combobox; a un panel lateral pregúntaleis_open()por separado. - El snackbar, el aviso, la tarjeta de pregunta y la marca guía solo reciben entrada en su propia tarjeta; en el resto de la pantalla, la recibe el juego.
Foco
Una ventana abierta con un puntero no enfoca nada — ningún anillo de foco destella con un toque. Si se abre con el teclado o con un mando, el botón de cerrar
(o initial_focus) toma el foco. El foco se queda dentro de la ventana mientras está abierta y vuelve al control que lo tenía
cuando la ventana se cierra. Los diálogos enfocan OK siempre.
Pausar el juego
Pausar el árbol pausa también las ventanas emergentes — solo la consola sigue funcionando. Para un menú de pausa, dale a su capa
process_mode = PROCESS_MODE_ALWAYS; así la ventana, sus botones y Atrás siguen funcionando mientras el juego está detenido.
La misma ventana emergente en cada aspecto
La confirmación del principio de esta página, sobre el mismo juego, cambiando solo el preajuste. El código es idéntico — el tema, el skin y los iconos hacen el resto.

default_dark
default_light
scifi_dark
scifi_light
medieval_dark
medieval_light
material_light Nuevo
material_dark Nuevo
kids_light Nuevo
kids_dark Nuevo
comic_light Nuevo
comic_dark Nuevo
arcade_light Nuevo
arcade_dark NuevoQué cambia cada aspecto
| Aspecto | La ventana | Su encabezado y sus botones |
|---|---|---|
| Predeterminado | Redondeada (18dp), con una sombra suave, opaca al 80% sobre el juego | Un título sencillo y la ×; los botones primarios se elevan y se hunden |
| Ciencia ficción | Esquinas cortadas con borde de neón y resplandor, el borde teñido por el acento | Marcas de foco en forma de corchete |
| Medieval | Un marco forjado — hierro y cuero, o pergamino — con remaches y ornamentos de esquina | Títulos en Cinzel |
| Material 3 | Esquinas de 28dp, relleno de 24dp, totalmente opaca | Un titular de 24sp en Roboto; botones en píldora de 40dp dentro de un área táctil de 48dp |
| Infantil | Una tarjeta de caramelo de gelatina con una sombra suave, totalmente opaca | Una cinta de título soleada (menta de noche), un botón de cerrar redondo de caramelo, botones que se aplastan al pulsarlos |
| Cómic | Un contorno grueso de tinta y una sombra dura, totalmente opaca | Un rótulo de título amarillo, un botón de cerrar redondo entintado, botones cuya sombra desaparece al pulsarlos |
| Arcade | Un tablero grueso — un marco de color alrededor de un fondo pálido —, totalmente opaco | Un estandarte de título dorado con un título blanco con contorno de tinta, una tecla de cerrar roja y redonda, teclas pintadas que saltan al pulsarlas |
Vestir una ventana emergente desde un skin
Un skin decide las partes a las que un Theme no llega. Estos son los métodos que llaman las ventanas emergentes — sobrescríbelos en tu propia
subclase de GoSkin; por omisión no hacen nada.
| Método | Qué viste |
|---|---|
title_plate_box() · title_plate_ink() · title_plate_min_size() | Una placa detrás del título de una ventana. El título se centra sobre ella y se encoge, hasta el tamaño mínimo (15), para caber en una sola línea. |
dress_close_button(button) | La × de la ventana — el aspecto infantil la convierte en un caramelo redondo; el de cómic, en un disco entintado, y el arcade, en una tecla roja y redonda. |
dress_title(label) | La etiqueta del título de la ventana — el aspecto arcade rodea de tinta su título blanco. |
press_feedback(control) | Se ejecuta en el momento en que un dedo cae sobre un botón creado por gohud — el aspecto infantil lo aplasta, y el arcade lo hace saltar. |
floating_box() · notice_box() · drawer_box() · banner_box() · overlay_box() | Las caras de las tarjetas de pregunta y de las tarjetas del recorrido, de los snackbars y los avisos, del panel lateral, de los banners y de la consola. |
coach_ring_box() · draw_coach_pointer() | El anillo y el puntero del recorrido. |
Cuánto se transparenta
Cada tipo de ventana emergente sigue su propia opacidad, así que una tarjeta del HUD puede quedar ligera mientras un diálogo se mantiene sólido: las ventanas, las hojas, los diálogos,
los popovers y el combobox siguen panel_alpha; el panel lateral y la tarjeta del recorrido, card_alpha; las tarjetas de pregunta y la
consola, hud_alpha; los snackbars y los avisos, notice_alpha; los menús, popup_alpha. Material e infantil
los vuelven todos opacos; cómic también, salvo las tarjetas de pregunta y la consola, al 92%, y arcade, salvo esas dos, al 94%. Sobrescribe un tipo con GoConfig.container_alpha_overrides, todos a la vez con
container_alpha (una proporción — 0.95), o una sola ventana con su alpha. Solo se desvanece la cara; el texto
nunca se transparenta. La opacidad en detalle →
Ajustes que dan forma a todas las ventanas emergentes
Campos de GoConfig (GoUi.config). Algunos se copian cuando se crea una ventana, así que asígnalos
antes de construir la interfaz — o llama a GoUi.refresh() después de cambiarlos.
| Campo | Por omisión | Qué hace |
|---|---|---|
surface_max_width · surface_max_height | 480 · 700 | El tamaño máximo que alcanza una ventana. |
surface_height_ratio · surface_max_height_ratio | 0.68 · 0.72 | La altura habitual y el tope (también el límite de arrastre). |
surface_fit_max_height_ratio | 0.94 | Cuánto puede crecer una tarjeta centrada ajustada a su contenido antes de desplazarse. |
surface_width_ratio_portrait · …_landscape | 0.94 · 0.72 | El ancho como fracción del espacio. |
dismiss_on_scrim | false | Un toque en el velo cierra ventanas y diálogos. Se copia cuando se crea la ventana. |
surface_fade_in · fade_seconds | false · 0.14 | Hacer aparecer las ventanas con un fundido, y a qué velocidad. |
close_on_back | true | Escape, ui_cancel y el botón Atrás de Android cierran la ventana de más arriba. |
close_button_visual | 36 | El tamaño con el que se ve la × — su área táctil se queda en 48dp. |
suppress_pointer_focus_ring | true | Sin anillo de foco cuando una ventana se abre con el tacto o con el ratón. |
container_alpha · container_alpha_overrides | -1 · {} | La opacidad de todas las caras, o por tipo (GoTheme.BOX_*). |
reduce_motion | false | Sin fundidos, deslizamientos, pulsos ni aplastamientos. |
min_touch_size | 48 | La altura de los botones de las tarjetas de pregunta y de las marcas guía. |
respect_safe_area | true | Mantiene las ventanas lejos de la muesca y de la barra de gestos. |
text_keys · text_overrides | — | Las palabras propias del kit: cerrar, atrás, confirmar, cancelar, siguiente, hecho, saltar, buscar, vacío… |
sound_cues · respuesta háptica | — | Lo que GoFeedback reproduce al abrir, cerrar, confirmar, cancelar y tocar. |
Recetas
Copia, pega, cambia las palabras. Cada llamada de esta sección está comprobada contra el código fuente.
Una compra con el precio dentro
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)
Una ventana de ajustes con Guardar fijo
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)
Un formulario que dice qué está mal
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())
Una fecha en una hoja
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)
Un menú de pausa
func pause() -> void:
get_tree().paused = true
var layer := CanvasLayer.new()
layer.layer = 50
layer.process_mode = Node.PROCESS_MODE_ALWAYS # sigue funcionando mientras el juego está en pausa
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)
Un juego que espera a las ventanas emergentes
func _unhandled_input(event: InputEvent) -> void:
if GoSurface.is_any_open() or drawer.is_open():
return
handle_gameplay(event)
Trampas
- Un
GoSurfacenunca se libera solo. Conectaclose_requested, o la × no hará nada. - Las opciones asignadas después de
add_child—scrim_transparent,show_header,resizable,scroll_body, ylayer_indexymax_widthdeGoDialogs— no se aplican. - Un segundo
confirm()devuelvefalsede inmediato. Activaqueue_when_busysi pueden coincidir dos preguntas. - Intro confirma. OK tiene el foco, también en los diálogos destructivos.
- Atrás en una hoja cierra la hoja, no la subpágina.
- Escape no cierra un panel lateral, y
is_any_open()no lo ve. - Un popover o una lista de combobox bloquean el juego hasta que se cierran, incluso con un velo transparente — y un popover con
dismissable = falsey sin título no tiene nada en pantalla que lo cierre. - Una tarjeta de pregunta empieza oculta y no tiene tiempo límite.
- Un snackbar con
seconds: 0y sin botón no se va nunca. Y, por omisión, recoge los toques sobre su tarjeta. - Una pulsación larga que nadie conoce es una función que nadie usa. Muestra también un
⋯. - Un árbol en pausa pausa tu ventana emergente. Usa
PROCESS_MODE_ALWAYSen su capa. - Un aviso sin ancho se encoge hasta su mínimo — usa
preferred_width(). - Nunca preguntes en un snackbar, nunca informes en un diálogo. Una pregunta en un snackbar desaparece si se ignora; una noticia no debería detener el juego.