Як вибрати потрібне спливне вікно
Спливне вікно перериває гру. Перш ніж вибирати, дайте відповідь на одне запитання: чи мусить гравець відповісти, перш ніж гра продовжиться? Якщо так — блокуйте. Якщо ні — ніколи не блокуйте: картка, що зупиняє гру заради новини, про яку гравець не просив, — найшвидший спосіб змусити його закрити вашу гру.
- Запитання, на яке потрібна відповідь — купити, видалити, вийти, перезаписати →
GoDialogs.confirm(). Те, що треба просто взяти до відома →alert(). Один із кількох варіантів →choose(). - Окремий екран — налаштування, картка персонажа, товар у крамниці →
GoSurfaceпосередині екрана. - Довгий список або сторінка керування — торба, пошта, друзі →
GoSheetзнизу на телефоні,GoDrawerзбоку на широкому екрані. - Щось про щойно натиснутий елемент — характеристики предмета, дії з ним →
GoPopoverабоGoContextMenuпоруч із ним. - Новини, за яких гра йде далі — «Збережено», «Викинуто», «Вас запросили» →
GoSnackbar,GoNoticeабоGoPromptCard. Вони ніколи не забирають екран. - Навчання керування →
GoCoachMark. Чити на час розробки →GoConsole.
Усі спливні вікна в одній таблиці
| Вікно | Зупиняє гру | «Назад» / Escape | Дотик зовні | Для чого |
|---|---|---|---|---|
GoDialogs confirm · alert | Так | Закриває — confirm відповідає false, alert повертається | Закриває лише з dismiss_on_scrim | Запитання, на які потрібна відповідь |
GoDialogs.choose | Так | Закриває — відповідає -1 | Закриває лише з dismiss_on_scrim | Вибір одного з кількох |
GoSurface | Так | Просить закрити найверхнє | Просить закрити з dismiss_on_scrim | Ваші власні вікна |
GoSheet | Так | Закриває весь аркуш | Закриває | Торба, список крамниці, пошта |
GoDrawer | Так | Лише «Назад» на Android | Закриває | Бічний список на широкому екрані |
GoPopover | Доки не закриється | Закриває | Закриває — і далі не проходить | Опис предметів і навичок |
GoCombobox — список | Доки не закриється | Закриває | Закриває | Список вибору з пошуком |
GoContextMenu · випадне меню · список вибору | Меню рушія | Меню рушія | Закриває | Дії, короткі списки |
GoSnackbar | Ні | — | Доходить до гри | «Збережено» · «Скасувати» · «Повторити» |
GoPromptCard | Ні | — | Доходить до гри | Запрошення, пропозиція обміну |
GoNotice | Ніколи | — | Доходить до гри | Незмінний рядок у HUD |
GoBanner | Ні — він частина сторінки | — | — | «Немає мережі. Повторити» |
GoCoachMark | Лише своєю карткою | Завершує тур | Доходить до гри | Тур під час першого запуску |
GoConsole | Лише своєю панеллю | Закривається клавішею Escape | Доходить до гри | Команди зневадження |
На якому шарі відкривається кожне вікно і чому саме в такому порядку — у розділі Шари, «Назад» і ввід.

GoDialogs
GoSurface
GoSheet
GoDrawer
GoPopover
GoContextMenu
GoSnackbar
GoPromptCard
GoCoachMark
GoConsoleКожен знімок на цій сторінці — та сама маленька гра: здоров'я й мана ліворуч угорі, монети праворуч угорі, стик і три швидкі
слоти внизу — у пресеті default_dark, з одним відкритим поверх неї спливним вікном. Те саме
вікно в усіх чотирнадцяти виглядах →
З чого складається спливне вікно
Майже кожне блокувальне спливне вікно в gohud — діалоги, аркуші, приколені картки, список комбобокса та ваші власні
вікна — це один вузол, GoSurface. Розберіться в його частинах один раз — і ви знатимете їх усі.

| Частина | Що це | Як до неї дістатися |
|---|---|---|
| Шар | CanvasLayer, на якому живе вікно. Його номер вирішує, що вікно перекриває. | Ваш — для GoSurface; в інших вбудований (таблиця) |
| Затемнення | Завіса над усім, що позаду. Вона ловить дотики повз картку. | scrim_transparent, dismiss_on_scrim, колір scrim із теми |
| Картка | Саме вікно — лицьова панель із теми: заокруглена, зрізана, кована чи желейна, залежно від вигляду. | card, alpha |
| Шапка | Необов'язкова кнопка «Назад», заголовок (щонайбільше два рядки, далі «…»), кнопка закриття. | set_title() · set_title_key() · set_back() · close_enabled · show_header |
| Панель інструментів | Закріплений рядок під шапкою — поле пошуку, чипи фільтрів. Прихована, доки ви нею не скористаєтеся. | toolbar |
| Тіло | Частина, що прокручується. Вміст кладіть сюди. | body · scroll_body · clear() |
| Рядок стану | Закріплений рядок між тілом і підвалом — «Ім'я не пройшло перевірку», «Збереження…». | set_status_text() · set_status_key() · clear_status() |
| Підвал | Закріплені кнопки внизу. Прихований, доки ви ним не скористаєтеся. | footer |
toolbar, рядком стану та footer — вони лишаються на місці, поки тіло прокручується.
Діалоги, на які ви робите await
GoDialogs питає й чекає. Один вузол, три запитання — і відповідь повертається в тому самому рядку, тож
код читається в тому самому порядку, у якому все відбувається з гравцем.
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) — незворотна дія
alert()
choose()
ActionLayout.HORIZONTALТри запитання
confirm(title, body, ok_text := "", cancel_text := "", extra := "",
args := {}, destructive := false) -> bool
alert(title, body, ok_text := "", extra := "", args := {}) # зробіть await, щоб дочекатися
choose(title, options: Array, cancel_text := "", translate := false) -> int
confirm_key(…) · alert_key(…) # ключі перекладу
| Виклик | Повертає | Для чого |
|---|---|---|
confirm() | true або false | Так чи ні — купити, видалити, вийти, перезаписати. |
alert() | нічого — зробіть await, щоб дочекатися | Те, що гравець мусить побачити: нагорода, втрачене з'єднання. |
choose() | індекс варіанта або -1 | Один із кількох. Варіант — це String або {text, icon, danger, subtitle}. |
confirm_key() · alert_key() | як вище | Те саме з ключами перекладу — слова йдуть за зміною мови, навіть поки діалог відкритий. |
Порожні написи кнопок замінюються власними словами набору (confirm, cancel у GoConfig.text_keys).
extra додається до тіла з нового рядка. args заповнює {placeholders} у заголовку
і в тілі: confirm("Buy {item}?", "It costs {price} gold.", "Buy", "", "", {"item": name, "price": 300}).
Кожен вихід має відповідь
| Гравець… | confirm | alert | choose |
|---|---|---|---|
| натискає OK / варіант | true | повертається | індекс варіанта |
| натискає «Скасувати» | false | — | -1 |
| натискає ×, Escape або «Назад» на Android | false | повертається | -1 |
торкається затемнення (лише з dismiss_on_scrim) | false | повертається | -1 |
вузол GoDialogs покидає дерево | false | повертається | -1 |
Тож код після await виконується завжди — жодна відповідь не зависає, навіть коли сцена змінюється просто під діалогом.
Незворотні дії
Передайте destructive = true для того, чого не можна скасувати. Кнопка OK стає залитою кнопкою
Tone.DANGER_SOLID — біле на червоному в кожному вигляді, тоді як підфарбованому напису небезпеки у світлій темі довелося б
темніти майже до чорного, щоб лишатися читабельним. Тон скидається при кожному виклику, бо вікно використовується повторно.
if await dialogs.confirm("Delete this save?", "Slot 2 — Aria, level 12. This cannot be undone.",
"Delete", "Keep it", "", {}, true):
delete_save(2)
Розкладка кнопок
action_layout | Що робить |
|---|---|
VERTICAL (типово) | Кнопки одна під одною, на всю ширину. Безпечно для будь-якої мови — довгий німецький чи російський напис ніколи не стискається. |
HORIZONTAL | Один рядок, кожній кнопці по половині ширини. |
AUTO | Один рядок лише тоді, коли обидва написи вміщаються в половину картки одним рядком; інакше стовпчиком. Вирішується знову при зміні мови чи розміру. |
action_gap — проміжок між кнопками, body_gap — проміжок над ними; зробіть його ширшим за проміжок між
кнопками, щоб запитання й вибір читалися як дві частини. Лише для одного діалогу викличте
set_next_action_layout() перед його відкриттям. Єдина кнопка сповіщення завжди займає всю ширину.
По одному
Другий confirm(), поки відкритий перший, одразу повертає false — діалог ніколи не ляже на діалог
випадково. Увімкніть queue_when_busy, і він натомість дочекається своєї черги; alert() чекає завжди.
queue_limit (8) обмежує чергу, а в разі переповнення найстаріший запит отримує відповідь false.
pending() рахує запити, що чекають, а clear_pending() відповідає їм усім false.
choose() ніколи не стає в чергу: якщо діалог уже відкритий, він повертає -1.
Телефон і комп'ютер
choose() відкривається на Android та iOS як аркуш знизу — там, де великий палець, — а на комп'ютері як картка
посередині. Підтвердження обмежене шириною max_width (420dp) і стоїть по центру. Задавайте layer_index,
max_width та інші експортовані властивості до add_child: вони застосовуються, коли вузол входить у дерево.
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 — ваше власне вікно
Коли спливне вікно — більше ніж запитання (налаштування, картка персонажа, товар для купівлі), будуйте його на
GoSurface. Безпечна зона, клавіатура, закріплені рядки, прокрутка, «Назад», фокус і зміна розміру вже зроблені.
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.
Сховати чи звільнити вікно — робота екрана-власника: лише він знає, що означає «закрити» — зберегти, відкинути чи спершу запитати.
Три розміщення

CENTER — підтвердження, налаштування
BOTTOM — списки, сторінки керування
ANCHOR — поруч із контролом
GoDatePicker у вікні CENTERANCHOR потребує anchor_control. Картка стає праворуч від нього, якщо вміщається anchor_min_width,
інакше — на ширший бік, інакше — всередину екрана, і відкривається вгору, коли згори більше місця. Вона стежить за рухомою прив'язкою
щокадру. Розмір задають anchor_width (320) і anchor_max_height (520).
Наскільки воно виростає
| Міра | Правило | Чим змінити |
|---|---|---|
| Місце | Безпечна зона мінус клавіатура мінус поле екрана (16dp, але не більше 10% короткої сторони). | — |
| Ширина | 94% місця в портретній орієнтації, 72% в альбомній, не більше 480dp. | max_width · GoConfig.surface_width_ratio_* |
| Висота | За висотою вмісту (fit_content), до 68% місця, зі стелею 72%. Картка по центру без зміни розміру може вирости до 94%, щоб обійтися без прокрутки. | height_ratio · max_height_ratio · max_height |
| Щільність | Щільна розкладка, коли задано compact, екран нижчий за 420dp (телефон, повернутий набік) або вміст не вміщається: менше відступів, менші проміжки, менший заголовок. Вікно вимірює обидва варіанти й лишає просторіший із тих, що вміщаються, — без тремтіння. | compact |
| Зміна розміру | З resizable заголовок стає ручкою: тягніть його між 30% і стелею. height_changed(ratio) повідомляє результат. | resizable |
Частка понад стелю урізається, з одним попередженням у зневаджувальній збірці. Клавіатура підіймає картку й прокручує поле з фокусом у видиму зону; безпечна зона тримає її подалі від вирізу та смуги жестів на Android та iOS.
Властивості
| Властивість | Типово | Що робить |
|---|---|---|
placement | CENTER | CENTER, BOTTOM або ANCHOR |
dismiss_on_scrim | GoConfig.dismiss_on_scrim (false) | Дотик до затемнення просить закрити вікно. Перетягування, що почалося всередині картки, — ні. |
scrim_transparent | false | Лишити гру видимою — затемнення все одно ловить дотики зовні. |
close_enabled | true | Показати × і дозволити Escape та «Назад» просити про закриття. |
show_header · scroll_body · resizable | true · true · false | Рядок шапки, тіло з прокруткою, ручка перетягування. |
fit_content · compact | true · false | Підганяти розмір під вміст; починати зі щільної розкладки. |
fade_in | GoConfig.surface_fade_in (false) | Плавно проявляти картку під час відкриття. З reduce_motion — миттєво. |
alpha | -1 (з теми) | Непрозорість лицьової панелі картки, часткою. Прозорішає лише панель — ніколи не текст. |
initial_focus | немає | Контрол, що отримує фокус під час відкриття. |
add_child: scrim_transparent, show_header, resizable і
scroll_body застосовуються, коли вікно входить у дерево. dismiss_on_scrim, close_enabled,
fade_in та initial_focus читаються наживо.
Сигнали та виклики
| Ім'я | Що це |
|---|---|
close_requested | ×, Escape, «Назад» на Android або дотик до затемнення — а також request_close(). Випромінюється лише найверхнім вікном. |
back_requested · set_back(action) | Кнопка «Назад» у шапці — для вікна зі сторінками. Порожній Callable її ховає. |
height_changed(ratio) | Після перетягування ручки. |
is_top() · GoSurface.is_any_open() | Чи це вікно отримує «Назад» — і чи відкрите взагалі хоч одне вікно. |
relayout() | Розкласти знову просто зараз. Зміна властивості розміру вже робить це за вас. |
Аркуші та бічні панелі
Список, якому потрібне місце, підіймається знизу на телефоні, який тримають вертикально, — там, де великий палець, — і виїжджає збоку на планшеті чи комп'ютері, де аркуш затулив би гру.

GoSheet
GoDrawer · LEFT
GoDrawer · RIGHTGoSheet — сторінки знизу
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))
- Це
CanvasLayer(шар 10), у якому лежитьBOTTOM-GoSurfaceзі змінним розміром, — тож він сидить над HUD, а гравець може витягнути його вище. open(title)починає нову сторінку: очищає тіло, панель інструментів і кнопку «Назад» та звільняє те, що додавadd_footer(). Викличте його знову, щоб показати іншу сторінку в тому самому аркуші;set_back()показує кнопку «Назад» для шляху додому.open_key()приймає ключ перекладу.close()ховає аркуш — він не звільняється, тож наступнийopen()миттєвий. Коли це стається, повідомляютьclosedіpage_changed.height_ratio(0.6) — висота під час відкриття;max_height_ratioпіднімає стелю;dismissable(true) закриває аркуш дотиком до затемнення.
set_back(), — на сторінку назад веде лише
екранна кнопка «Назад». Якщо «Назад» має повертати на крок, обробіть close_requested у sheet.surface самі.
GoDrawer — збоку
var drawer := GoDrawer.new()
drawer.side = GoDrawer.Side.LEFT
drawer.follow_text_direction = true # бік початку: ліворуч, а в арабській — праворуч
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")
- Шар 80. Ширина — 42% екрана (
width_ratio), міжmin_width(320dp) іmax_width(420dp): на телефоні панель затуляє більшу частину екрана, тому там краще вибрати аркуш. - Панель дістає до краю екрана; всередину безпечної зони відступає лише її вміст, тож заокруглений кут ніколи не обріже перший рядок.
LEFTозначає «ліворуч на екрані» навіть в арабській. Вмикайтеfollow_text_direction, коли йдеться саме про «бік початку»;effective_side()скаже, куди панель зрештою стала.open(title)зберігає тіло — заповніть його один раз.set_title("")ховає шапку.motion_seconds(0.2) задає виїзд;reduce_motionробить його миттєвим.
drawer.close() із власного прив'язування клавіш — і зважте, що GoSurface.is_any_open() бічну
панель не рахує; питайте drawer.is_open().
Картки та меню поруч із контролом
Те, що гравець щойно натиснув, заслуговує на відповідь просто біля пальця, а не посередині екрана.

GoPopover
GoContextMenu
GoStyle.dropdown()
GoStyle.select()
GoComboboxGoPopover — картка біля натиснутого елемента
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}))
Поверхня ANCHOR, у якій шар, прив'язка та прибирання вже з'єднані, — ті десять рядків, що їх кожна гра пише знову
й знову. По одній: відкриття другої картки закриває першу, бо складені стосом картки ховають екран, і вже годі сказати, яка
з них якому слоту належить. Затемнення прозоре — картка, відкрита, щоб порівняти спорядження, не повинна це спорядження
притемнювати, — але воно все одно ловить перший дотик зовні, який закриває картку й далі не проходить.
| Параметр | Типово | Що робить |
|---|---|---|
title | "" | Шапка з ×. Порожньо — шапки немає. |
width · max_height | 320 · 520 | Розмір картки. |
dismissable | true | Дотик зовні її закриває. |
compact · alpha · translate | false · -1 · false | Щільні відступи, непрозорість лицьової панелі, заголовок як ключ перекладу. |
layer | 95 | CanvasLayer, на якому вона відкривається. |
GoPopover.close() і GoPopover.is_open() — статичні. open() повертає поверхню — або
null, коли прив'язки немає в дереві, тож перевірте результат, перш ніж робити await shown.close_requested.
dismissable = false і без заголовка на екрані немає нічого, що її закриє, — лише Escape, «Назад» чи ваш власний виклик.
GoContextMenu — довге натискання та клац правою
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))
- Клац правою відкриває меню одразу. Палець, утримуваний пів секунди (
HOLD_SECONDS), теж його відкриває; зсув більш ніж на 12dp скасовує — тож прокрутка списку ніколи не викидає меню. - Пункт — це
{text, action, icon, disabled, checked, separator, danger, translate}. ПередавайтеCallableзамість масиву, коли пункти залежать від того, на що натиснули; його викликають щоразу, коли меню відкривається. dangerставить «⚠ » перед текстом — меню рушія вміє фарбувати лише всі свої пункти разом.- Це
PopupMenuрушія, оформленеGoStyle.style_popup(). Відкритою поверхнею воно не вважається.
⋯
(open_at(), як вище) або один раз навчіть його коуч-міткою.
Випадні меню, списки вибору та розділена кнопка
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"])
Усі три відкривають спливне меню рушія: dropdown() — це MenuButton, що запускає дію,
select() — OptionButton, що зберігає значення, GoSplitButton — головна дія плюс меню на
стрілці (chosen(index)). Меню навмисно лишаються цілком непрозорими: меню, показане у власному вікні ОС, перетворює
напівпрозорі пікселі на чорні.
GoCombobox — список із пошуком
var friend := GoCombobox.make(friend_names, -1, "Find a friend")
friend.picked.connect(func(index: int) -> void: invite(friend_names[index]))
Після натискання він відкриває прив'язаний список на шарі 96 із прозорим затемненням. Починаючи з search_threshold (8)
пунктів з'являється поле пошуку й забирає фокус; воно шукає збіг у будь-якому місці тексту чи підказки без урахування регістру, а
порожній результат так і каже. Ширину списку задає list_width (0 = ширина кнопки, але не менше 180dp).
Спливні вікна, що ніколи не зупиняють гру
Новини, які гравець може прочитати, не перериваючи гру. Жодне з цих вікон не має затемнення; дотик будь-де, крім самої картки, іде в гру.

GoSnackbar
GoNotice
GoPromptCard
GoStyle.tooltip_node()GoSnackbar — повідомлення з «Скасувати»
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)
Ключ post() | Що робить |
|---|---|
text · title · args · translate | Повідомлення, необов'язковий рядок жирним, {placeholders}, ключі перекладу. |
tone · icon | Акцентний колір (GoTheme.SUCCESS, WARNING…) та іконка. |
actions · closable | Кнопки — рядки або {text, action} — та ×. |
seconds | Скільки він тримається. Від'ємне значення — notice_duration_ms із теми. 0 — доки його не закриють; тоді дайте йому кнопку. |
post() повертає індекс натиснутої кнопки або -1, якщо час вийшов чи снекбар закрили. Повідомлення стають
у чергу (queue_limit 4, найстаріше викидається з -1), а той самий текст, надісланий знову, поки він на екрані, лише
перезапускає таймер. Снекбар стоїть унизу (або вгорі — placement), над безпечною зоною та клавіатурою, на шарі 90 —
його видно поверх бічної панелі. З tap_to_dismiss (увімкнено) дотик до картки без кнопок закриває її; дотик повз картку
завжди доходить до гри. show_text() і show_key() — однорядкові форми.
GoNotice — рядок у 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)
Проста панель, яку ви розміщуєте самі, — у GoHudAnchor, у кутку, будь-де. Вона не бере ні вводу, ні фокуса, тож
натискання крізь неї доходить до кнопки під нею, а нове повідомлення замінює старе. Задайте їй ширину:
preferred_width(limit) повертає природну ширину короткого тексту й переносить довгий у межах limit.
set_content() показує замість тексту будь-який контрол.
GoPromptCard — запитання, яке може зачекати
var spot := GoHudAnchor.new()
spot.spot = GoHudAnchor.Spot.TOP_CENTER
spot.reserve_space = false
spot.avoid_peers = true # стати під смугами здоров'я, а не затуляти їх
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)
- Картка стартує прихованою — викличте
show(). Власного таймера в неї немає; додайте його, якщо запитання має термін. - Її кнопки її не закривають — вирішують дії. × (
set_closable(true)) випромінюєclosed. fit_width(width)задає ширину й передбачає висоту за шрифтом, тож ніщо, що розкладається відносно неї, не стрибне кадром пізніше.- Її лицьова панель іде за
hud_alpha— це частина HUD, а не вікно.
GoBanner і спливні підказки
GoBanner.make("You're offline.", [{"text": "Retry", "action": reconnect}], GoIconSet.WARNING) стоїть у самій сторінці,
над вмістом, і ніколи не зникає за таймером; кнопка закриває його, якщо в неї немає "keep": true.
GoStyle.tooltip_node(text) — те, що слід повернути з _make_custom_tooltip(): плашку малює рушій
(TooltipPanel), а текст — gohud, з перенесенням після 260dp. Підказки — для миші; ніколи не ховайте за ними те, що потрібно гравцеві.
Тури та консоль

GoCoachMark
GoConsoleGoCoachMark — тур справжніми контролами
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."}])
- Кільце навколо справжнього контрола й картка поруч. Натискання на ціль веде тур далі (її сигнал
pressedабо"signal"у кроці); «Далі» та «Пропустити» — на картці. Прихована ціль пропускається. - Картка тримається подалі від середини екрана (
avoid_center_band), від прив'язок HUD і відkeep_clear. - Поки відкрита будь-яка
GoSurface, тур ховається й чекає. «Назад» чи Escape завершують його зfinished(false). - Власного шару в нього немає — додавайте його останнім або на шар вище за HUD.
GoConsole — чити на час розробки
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()
Шар 200 — над усім, тож зневаджувати можна й поверх діалогу, — і консоль далі працює, поки дерево на паузі. Палітра підказує команди, доки ви набираєте; стрілки вгору й униз гортають історію; Escape її закриває. Вбудованої клавіші немає: призначте свою.
debug_only (увімкнено) блокує її в релізній збірці — і open(), і run(). Читерські
команди в руках гравця кінчають ігрову економіку першого ж дня.
Шари, «Назад» і ввід
Три правила вирішують, чого може торкатися гравець, поки щось відкрите: що лежить згори, кому належить «Назад» і хто отримує дотик.
Що лежить згори
| Шар | Що там лежить | Чим задається |
|---|---|---|
| 5 | Ваш HUD (на ваш вибір) | — |
| 10 | GoSheet | CanvasLayer.layer |
| ~50 | Ваші власні вікна GoSurface, меню паузи | ваш CanvasLayer |
| 80 | GoDrawer | CanvasLayer.layer |
| 90 | GoSnackbar | layer_index |
| 95 | GoPopover | параметр "layer" |
| 96 | список GoCombobox | — |
| 100 | GoDialogs | layer_index |
| 200 | GoConsole | layer_index |
Порядок навмисний: снекбар повідомляє про щойно сталося, тож читається поверх бічної панелі; приколена картка належить до щойно натиснутого, тож лежить над ним; діалог ставить запитання й накриває і те, і те; консоль накриває все, бо її відкривають, коли все інше пішло не так.
Кому належить «Назад»
- Відповідає лише найверхнє вікно. Серед відкритих поверхонь вікно на найвищому шарі (а за рівності — останнє в дереві)
отримує Escape, скасування на геймпаді (
ui_cancel), «Назад» на Android і дотики до затемнення. Вікна в стосі закриваються по одному, ніколи разом. - «Назад» на Android обробляється кадром пізніше, тож одне натискання ніколи не закриє двох вікон.
- Поки відкрите хоч щось, що володіє «Назад», кнопка «Назад» на Android не закриває застосунок (
GoBackPolicyрахує власників і відновлюєquit_on_go_backпісля останнього). Ваше власне спливне вікно, що перехоплює «Назад», має викликатиGoBackPolicy.acquire()іrelease()— зокрема і в_exit_tree. - Бічна панель, тур коуч-міток і форма поступаються, поки відкрита поверхня, — «Назад» спершу дістається поверхні.
GoConfig.close_on_back(увімкнено) — перемикач для поверхонь.
Хто отримує дотик
- Поверхня накриває весь екран і зупиняє ввід — прозоре затемнення теж. Поки вона відкрита, ніщо на нижчих шарах не натискається; шари над нею (снекбар) — натискаються.
- Коліщатко миші та жест щипка теж зупиняються на вікні: прокрутка за кінець списку ніколи не прокручує гру.
- Ігровий ввід, що йде не через контроли, —
_unhandled_inputдля руху, перетягування камери — має ставати на паузу:if GoSurface.is_any_open(): return. Це покриває діалоги, аркуші, приколені картки й комбобокс; бічну панель питайтеis_open()окремо. - Снекбар, сповіщення, картка-запитання й коуч-мітка беруть ввід лише на своїй картці; скрізь деінде його отримує гра.
Фокус
Вікно, відкрите вказівником, нічого не фокусує — на дотик жодне кільце не блимає. Якщо вікно відкрито з клавіатури чи геймпада,
фокус отримує кнопка закриття (або initial_focus). Поки вікно відкрите, фокус лишається всередині нього, а після
закриття повертається до контрола, який мав його раніше. Діалоги щоразу ставлять фокус на OK.
Пауза в грі
Пауза дерева ставить на паузу й спливні вікна — далі працює лише консоль. Для меню паузи задайте його шару
process_mode = PROCESS_MODE_ALWAYS; тоді вікно, його кнопки й «Назад» працюють, поки гра стоїть.
Те саме вікно в кожному вигляді
Підтвердження з початку цієї сторінки, поверх тієї самої гри, — змінився лише пресет. Код ідентичний; решту роблять тема, скін та іконки.

default_dark
default_light
scifi_dark
scifi_light
medieval_dark
medieval_light
material_light Нове
material_dark Нове
kids_light Нове
kids_dark Нове
comic_light Нове
comic_dark Нове
arcade_light Нове
arcade_dark НовеЩо змінює кожен вигляд
| Вигляд | Вікно | Його шапка й кнопки |
|---|---|---|
| Стандартний | Заокруглене (18dp), м'яка тінь, непрозорість 80% поверх гри | Простий заголовок і ×; головні кнопки підіймаються й осідають |
| Сай-фай | Зрізані кути з неоновим краєм і сяйвом, край підфарбований акцентом | Позначки фокуса у вигляді дужок |
| Середньовічний | Кована рама — залізо та шкіра або пергамент — із заклепками й кутовими оздобами | Заголовки шрифтом Cinzel |
| Material 3 | Кути 28dp, відступи 24dp, повна непрозорість | Заголовок 24sp шрифтом Roboto; кнопки-пігулки з плашкою 40dp у зоні дотику 48dp |
| Дитячий | Картка — желейна цукерка з м'якою тінню, повна непрозорість | Сонячна стрічка заголовка (вночі м'ятна), кругла кнопка закриття-цукерка, кнопки, що сплющуються від натискання |
| Комікс | Жирний чорнильний контур і жорстка тінь, повна непрозорість | Жовта плашка заголовка, кругла кнопка закриття, обведена чорнилом, кнопки, чия тінь зникає від натискання |
| Аркада | Товста дошка — кольорова рамка навколо світлої заглибини, — повна непрозорість | Золотий банер із білим заголовком у чорнильному контурі, кругла червона клавіша закриття, розфарбовані клавіші, що підскакують від натискання |
Оформити спливне вікно через скін
Скін вирішує те, куди темі не дістатися. Нижче — хуки, які викликають спливні вікна: перевизначайте їх у власному підкласі
GoSkin; типово вони нічого не роблять.
| Хук | Що оформлює |
|---|---|
title_plate_box() · title_plate_ink() · title_plate_min_size() | Плашку за заголовком вікна. Заголовок центрується на ній і зменшується — аж до мінімального розміру (15), — щоб лишитися в один рядок. |
dress_close_button(button) | × вікна — дитячий вигляд робить її круглою цукеркою, комікс — кружечком, обведеним чорнилом, аркада — круглою червоною клавішею. |
dress_title(label) | Мітка заголовка вікна — аркада обводить її білий заголовок чорнилом. |
press_feedback(control) | Спрацьовує тієї миті, коли палець торкається кнопки, створеної gohud, — дитячий вигляд її сплющує, аркада змушує її підскочити. |
floating_box() · notice_box() · drawer_box() · banner_box() · overlay_box() | Лицьові панелі карток-запитань і карток туру, снекбарів і сповіщень, бічної панелі, банерів, консолі. |
coach_ring_box() · draw_coach_pointer() | Кільце й вказівник туру. |
Наскільки прозоро
Кожен вид спливних вікон іде за своєю непрозорістю, тож картка HUD може лишатися легкою, а діалог — щільним: вікна, аркуші,
діалоги, приколені картки й комбобокс ідуть за panel_alpha; бічна панель і картка туру — за card_alpha;
картки-запитання й консоль — за hud_alpha; снекбари й сповіщення — за notice_alpha; меню — за
popup_alpha. Material і дитячий вигляд роблять їх усі непрозорими; комікс теж, окрім карток-запитань і консолі — у них 92%, і аркада, окрім тих самих двох — у них 94%. Перевизначте один вид через
GoConfig.container_alpha_overrides, усі разом — через container_alpha (частка — 0.95), а одне
вікно — через його alpha. Прозорішає лише лицьова панель; текст ніколи не просвічує. Непрозорість докладно →
Налаштування, що визначають кожне спливне вікно
Поля GoConfig (GoUi.config). Деякі копіюються під час створення вікна, тож задавайте їх
до побудови інтерфейсу — або викличте GoUi.refresh() після зміни.
| Поле | Типово | Що робить |
|---|---|---|
surface_max_width · surface_max_height | 480 · 700 | Найбільший розмір вікна. |
surface_height_ratio · surface_max_height_ratio | 0.68 · 0.72 | Звичайна висота та стеля (вона ж межа перетягування). |
surface_fit_max_height_ratio | 0.94 | Наскільки може вирости картка по центру, підігнана під вміст, перш ніж почне прокручуватися. |
surface_width_ratio_portrait · …_landscape | 0.94 · 0.72 | Ширина як частка місця. |
dismiss_on_scrim | false | Дотик до затемнення закриває вікна й діалоги. Копіюється під час створення вікна. |
surface_fade_in · fade_seconds | false · 0.14 | Плавна поява вікон і її швидкість. |
close_on_back | true | Escape, ui_cancel і «Назад» на Android закривають найверхнє вікно. |
close_button_visual | 36 | Видимий розмір × — його зона дотику лишається 48dp. |
suppress_pointer_focus_ring | true | Жодного кільця фокуса, коли вікно відкрито дотиком чи мишею. |
container_alpha · container_alpha_overrides | -1 · {} | Непрозорість усіх лицьових панелей або окремо за видами (GoTheme.BOX_*). |
reduce_motion | false | Жодних проявів, виїздів, пульсацій і сплющувань. |
min_touch_size | 48 | Висота кнопок у картках-запитаннях і коуч-мітках. |
respect_safe_area | true | Тримати вікна подалі від вирізу та смуги жестів. |
text_keys · text_overrides | — | Власні слова набору: закрити, назад, підтвердити, скасувати, далі, готово, пропустити, пошук, порожньо… |
sound_cues · вібрація | — | Що GoFeedback відтворює під час відкриття, закриття, підтвердження, скасування й дотику. |
Рецепти
Скопіюйте, вставте, змініть слова. Кожен виклик тут звірено з вихідним кодом.
Купівля з ціною в тексті
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)
Вікно налаштувань із закріпленою кнопкою «Зберегти»
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)
Форма, яка каже, що не так
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())
Дата в аркуші
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)
Меню паузи
func pause() -> void:
get_tree().paused = true
var layer := CanvasLayer.new()
layer.layer = 50
layer.process_mode = Node.PROCESS_MODE_ALWAYS # працює, поки гра на паузі
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)
Ігровий процес, що чекає на спливні вікна
func _unhandled_input(event: InputEvent) -> void:
if GoSurface.is_any_open() or drawer.is_open():
return
handle_gameplay(event)
Підводні камені
GoSurfaceніколи не звільняє себе сама. Під'єднайтеclose_requested, інакше × нічого не робить.- Параметри, задані після
add_child, —scrim_transparent,show_header,resizable,scroll_body, а також уGoDialogs—layer_indexіmax_width— не застосовуються. - Другий
confirm()одразу повертаєfalse. Увімкнітьqueue_when_busy, якщо два запитання можуть збігтися в часі. - Enter підтверджує. Фокус на OK — і в діалогах незворотних дій теж.
- «Назад» на аркуші закриває весь аркуш, а не підсторінку.
- Escape не закриває бічну панель, а
is_any_open()її не бачить. - Приколена картка чи список комбобокса блокують гру, доки не закриються, навіть із прозорим затемненням, — а в приколеної
картки з
dismissable = falseі без заголовка на екрані немає нічого, що її закриє. - Картка-запитання стартує прихованою і не має тайм-ауту.
- Снекбар із
seconds: 0і без кнопки ніколи не зникає. До того ж типово він ловить дотики на своїй картці. - Довге натискання, про яке ніхто не знає, — функція, якою ніхто не користується. Покажіть ще й
⋯. - Дерево на паузі ставить на паузу й ваше спливне вікно. Використовуйте
PROCESS_MODE_ALWAYSна його шарі. - Сповіщення без ширини стискається до найвужчого — використовуйте
preferred_width(). - Ніколи не питайте в снекбарі й не повідомляйте в діалозі. Запитання зникає, якщо його не помітити; новина не повинна зупиняти гру.