Voltar para o Blog
Quest Log

Tooltips de UI no Godot 4: do tooltip_text ao painel customizado

Painel de tooltip customizado aparecendo ao lado do cursor sobre um botao de interface no Godot

Aprenda a usar o tooltip godot nativo do Control, contornar suas limitacoes e criar um tooltip customizado com PanelContainer que segue o mouse e fica na tela.

Tooltip no jogo serve para uma coisa simples: explicar um elemento sem ocupar espaco fixo na tela. O usuario passa o mouse, le a informacao, tira o mouse e ela some. O tooltip godot nativo resolve metade dos casos com uma unica propriedade, mas para itens de inventario, habilidades ou qualquer conteudo com titulo e descricao formatados voce vai precisar de algo proprio. Neste post vamos do mais simples (o tooltip_text do Control) ate um painel customizado que segue o cursor e se mantem dentro dos limites da janela.

Tooltips de UI no Godot 4

A boa noticia e que o Godot ja tem um sistema de tooltip embutido em todo Control. A noticia menos boa e que ele e limitado a texto puro por padrao, e a aparencia depende do tema. Quando isso nao basta, da para sobrescrever o metodo que gera o tooltip ou construir o seu do zero. Vamos ver as tres abordagens em ordem de complexidade.

O tooltip nativo com tooltip_text

Qualquer no que herda de Control (Button, TextureRect, Panel, etc.) tem a propriedade tooltip_text. Voce define o texto no Inspector ou por codigo, e o Godot cuida do resto: o tooltip aparece depois de um pequeno atraso quando o mouse para sobre o no, e some quando o mouse sai.

extends Button

func _ready() -> void:
    tooltip_text = "Restaura 30 de vida"

E so isso para o caso basico. O atraso antes de aparecer e controlado por uma configuracao do projeto em gui/timers/tooltip_delay_sec, caso voce queira que apareca mais rapido ou mais devagar.

As limitacoes aparecem cedo. O texto e uma string unica, sem cores diferentes para titulo e corpo, sem icones, sem barra de progresso. A largura tambem nao e trivial de controlar. Se o conteudo do seu jogo e simples (um botao de menu que diz o que faz), tooltip_text e a escolha certa e voce nao deveria reinventar nada. Para itens de RPG com raridade, atributos e descricao, ele fica pequeno.

Sobrescrevendo _make_custom_tooltip

Antes de jogar fora o sistema nativo, vale conhecer um meio-termo. O Control expoe o metodo virtual _make_custom_tooltip, que voce pode sobrescrever para retornar qualquer no Control em vez do tooltip de texto padrao. O Godot ainda cuida de mostrar, esconder e posicionar, mas o conteudo passa a ser uma cena sua.

extends Button

@export var tooltip_scene: PackedScene

func _make_custom_tooltip(for_text: String) -> Object:
    var tip := tooltip_scene.instantiate()
    tip.get_node("Titulo").text = "Pocao de Vida"
    tip.get_node("Descricao").text = for_text
    return tip

A vantagem e que voce ganha conteudo rico (um PanelContainer com VBoxContainer, varios labels, icones) sem perder a logica de delay e de esconder automaticamente. O parametro for_text recebe o que estiver em tooltip_text, entao da para reaproveitar essa string como descricao.

O limite dessa abordagem e o controle fino de posicao. O Godot decide onde colocar o tooltip, e voce nao tem chamadas de "mostrar agora" ou "seguir o mouse". Para a maioria dos casos isso e suficiente e custa menos codigo. Quando voce precisa que o painel grude no cursor ou apareca em resposta a eventos que nao sejam o hover padrao, e hora de construir o tooltip por conta propria.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

Tooltip customizado com PanelContainer

Aqui assumimos o controle total. A ideia: um PanelContainer que fica escondido, e que mostramos e posicionamos manualmente quando o mouse entra em algum elemento. Comece com uma cena de tooltip separada. A raiz e um PanelContainer chamado Tooltip, com um VBoxContainer dentro contendo dois labels: Titulo e Descricao.

O script da cena do tooltip cuida de preencher o conteudo e de se posicionar:

extends PanelContainer

@onready var titulo: Label = $VBox/Titulo
@onready var descricao: Label = $VBox/Descricao

func _ready() -> void:
    hide()
    # Ignora o mouse para nao roubar eventos de hover dos elementos embaixo.
    mouse_filter = Control.MOUSE_FILTER_IGNORE

func mostrar(novo_titulo: String, nova_descricao: String) -> void:
    titulo.text = novo_titulo
    descricao.text = nova_descricao
    show()

func esconder() -> void:
    hide()

Definir mouse_filter como MOUSE_FILTER_IGNORE e importante. Sem isso, o proprio tooltip passa por baixo do cursor e pode disparar mouse_exited no elemento de origem, criando aquele efeito de piscar sem fim.

Seguindo o mouse e ficando dentro da tela

A parte que diferencia um tooltip bom de um amador e o posicionamento. Queremos que ele apareca perto do cursor, mas nunca cortado pela borda da janela. O calculo: pega a posicao do mouse, soma um pequeno deslocamento, e depois clampa o resultado para que o painel inteiro caiba na area visivel.

Para saber o tamanho real do painel, usamos get_combined_minimum_size, que considera o conteudo dos labels. O tamanho da janela vem de get_viewport_rect.

extends PanelContainer

const DESLOCAMENTO := Vector2(16, 16)

func _process(_delta: float) -> void:
    if not visible:
        return
    var mouse := get_viewport().get_mouse_position()
    var tamanho := get_combined_minimum_size()
    var tela := get_viewport_rect().size
    var alvo := mouse + DESLOCAMENTO
    # Mantem o painel inteiro dentro da tela.
    alvo.x = clampf(alvo.x, 0.0, tela.x - tamanho.x)
    alvo.y = clampf(alvo.y, 0.0, tela.y - tamanho.y)
    global_position = alvo

Rodar isso em _process so quando o tooltip esta visivel mantem o custo baixo. Se preferir nao usar _process, da para atualizar a posicao apenas no evento de movimento do mouse, mas a versao com guarda de visible ja e barata o bastante para a grande maioria dos projetos.

Conectando aos sinais mouse_entered e mouse_exited

Agora ligamos os elementos que disparam o tooltip. Cada slot, botao ou icone que deve mostrar informacao conecta seus sinais mouse_entered e mouse_exited a funcoes que chamam o tooltip. O ideal e ter uma unica instancia de tooltip na cena (por exemplo num CanvasLayer no topo) e reusa-la para todos os elementos.

Um exemplo com um slot de inventario que guarda seus proprios dados:

extends TextureRect

@export var item_titulo: String = "Espada de Ferro"
@export var item_descricao: String = "Dano 12. Requer nivel 3."

@onready var tooltip := get_tree().get_first_node_in_group("tooltip")

func _ready() -> void:
    mouse_entered.connect(_ao_entrar)
    mouse_exited.connect(_ao_sair)

func _ao_entrar() -> void:
    if tooltip:
        tooltip.mostrar(item_titulo, item_descricao)

func _ao_sair() -> void:
    if tooltip:
        tooltip.esconder()

Para o get_first_node_in_group funcionar, marque a cena do tooltip com o grupo tooltip no editor (aba Node, depois Groups). Assim qualquer slot encontra a instancia compartilhada sem precisar de uma referencia manual arrastada no Inspector. Se voce ja organiza seus dados de item num recurso, vale ler como estruturar isso no guia de sistema de inventario para RPG, porque o mesmo recurso que descreve o item pode alimentar o titulo e a descricao do tooltip.

Conteudo rico: titulo, descricao e mais

Com a estrutura de PanelContainer mais VBoxContainer, expandir o conteudo e questao de adicionar nos. Um tooltip de item de RPG costuma ter:

  • Um Label de titulo, com a cor variando pela raridade do item.
  • Um Label ou RichTextLabel de descricao.
  • Uma linha de atributos (dano, defesa, peso).
  • Opcionalmente um TextureRect com o icone.

Usar RichTextLabel com BBCode ativado abre espaco para cores e negrito dentro do mesmo bloco de texto. A funcao mostrar so precisa aceitar mais parametros:

extends PanelContainer

@onready var titulo: Label = $VBox/Titulo
@onready var corpo: RichTextLabel = $VBox/Corpo

func mostrar_item(nome: String, cor: Color, texto_bbcode: String) -> void:
    titulo.text = nome
    titulo.add_theme_color_override("font_color", cor)
    corpo.text = texto_bbcode
    show()

Com bbcode_enabled ligado no RichTextLabel, voce pode passar algo como "[b]Dano[/b] 12\n[color=gray]Requer nivel 3[/color]" e a formatacao aparece direto. Isso mantem a logica de exibicao simples enquanto o conteudo fica flexivel.

Cuidados de layout e responsividade

Tooltip e um elemento de UI como qualquer outro, e sofre dos mesmos problemas em telas diferentes. Tres pontos merecem atencao.

Primeiro, o tamanho minimo dos labels. Se a descricao for longa, defina autowrap_mode no label para que o texto quebre em vez de esticar o painel para fora da tela. Combine isso com um custom_minimum_size no VBoxContainer para garantir uma largura confortavel.

Segundo, escala. Em projetos que usam o modo de esticar canvas_items, o tooltip escala junto com o resto da UI, o que normalmente e o que voce quer. Se a posicao parecer errada em outra resolucao, confira se voce esta usando coordenadas do viewport e nao da janela do sistema operacional. O tema de adaptar a interface a varias resolucoes esta detalhado em UI responsiva no Godot.

Terceiro, ordem de desenho. O tooltip precisa aparecer por cima de tudo. Colocar a instancia num CanvasLayer com layer alto resolve isso sem depender da ordem dos nos na arvore principal.

Qual abordagem escolher

Nao existe uma resposta unica, e cada nivel tem seu lugar.

Use tooltip_text quando o conteudo e uma frase curta e voce nao se importa com a aparencia padrao do tema. E o menor esforco possivel.

Use _make_custom_tooltip quando precisa de conteudo formatado mas esta satisfeito com o delay e o posicionamento automaticos do Godot. Voce ganha layout rico sem escrever logica de mostrar e esconder.

Construa o tooltip do zero com PanelContainer quando precisa de controle: seguir o cursor, reagir a eventos especificos, animar a entrada, ou reusar uma unica instancia para dezenas de slots. Foi o caso que detalhamos aqui, e o que mais aparece em inventarios e arvores de habilidade.

O ponto de partida pratico e nunca pular etapas. Comece pelo tooltip_text, suba para _make_custom_tooltip se o visual nao bastar, e so vá para o painel manual quando o comportamento exigir. Cada degrau resolve um conjunto de casos, e empilhar complexidade sem necessidade so deixa o codigo mais dificil de manter. Com a base de seguir o mouse, clampar na tela e ignorar o filtro de mouse, voce cobre praticamente qualquer tooltip que um jogo 2D vai precisar.