redesign tile action menu with toolbar and submenus
- replace old PopupMenu (flat preset list) with PopupPanel-based menu - add 3-icon toolbar: ⚙ Settings, ℹ Info, ✕ Remove (+) + Add - red ✕ on tiles removes the tile; green + on empty space opens add-tile submenu - 'Themes ▸' button opens a submenu with all ShaderPresets - add-tile submenu populates from PluginManager.get_all_tile_defs() - all toolbar buttons are 48×48 flat buttons with 24px font for touch - orphan PopupPanel children cleaned up during grid rebuild
This commit is contained in:
parent
6aba0e23d0
commit
ebf97a6fc8
3 changed files with 164 additions and 28 deletions
|
|
@ -1,7 +1,7 @@
|
|||
[layout]
|
||||
|
||||
name="Main"
|
||||
saved_at="2026-05-21T09:09:37"
|
||||
saved_at="2026-05-21T09:16:49"
|
||||
|
||||
[tiles]
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ count=3
|
|||
[tile_0]
|
||||
|
||||
id="system_monitor/cpu"
|
||||
col=0
|
||||
col=1
|
||||
row=0
|
||||
w=1
|
||||
h=1
|
||||
|
|
@ -18,7 +18,7 @@ h=1
|
|||
[tile_1]
|
||||
|
||||
id="system_monitor/memory"
|
||||
col=1
|
||||
col=2
|
||||
row=0
|
||||
w=1
|
||||
h=1
|
||||
|
|
@ -26,7 +26,7 @@ h=1
|
|||
[tile_2]
|
||||
|
||||
id="system_monitor/testing"
|
||||
col=2
|
||||
col=0
|
||||
row=0
|
||||
w=1
|
||||
h=2
|
||||
h=3
|
||||
|
|
|
|||
|
|
@ -32,4 +32,5 @@ window/stretch/mode="viewport"
|
|||
|
||||
[rendering]
|
||||
|
||||
renderer/rendering_method.mobile="forward_plus"
|
||||
renderer/rendering_method="gl_compatibility"
|
||||
renderer/rendering_method.mobile="gl_compatibility"
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func _on_gui_input(event: InputEvent) -> void:
|
|||
var mb: InputEventMouseButton = event
|
||||
if mb.button_index == MOUSE_BUTTON_LEFT and mb.double_click and mb.pressed:
|
||||
# Double-click still works for mouse users
|
||||
_show_preset_popup(mb.position)
|
||||
_show_tile_menu(mb.position)
|
||||
elif mb.button_index == MOUSE_BUTTON_LEFT and not mb.double_click:
|
||||
if mb.pressed:
|
||||
# Try resize edge first (instant, no long-press)
|
||||
|
|
@ -120,33 +120,168 @@ func _on_gui_input(event: InputEvent) -> void:
|
|||
_update_hover_cursor(mm.position)
|
||||
|
||||
|
||||
func _show_preset_popup(mouse_pos: Vector2) -> void:
|
||||
var cell_pos := _cell_at_position(mouse_pos)
|
||||
if cell_pos.x < 0 or cell_pos.y < 0:
|
||||
return
|
||||
var module := _get_module_at(cell_pos.x, cell_pos.y)
|
||||
if module == null:
|
||||
return
|
||||
# Don't show popup if already dragging or resizing
|
||||
const MENU_BUTTON_MIN_SIZE: Vector2 = Vector2(48, 48)
|
||||
const MENU_BUTTON_FONT_SIZE: int = 24
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Tile action menu (replaces old preset-only popup)
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
func _show_tile_menu(mouse_pos: Vector2) -> void:
|
||||
if _is_dragging or _is_resizing:
|
||||
return
|
||||
|
||||
var popup := PopupMenu.new()
|
||||
popup.name = "PresetPopup"
|
||||
var cell_pos := _cell_at_position(mouse_pos)
|
||||
if cell_pos.x < 0 or cell_pos.y < 0:
|
||||
return
|
||||
|
||||
var module := _get_module_at(cell_pos.x, cell_pos.y)
|
||||
|
||||
var panel := PopupPanel.new()
|
||||
panel.name = "TileMenu"
|
||||
panel.position = Vector2i(get_screen_position()) + Vector2i(mouse_pos)
|
||||
add_child(panel)
|
||||
|
||||
var margin := MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 8)
|
||||
margin.add_theme_constant_override("margin_right", 8)
|
||||
margin.add_theme_constant_override("margin_top", 8)
|
||||
margin.add_theme_constant_override("margin_bottom", 8)
|
||||
panel.add_child(margin)
|
||||
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 6)
|
||||
margin.add_child(vbox)
|
||||
|
||||
# -- toolbar row --
|
||||
var toolbar := HBoxContainer.new()
|
||||
toolbar.add_theme_constant_override("separation", 6)
|
||||
toolbar.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
vbox.add_child(toolbar)
|
||||
|
||||
var settings_btn := _make_menu_button("⚙", "Settings")
|
||||
toolbar.add_child(settings_btn)
|
||||
|
||||
var info_btn := _make_menu_button("ℹ", "Info")
|
||||
toolbar.add_child(info_btn)
|
||||
|
||||
if module != null:
|
||||
# On a tile: show remove button (red X)
|
||||
var remove_btn := _make_menu_button("✕", "Remove Tile")
|
||||
remove_btn.modulate = Color(1.0, 0.25, 0.25, 1.0)
|
||||
remove_btn.pressed.connect(_remove_tile.bind(module, panel))
|
||||
toolbar.add_child(remove_btn)
|
||||
else:
|
||||
# On empty space: show add button (green +)
|
||||
var add_btn := _make_menu_button("+", "Add Tile")
|
||||
add_btn.modulate = Color(0.2, 1.0, 0.2, 1.0)
|
||||
add_btn.pressed.connect(_show_add_tile_submenu.bind(add_btn, panel))
|
||||
toolbar.add_child(add_btn)
|
||||
|
||||
# -- Themes button (only on a tile) --
|
||||
if module != null:
|
||||
var themes_btn := Button.new()
|
||||
themes_btn.text = "Themes ▸"
|
||||
themes_btn.flat = true
|
||||
themes_btn.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||
themes_btn.pressed.connect(_show_themes_submenu.bind(themes_btn, module, panel))
|
||||
vbox.add_child(themes_btn)
|
||||
|
||||
panel.popup()
|
||||
|
||||
|
||||
func _make_menu_button(text: String, tooltip: String) -> Button:
|
||||
var btn := Button.new()
|
||||
btn.text = text
|
||||
btn.tooltip_text = tooltip
|
||||
btn.flat = true
|
||||
btn.custom_min_size = MENU_BUTTON_MIN_SIZE
|
||||
btn.add_theme_font_size_override("font_size", MENU_BUTTON_FONT_SIZE)
|
||||
return btn
|
||||
|
||||
|
||||
func _show_themes_submenu(parent_btn: Button, module: Control, panel: PopupPanel) -> void:
|
||||
var submenu := PopupMenu.new()
|
||||
submenu.name = "ThemeSubmenu"
|
||||
var names := ShaderPresets.get_preset_names()
|
||||
for i in names.size():
|
||||
popup.add_item(names[i], i)
|
||||
popup.position = Vector2i(get_screen_position()) + Vector2i(mouse_pos)
|
||||
add_child(popup)
|
||||
popup.popup()
|
||||
submenu.add_item(names[i], i)
|
||||
|
||||
var on_select := func(id: int):
|
||||
# Position the submenu to the right of the parent button
|
||||
var btn_global: Vector2 = parent_btn.global_position + Vector2(parent_btn.size.x, 0.0)
|
||||
submenu.position = Vector2i(btn_global)
|
||||
add_child(submenu)
|
||||
submenu.popup()
|
||||
|
||||
submenu.id_pressed.connect(func(id: int):
|
||||
if id >= 0 and id < names.size():
|
||||
ShaderPresets.apply_preset(module, names[id])
|
||||
if is_instance_valid(popup):
|
||||
popup.queue_free()
|
||||
if is_instance_valid(submenu):
|
||||
submenu.queue_free()
|
||||
if is_instance_valid(panel):
|
||||
panel.queue_free()
|
||||
)
|
||||
|
||||
popup.id_pressed.connect(on_select)
|
||||
|
||||
func _show_add_tile_submenu(parent_btn: Button, panel: PopupPanel) -> void:
|
||||
if not PluginManager.has_method("get_all_tile_defs"):
|
||||
return
|
||||
|
||||
var defs: Array[Dictionary] = PluginManager.get_all_tile_defs()
|
||||
if defs.is_empty():
|
||||
return
|
||||
|
||||
var submenu := PopupMenu.new()
|
||||
submenu.name = "AddTileSubmenu"
|
||||
for i in defs.size():
|
||||
var label: String = defs[i].get("name", defs[i].get("id", "Tile %d" % i))
|
||||
submenu.add_item(label, i)
|
||||
|
||||
var btn_global: Vector2 = parent_btn.global_position + Vector2(parent_btn.size.x, 0.0)
|
||||
submenu.position = Vector2i(btn_global)
|
||||
add_child(submenu)
|
||||
submenu.popup()
|
||||
|
||||
submenu.id_pressed.connect(func(id: int):
|
||||
if id >= 0 and id < defs.size():
|
||||
_add_tile_from_def(defs[id])
|
||||
if is_instance_valid(submenu):
|
||||
submenu.queue_free()
|
||||
if is_instance_valid(panel):
|
||||
panel.queue_free()
|
||||
)
|
||||
|
||||
|
||||
func _remove_tile(module: Control, panel: PopupPanel) -> void:
|
||||
remove_module(module)
|
||||
if is_instance_valid(panel):
|
||||
panel.queue_free()
|
||||
|
||||
|
||||
func _add_tile_from_def(def: Dictionary) -> void:
|
||||
if not PluginManager.has_method("instantiate_tile"):
|
||||
return
|
||||
var tid: String = def.get("id", "")
|
||||
if tid.is_empty():
|
||||
return
|
||||
var instance := PluginManager.instantiate_tile(tid)
|
||||
if instance == null:
|
||||
return
|
||||
|
||||
# Find a free position at the end of the grid
|
||||
var gs := get_grid_size()
|
||||
var target_col := 0
|
||||
var target_row := gs.y
|
||||
# Try current row first
|
||||
var dw: int = def.get("default_w", 1)
|
||||
var dh: int = def.get("default_h", 1)
|
||||
for c in range(gs.x):
|
||||
if _span_fits(c, target_row, dw, dh, null):
|
||||
target_col = c
|
||||
break
|
||||
|
||||
place_module(instance, target_col, target_row, dw, dh)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
|
@ -177,7 +312,7 @@ func _cancel_long_press() -> void:
|
|||
func _on_long_press_timeout() -> void:
|
||||
_long_press_active = false
|
||||
_long_press_timer = null
|
||||
_show_preset_popup(_long_press_pos)
|
||||
_show_tile_menu(_long_press_pos)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
|
@ -665,9 +800,9 @@ func _rebuild_grid() -> void:
|
|||
_cancel_drag()
|
||||
if _is_resizing:
|
||||
_end_resize(Vector2.ZERO)
|
||||
# Clean up orphaned popups from preset selection
|
||||
# Clean up orphaned popups from tile menu
|
||||
for child in get_children():
|
||||
if child is PopupMenu:
|
||||
if child is PopupMenu or child is PopupPanel:
|
||||
child.queue_free()
|
||||
|
||||
var avail := size - Vector2(margin * 2.0, margin * 2.0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue