Keeping a tooltip (PanelContainer) on screen

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By maitake_maitake

I am attempting to achieve an effect similar to Terraria’s inventory system. In that game, when you mouse over an item, it will display a tooltip describing the item to the bottom right of your cursor. If the item is located near the edge of a screen, the tooltip will display in a way so that it never leaves the screen. Example

In Godot, I have something similar working as far as displaying information and moving to the mouse. But when it reaches the edge of the screen, it gets cut off.

func _process(_delta):
 cursor_pos = get_global_mouse_position() + Vector2(14,18)
 set_position(cursor_pos)

This is the code I use for a PanelContainer. How would I change this to keep the node from leaving the screen?

:bust_in_silhouette: Reply From: rakkarage
func _cameraUpdate() -> void:
	var map := _mapBounds()
	var world := _worldBounds().grow(-_back.cell_size.x)
	if not world.intersects(map):
		_snapCameraBy(_constrainRect(world, map))

func _constrainRect(world: Rect2, map: Rect2) -> Vector2:
	return _constrain(world.position, world.end, map.position, map.end)
func _constrain(minWorld: Vector2, maxWorld: Vector2, minMap: Vector2, maxMap: Vector2) -> Vector2:
	var delta = Vector2.ZERO
	if minWorld.x > minMap.x: delta.x += minMap.x - minWorld.x
	if maxWorld.x < maxMap.x: delta.x -= maxWorld.x - maxMap.x
	if minWorld.y > minMap.y: delta.y += minMap.y - minWorld.y
	if maxWorld.y < maxMap.y: delta.y -= maxWorld.y - maxMap.y
	return delta

i use this to keep a tilemap on screen @ GitHub - rakkarage/PixelLevel: Pixel-perfect dungeon and environment sprites.

once you got 2 bounds compare the four corners to generate a delta that would put it back on screen or where you want it.
here i test if all 4 corner are within 1 tile of being offscreen so as soon as less the one tile is visible in any direction it snaps back on screen but you could instead test if any point is offscreen to generate a delta

grow or shrink the rects before test to adjust

also here is some old objective-c code i made that does this
it would pop up a little window with an arrow pointing to the tile
is is bad language and bad code but might help :slight_smile:
i guess i am just testing if room with arrow else shifting it around
but ya way easier without callout arrows

these arrows @ GitHub - rakkarage/PixelInterface: Pixel-perfect fonts and user interface elements.

tmi? sorry blah blah blah

:bust_in_silhouette: Reply From: maitake_maitake

Thank you @rakkarage for your answer. It was cool to look through your code, but I figured out something and I will share it here for others looking for an answer to the problem I had.

var screensize = OS.get_window_size()
var adj_pos = Vector2()

func _process(_delta):
    cursor_pos = get_global_mouse_position()
    adj_pos.x = clamp(cursor_pos.x, 0, screensize.x - rect_size.x - 4)
    adj_pos.y = clamp(cursor_pos.y, 0 , screensize.y - rect_size.y - 4)
    set_position(adj_pos)

In this I get the window size (could also do a viewport if needed). In the process function I get the cursor position. I then clamp cursor_pos X and Y values to values between 0 and the screen size minus the size of the PanelContainer, and 4 to put a little more space from the edge of the screen so it’s easier to see. I then set position to this final adjusted position, adj_pos.

This probably isn’t the best, I am just learning to code :slight_smile:

EDIT: You also want to change the mouse filter flag in the PanelContainer to false so it doesn’t interrupt any hovering the mouse is doing