feat: Add StageDialogStyler for enhanced dialog management
- Introduced StageDialogStyler class to render child-facing dialogs as shared in-stage cards. - Preserved native AcceptDialog/ConfirmationDialog signals for stage workflows. - Implemented methods for applying styles, managing visibility, and handling button actions. - Added support for dynamic theming and responsive design based on viewport size. - Created utility functions for styling buttons and managing dialog content.
This commit is contained in:
@@ -47,6 +47,7 @@ const STAGE_RUN_SESSION_SCRIPT := preload("res://scripts/editors/stage/stage_run
|
||||
const STAGE_IMPACT_RESOLVER_SCRIPT := preload("res://scripts/stage/stage_impact_resolver.gd")
|
||||
const STAGE_ASSET_BROWSER_SCRIPT := preload("res://scripts/editors/stage/stage_asset_browser.gd")
|
||||
const STAGE_MODAL_VIEWS_SCRIPT := preload("res://scripts/editors/stage/stage_modal_views.gd")
|
||||
const STAGE_DIALOG_STYLER := preload("res://scripts/shared/stage_ui/stage_dialog_styler.gd")
|
||||
const STAGE_THEME_CONTROLLER_SCRIPT := preload("res://scripts/editors/stage/stage_theme_controller.gd")
|
||||
const STAGE_TARGET_CAPTURE_SCRIPT := preload("res://scripts/editors/stage/stage_target_capture_session.gd")
|
||||
const STAGE_SCENERY_CONTROLLER_SCRIPT := preload("res://scripts/editors/stage/stage_scenery_controller.gd")
|
||||
@@ -1543,11 +1544,28 @@ func _place_speech_bubble_at(viewport_position: Vector2) -> void:
|
||||
_speech_session.place_bubble_at(viewport_position)
|
||||
|
||||
|
||||
func _active_styled_dialog() -> Window:
|
||||
if _chrome == null:
|
||||
return null
|
||||
var ui_layer: CanvasLayer = _chrome.get_canvas_layer()
|
||||
for node: Node in ui_layer.find_children("*", "Window", true, false):
|
||||
var dialog: Window = node as Window
|
||||
if dialog != null and STAGE_DIALOG_STYLER.is_styled(dialog) and STAGE_DIALOG_STYLER.is_open(dialog):
|
||||
return dialog
|
||||
return null
|
||||
|
||||
|
||||
func _unhandled_key_input(event: InputEvent) -> void:
|
||||
if _hosted_prop_editor != null:
|
||||
return
|
||||
if not (event is InputEventKey) or not event.pressed or event.echo:
|
||||
return
|
||||
var active_dialog: Window = _active_styled_dialog()
|
||||
if active_dialog != null:
|
||||
if event.keycode == KEY_ESCAPE:
|
||||
STAGE_DIALOG_STYLER.dismiss_on_escape(active_dialog)
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if _scenery_erase_piece_dialog != null and _scenery_erase_piece_dialog.visible:
|
||||
if event.keycode == KEY_ESCAPE:
|
||||
_dismiss_scenery_erase_piece_dialog()
|
||||
@@ -1670,7 +1688,7 @@ func _space_should_pass_to_gui() -> bool:
|
||||
if focus is LineEdit or focus is TextEdit or focus is SpinBox:
|
||||
return true
|
||||
for window: Window in [_browse_dialog, _speak_dialog, _wait_dialog, _scene_open_dialog, _scene_save_dialog, _speech_save_gate_dialog, _group_preview_save_dialog, _scenery_repair_confirm_dialog, _missing_scenery_dialog, _rename_scene_dialog, _unsaved_changes_dialog, _missing_assets_dialog, _cli_error_dialog, _confirm_dialog]:
|
||||
if window != null and window.visible:
|
||||
if window != null and STAGE_DIALOG_STYLER.is_open(window):
|
||||
return true
|
||||
if _selector_open or (_queue_panel != null and _queue_panel.visible) or (_rule_panel != null and _rule_panel.visible) or (_action_editor != null and _action_editor.visible) or (_rule_editor != null and _rule_editor.visible):
|
||||
return true
|
||||
@@ -2074,7 +2092,7 @@ func _pause_toggle_blocked() -> bool:
|
||||
if focus is LineEdit or focus is TextEdit or focus is SpinBox:
|
||||
return true
|
||||
for dialog: Window in [_browse_dialog, _speak_dialog, _wait_dialog, _scene_open_dialog, _scene_save_dialog, _speech_save_gate_dialog, _group_preview_save_dialog, _scenery_repair_confirm_dialog, _missing_scenery_dialog, _rename_scene_dialog, _unsaved_changes_dialog, _missing_assets_dialog, _cli_error_dialog, _confirm_dialog]:
|
||||
if dialog != null and dialog.visible:
|
||||
if dialog != null and STAGE_DIALOG_STYLER.is_open(dialog):
|
||||
return true
|
||||
return (_queue_panel != null and _queue_panel.visible) or (_action_editor != null and _action_editor.visible) or (_rule_editor != null and _rule_editor.visible)
|
||||
|
||||
@@ -2630,8 +2648,8 @@ func _save_scene_to_path(path: String, is_autosave: bool = false) -> bool:
|
||||
if is_autosave:
|
||||
return false
|
||||
_pending_group_save_path = path
|
||||
if _group_preview_save_dialog != null and not _group_preview_save_dialog.visible:
|
||||
_group_preview_save_dialog.popup_centered()
|
||||
if _group_preview_save_dialog != null and not STAGE_DIALOG_STYLER.is_open(_group_preview_save_dialog):
|
||||
STAGE_DIALOG_STYLER.popup(_group_preview_save_dialog)
|
||||
return false
|
||||
if _speech_session != null and _speech_session.is_save_blocked():
|
||||
if is_autosave:
|
||||
@@ -2639,8 +2657,8 @@ func _save_scene_to_path(path: String, is_autosave: bool = false) -> bool:
|
||||
_speech_pending_scene_save_path = path
|
||||
_speech_pending_scene_save_as = false
|
||||
_speech_scene_save_pending = true
|
||||
if _speech_save_gate_dialog != null and not _speech_save_gate_dialog.visible:
|
||||
_speech_save_gate_dialog.popup_centered()
|
||||
if _speech_save_gate_dialog != null and not STAGE_DIALOG_STYLER.is_open(_speech_save_gate_dialog):
|
||||
STAGE_DIALOG_STYLER.popup(_speech_save_gate_dialog)
|
||||
return false
|
||||
if _rule_edit_uncommitted:
|
||||
if not is_autosave:
|
||||
@@ -2699,7 +2717,7 @@ func _reset_scene_content() -> void:
|
||||
_scenery_erase_piece_dialog.hide()
|
||||
_clear_erase_scenery_targets()
|
||||
if _scenery_repair_confirm_dialog != null:
|
||||
_scenery_repair_confirm_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_scenery_repair_confirm_dialog)
|
||||
_cancel_scenery_repair_preview()
|
||||
_scenery_repair_target = null
|
||||
_sky_id = &"sky_neutral"
|
||||
@@ -2806,36 +2824,42 @@ func _apply_scene_data(data: Dictionary, source_path: String, is_recovery: bool)
|
||||
|
||||
func _show_scene_load_warnings(warnings: Array[String]) -> void:
|
||||
var dialog: AcceptDialog = AcceptDialog.new()
|
||||
dialog.title = "Some saved actions need a look"
|
||||
dialog.title = "⚠️ Some saved actions need a look"
|
||||
dialog.ok_button_text = "Got it"
|
||||
var lines: PackedStringArray = PackedStringArray()
|
||||
for index: int in mini(warnings.size(), 12):
|
||||
for index: int in mini(warnings.size(), 5):
|
||||
lines.append("• " + warnings[index])
|
||||
if warnings.size() > 12:
|
||||
lines.append("• And %d more warning(s)." % (warnings.size() - 12))
|
||||
if warnings.size() > 5:
|
||||
lines.append("• And %d more warning(s)." % (warnings.size() - 5))
|
||||
dialog.dialog_text = "These items could not be restored:\n\n%s" % "\n".join(lines)
|
||||
add_child(dialog)
|
||||
_chrome.get_canvas_layer().add_child(dialog)
|
||||
_apply_dialog_theme(dialog)
|
||||
dialog.confirmed.connect(dialog.queue_free)
|
||||
dialog.close_requested.connect(dialog.queue_free)
|
||||
dialog.popup_centered(Vector2i(620, 420))
|
||||
STAGE_DIALOG_STYLER.popup(dialog)
|
||||
|
||||
|
||||
func _show_missing_assets_dialog(paths: Array[String]) -> void:
|
||||
if _missing_assets_dialog == null:
|
||||
return
|
||||
var asset_lines: Array[String] = []
|
||||
for path: String in paths:
|
||||
var asset_name := path.get_file()
|
||||
if asset_name.is_empty(): asset_name = "Unnamed .stk asset"
|
||||
asset_lines.append("• %s\n %s" % [asset_name, path if not path.is_empty() else "(no asset path was saved)"])
|
||||
_missing_assets_dialog.dialog_text = "%d stickman asset(s) could not be loaded:\n\n%s\n\nA red marker was placed where each missing asset belongs." % [paths.size(), "\n\n".join(asset_lines)]
|
||||
_missing_assets_dialog.popup_centered()
|
||||
for index: int in mini(paths.size(), 5):
|
||||
var asset_name: String = paths[index].get_file()
|
||||
if asset_name.is_empty():
|
||||
asset_name = "Unnamed stickman"
|
||||
asset_lines.append("• %s" % asset_name)
|
||||
if paths.size() > 5:
|
||||
asset_lines.append("• And %d more" % (paths.size() - 5))
|
||||
_missing_assets_dialog.title = "⚠️ Some scene assets are missing"
|
||||
_missing_assets_dialog.dialog_text = "Red markers show where the missing stickmen belong.\n\n%s" % "\n".join(asset_lines)
|
||||
STAGE_DIALOG_STYLER.popup(_missing_assets_dialog)
|
||||
|
||||
|
||||
func _show_missing_scenery_dialog(count: int) -> void:
|
||||
if _missing_scenery_dialog == null:
|
||||
return
|
||||
_missing_scenery_dialog.dialog_text = "%d scenery picture%s could not be found. The scene still has a marker in each spot, so nothing was lost." % [count, "" if count == 1 else "s"]
|
||||
_missing_scenery_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_missing_scenery_dialog)
|
||||
|
||||
|
||||
func _add_recent_scene(path: String) -> void:
|
||||
@@ -2884,7 +2908,12 @@ func _check_autosave_recovery(show_starter_chooser_after_discard: bool = false)
|
||||
return
|
||||
var autosave_data: Dictionary = inspection.get("data", {}) as Dictionary
|
||||
var dialog := ConfirmationDialog.new()
|
||||
dialog.name = "AutosaveRecoveryDialog"; dialog.title = "Recover Sandbox Scene?"; dialog.dialog_text = "A previous Play session was not closed cleanly. Recover it?"
|
||||
dialog.name = "AutosaveRecoveryDialog"
|
||||
dialog.title = "🔄 Scene recovery found"
|
||||
dialog.dialog_text = "A recovery copy of this scene is available. Would you like to restore it?\n\nℹ️ Restoring opens the recovered scene for review before you save it."
|
||||
dialog.ok_button_text = "Restore scene"
|
||||
dialog.cancel_button_text = "Discard recovery"
|
||||
dialog.min_size = _theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
dialog.confirmed.connect(func() -> void: _apply_scene_data(autosave_data, AUTOSAVE_PATH, true); dialog.queue_free())
|
||||
dialog.canceled.connect(func() -> void:
|
||||
_document.remove_autosave()
|
||||
@@ -2892,7 +2921,9 @@ func _check_autosave_recovery(show_starter_chooser_after_discard: bool = false)
|
||||
if show_starter_chooser_after_discard:
|
||||
_queue_startup_starter_chooser()
|
||||
)
|
||||
get_node(NodePath("UI")).add_child(dialog); _apply_dialog_theme(dialog); dialog.popup_centered()
|
||||
get_node(NodePath("UI")).add_child(dialog)
|
||||
_apply_dialog_theme(dialog, dialog.get_ok_button(), [dialog.get_cancel_button()])
|
||||
STAGE_DIALOG_STYLER.popup(dialog)
|
||||
|
||||
|
||||
func _queue_startup_starter_chooser() -> void:
|
||||
@@ -2924,7 +2955,7 @@ func _startup_modal_is_visible() -> bool:
|
||||
if ui_layer != null:
|
||||
for child: Node in ui_layer.get_children():
|
||||
var window := child as Window
|
||||
if window != null and window.visible:
|
||||
if (window != null and STAGE_DIALOG_STYLER.is_open(window)) or (child is Control and child.name == "StageDialogOverlay" and (child as Control).visible):
|
||||
return true
|
||||
for dialog: Window in [
|
||||
_cli_error_dialog,
|
||||
@@ -2940,7 +2971,7 @@ func _startup_modal_is_visible() -> bool:
|
||||
_group_preview_save_dialog,
|
||||
_confirm_dialog,
|
||||
]:
|
||||
if dialog != null and is_instance_valid(dialog) and dialog.visible:
|
||||
if dialog != null and is_instance_valid(dialog) and STAGE_DIALOG_STYLER.is_open(dialog):
|
||||
return true
|
||||
return false
|
||||
|
||||
@@ -2978,11 +3009,11 @@ func _load_cli_scene(path: String) -> Dictionary:
|
||||
return {"ok": true, "error": ""}
|
||||
|
||||
|
||||
func _show_cli_error_dialog(message: String) -> void:
|
||||
func _show_cli_error_dialog(_message: String) -> void:
|
||||
if _cli_error_dialog == null:
|
||||
return
|
||||
_cli_error_dialog.dialog_text = message
|
||||
_cli_error_dialog.popup_centered()
|
||||
_cli_error_dialog.dialog_text = ""
|
||||
STAGE_DIALOG_STYLER.popup(_cli_error_dialog)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
@@ -3085,9 +3116,9 @@ func _theme_dialog_size(key: String, fallback: Vector2i) -> Vector2i:
|
||||
|
||||
## Dialogs are separate native/embedded windows, so they do not reliably
|
||||
## inherit the CanvasLayer's visual defaults. Apply only local overrides.
|
||||
func _apply_dialog_theme(dialog: Window) -> void:
|
||||
func _apply_dialog_theme(dialog: Window, primary_button: Button = null, destructive_buttons: Array = []) -> void:
|
||||
if _modal_views != null:
|
||||
_modal_views.apply_dialog_theme(dialog)
|
||||
_modal_views.apply_dialog_theme(dialog, primary_button, destructive_buttons)
|
||||
|
||||
|
||||
func _apply_dialog_font_to_children(node: Node, size: int) -> void:
|
||||
@@ -3446,7 +3477,7 @@ func _build_scenery_inspector(ui: CanvasLayer) -> void:
|
||||
_missing_scenery_dialog.dialog_text = "This scene has artwork that could not be found. You can still explore the scene and fix it later."
|
||||
_missing_scenery_dialog.ok_button_text = "Show Scenery"
|
||||
_missing_scenery_dialog.cancel_button_text = "Later"
|
||||
_missing_scenery_dialog.min_size = _theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
_missing_scenery_dialog.min_size = _theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
_missing_scenery_dialog.confirmed.connect(_enter_scenery_edit_mode)
|
||||
ui.add_child(_missing_scenery_dialog)
|
||||
_apply_dialog_theme(_missing_scenery_dialog)
|
||||
@@ -3455,7 +3486,7 @@ func _build_scenery_inspector(ui: CanvasLayer) -> void:
|
||||
_scenery_repair_confirm_dialog.title = "Use this artwork here?"
|
||||
_scenery_repair_confirm_dialog.ok_button_text = "Use artwork"
|
||||
_scenery_repair_confirm_dialog.cancel_button_text = "Keep for now"
|
||||
_scenery_repair_confirm_dialog.min_size = _theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
_scenery_repair_confirm_dialog.min_size = _theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
_scenery_repair_confirm_dialog.confirmed.connect(_confirm_scenery_repair)
|
||||
_scenery_repair_confirm_dialog.canceled.connect(_cancel_scenery_repair_preview)
|
||||
ui.add_child(_scenery_repair_confirm_dialog)
|
||||
@@ -3556,7 +3587,7 @@ func _build_scenery_erase_piece_dialog(ui: CanvasLayer) -> void:
|
||||
var backdrop: ColorRect = ColorRect.new()
|
||||
backdrop.name = "DialogBackdrop"
|
||||
backdrop.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
backdrop.color = _confirmation_dialog_color("backdrop_color", Color("#10151D66"))
|
||||
backdrop.color = _confirmation_dialog_color("backdrop_color", Color("#080D15B8"))
|
||||
backdrop.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_scenery_erase_piece_dialog.add_child(backdrop)
|
||||
|
||||
@@ -3572,7 +3603,7 @@ func _build_scenery_erase_piece_dialog(ui: CanvasLayer) -> void:
|
||||
|
||||
var margin: MarginContainer = MarginContainer.new()
|
||||
margin.name = "DialogMargin"
|
||||
var padding: int = _confirmation_dialog_integer("padding", 18, 12, 32)
|
||||
var padding: int = _confirmation_dialog_integer("padding", 22, 12, 36)
|
||||
margin.add_theme_constant_override("margin_left", padding)
|
||||
margin.add_theme_constant_override("margin_right", padding)
|
||||
margin.add_theme_constant_override("margin_top", padding)
|
||||
@@ -3583,14 +3614,14 @@ func _build_scenery_erase_piece_dialog(ui: CanvasLayer) -> void:
|
||||
layout.name = "DialogLayout"
|
||||
layout.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
layout.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
layout.add_theme_constant_override("separation", _confirmation_dialog_integer("body_gap", 10, 4, 24))
|
||||
layout.add_theme_constant_override("separation", _confirmation_dialog_integer("body_gap", 12, 4, 24))
|
||||
margin.add_child(layout)
|
||||
|
||||
_scenery_erase_piece_dialog_title = Label.new()
|
||||
_scenery_erase_piece_dialog_title.name = "DialogTitle"
|
||||
_scenery_erase_piece_dialog_title.text = "Remove this scenery?"
|
||||
_scenery_erase_piece_dialog_title.text = "🧱 Remove this scenery piece?"
|
||||
_scenery_erase_piece_dialog_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_scenery_erase_piece_dialog_title.add_theme_font_size_override("font_size", _confirmation_dialog_integer("title_font_size", 26, 20, 40))
|
||||
_scenery_erase_piece_dialog_title.add_theme_font_size_override("font_size", _confirmation_dialog_integer("title_font_size", 28, 20, 40))
|
||||
_scenery_erase_piece_dialog_title.add_theme_color_override("font_color", _stage_panel_color("type.text_color", Color("#F5F7FA")))
|
||||
_apply_ui_font(_scenery_erase_piece_dialog_title)
|
||||
var title_font: Font = _ui_font
|
||||
@@ -3603,7 +3634,7 @@ func _build_scenery_erase_piece_dialog(ui: CanvasLayer) -> void:
|
||||
|
||||
_scenery_erase_piece_dialog_message = Label.new()
|
||||
_scenery_erase_piece_dialog_message.name = "DialogMessage"
|
||||
_scenery_erase_piece_dialog_message.text = "Remove this whole piece? You can’t undo this."
|
||||
_scenery_erase_piece_dialog_message.text = "This removes the selected scenery piece from the scene."
|
||||
_scenery_erase_piece_dialog_message.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_scenery_erase_piece_dialog_message.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_scenery_erase_piece_dialog_message.add_theme_font_size_override("font_size", _confirmation_dialog_integer("message_font_size", 20, 16, 32))
|
||||
@@ -3623,14 +3654,14 @@ func _build_scenery_erase_piece_dialog(ui: CanvasLayer) -> void:
|
||||
|
||||
_scenery_erase_piece_keep_button = Button.new()
|
||||
_scenery_erase_piece_keep_button.name = "KeepSceneryButton"
|
||||
_scenery_erase_piece_keep_button.text = "Keep it"
|
||||
_scenery_erase_piece_keep_button.text = "Keep this piece"
|
||||
_scenery_erase_piece_keep_button.focus_mode = Control.FOCUS_ALL
|
||||
_scenery_erase_piece_keep_button.pressed.connect(_dismiss_scenery_erase_piece_dialog)
|
||||
_scenery_erase_piece_dialog_actions.add_child(_scenery_erase_piece_keep_button)
|
||||
|
||||
_scenery_erase_piece_remove_button = Button.new()
|
||||
_scenery_erase_piece_remove_button.name = "RemoveSceneryButton"
|
||||
_scenery_erase_piece_remove_button.text = "Remove scenery"
|
||||
_scenery_erase_piece_remove_button.text = "Remove piece"
|
||||
_scenery_erase_piece_remove_button.focus_mode = Control.FOCUS_ALL
|
||||
_scenery_erase_piece_remove_button.pressed.connect(_confirm_erase_scenery_from_dialog)
|
||||
_scenery_erase_piece_dialog_actions.add_child(_scenery_erase_piece_remove_button)
|
||||
@@ -3642,66 +3673,18 @@ func _build_scenery_erase_piece_dialog(ui: CanvasLayer) -> void:
|
||||
func _apply_scenery_erase_piece_dialog_style() -> void:
|
||||
if _scenery_erase_piece_dialog == null:
|
||||
return
|
||||
var shell: StyleBoxFlat = StyleBoxFlat.new()
|
||||
shell.bg_color = _confirmation_dialog_color("surface_color", Color("#202633FA"))
|
||||
shell.border_color = _confirmation_dialog_color("border_color", Color("#AAB2C066"))
|
||||
shell.set_border_width_all(_confirmation_dialog_integer("border_width", 1, 1, 4))
|
||||
shell.set_corner_radius_all(_confirmation_dialog_integer("corner_radius", 16, 10, 28))
|
||||
var shell: StyleBoxFlat = STAGE_DIALOG_STYLER.panel_style(_sandbox_theme, false)
|
||||
_scenery_erase_piece_dialog_panel.add_theme_stylebox_override("panel", shell)
|
||||
_apply_scenery_erase_piece_action_style(_scenery_erase_piece_keep_button, true)
|
||||
_apply_scenery_erase_piece_action_style(_scenery_erase_piece_remove_button, false)
|
||||
|
||||
|
||||
func _apply_scenery_erase_piece_action_style(button: Button, primary: bool) -> void:
|
||||
if button == null:
|
||||
return
|
||||
var accent: Color = _confirmation_dialog_color("accent_color", Color("#22C6FF"))
|
||||
var blue: Color = _confirmation_dialog_color("action_color", Color("#087DBA"))
|
||||
var danger: Color = _confirmation_dialog_color("danger_color", Color("#FF626B"))
|
||||
var neutral: Color = _stage_panel_color("action.surface_color", Color("#171B24"))
|
||||
var hover_neutral: Color = _confirmation_dialog_color("danger_hover_surface", Color("#3B252C"))
|
||||
var pressed_neutral: Color = _confirmation_dialog_color("danger_pressed_surface", Color("#4A252D"))
|
||||
var border_width: int = _confirmation_dialog_integer("button_border_width", 2, 1, 5)
|
||||
var radius: int = _confirmation_dialog_integer("button_radius", 12, 8, 24)
|
||||
var normal_color: Color = blue if primary else neutral
|
||||
var hover_color: Color = blue.lightened(0.12) if primary else hover_neutral
|
||||
var pressed_color: Color = blue.darkened(0.12) if primary else pressed_neutral
|
||||
var edge_color: Color = accent if primary else danger
|
||||
button.add_theme_stylebox_override("normal", _make_scenery_erase_button_style(normal_color, edge_color, border_width, radius))
|
||||
button.add_theme_stylebox_override("hover", _make_scenery_erase_button_style(hover_color, edge_color.lightened(0.12), border_width + 1, radius))
|
||||
button.add_theme_stylebox_override("pressed", _make_scenery_erase_button_style(pressed_color, edge_color, border_width + 1, radius))
|
||||
button.add_theme_stylebox_override("hover_pressed", _make_scenery_erase_button_style(pressed_color.lightened(0.05), edge_color, border_width + 1, radius))
|
||||
button.add_theme_stylebox_override("focus", _make_scenery_erase_button_style(normal_color, accent, border_width + 1, radius))
|
||||
var label_color: Color = Color.WHITE if primary else danger
|
||||
button.add_theme_color_override("font_color", label_color)
|
||||
button.add_theme_color_override("font_hover_color", label_color)
|
||||
button.add_theme_color_override("font_pressed_color", label_color)
|
||||
button.add_theme_font_size_override("font_size", _confirmation_dialog_integer("button_font_size", 20, 16, 32))
|
||||
button.custom_minimum_size = Vector2(
|
||||
float(_confirmation_dialog_integer("remove_button_width", 170, 128, 240)) if not primary else float(_confirmation_dialog_integer("keep_button_width", 132, 112, 220)),
|
||||
float(_confirmation_dialog_integer("button_height", 48, 44, 80))
|
||||
STAGE_DIALOG_STYLER.style_button(_scenery_erase_piece_keep_button, &"primary", _sandbox_theme, _ui_font)
|
||||
STAGE_DIALOG_STYLER.style_button(_scenery_erase_piece_remove_button, &"danger", _sandbox_theme, _ui_font)
|
||||
_scenery_erase_piece_keep_button.custom_minimum_size = Vector2(
|
||||
float(_confirmation_dialog_integer("keep_button_width", 144, 112, 240)),
|
||||
float(_confirmation_dialog_integer("button_height", 52, 44, 80))
|
||||
)
|
||||
_scenery_erase_piece_remove_button.custom_minimum_size = Vector2(
|
||||
float(_confirmation_dialog_integer("remove_button_width", 170, 128, 240)),
|
||||
float(_confirmation_dialog_integer("button_height", 52, 44, 80))
|
||||
)
|
||||
var font: Font = _ui_font
|
||||
if primary and font != null:
|
||||
var strong_font: FontVariation = FontVariation.new()
|
||||
strong_font.base_font = font
|
||||
strong_font.variation_embolden = 0.55
|
||||
button.add_theme_font_override("font", strong_font)
|
||||
else:
|
||||
_apply_ui_font(button)
|
||||
|
||||
|
||||
func _make_scenery_erase_button_style(background: Color, border: Color, width: int, radius: int) -> StyleBoxFlat:
|
||||
var style: StyleBoxFlat = StyleBoxFlat.new()
|
||||
style.bg_color = background
|
||||
style.border_color = border
|
||||
style.set_border_width_all(width)
|
||||
style.set_corner_radius_all(radius)
|
||||
style.content_margin_left = 12.0
|
||||
style.content_margin_right = 12.0
|
||||
style.content_margin_top = 8.0
|
||||
style.content_margin_bottom = 8.0
|
||||
return style
|
||||
|
||||
|
||||
func _confirmation_dialog_integer(token: String, fallback: int, minimum: int, maximum: int) -> int:
|
||||
@@ -3961,53 +3944,14 @@ func _build_use_spot_overlay(ui: CanvasLayer) -> void:
|
||||
ui.add_child(_use_choice_popup)
|
||||
_spot_erase_dialog = ConfirmationDialog.new()
|
||||
_spot_erase_dialog.name = "EraseUseSpotDialog"
|
||||
_spot_erase_dialog.title = "⚠️ Erase Use spot?"
|
||||
_spot_erase_dialog.title = "📍 Erase this Use spot?"
|
||||
_spot_erase_dialog.ok_button_text = "Erase spot"
|
||||
_spot_erase_dialog.cancel_button_text = "Keep spot"
|
||||
_spot_erase_dialog.min_size = _theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
_spot_erase_dialog.min_size = _theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
_spot_erase_dialog.theme = _sandbox_theme.control_theme() if _sandbox_theme != null else null
|
||||
_spot_erase_dialog.confirmed.connect(_on_spot_erase_confirmed)
|
||||
ui.add_child(_spot_erase_dialog)
|
||||
_apply_dialog_theme(_spot_erase_dialog)
|
||||
_style_spot_erase_dialog_buttons()
|
||||
|
||||
|
||||
func _style_spot_erase_dialog_buttons() -> void:
|
||||
if _spot_erase_dialog == null:
|
||||
return
|
||||
var danger: Color = _sandbox_theme.color("overlays.use_preview.invalid_color", Color("#F06464")) if _sandbox_theme != null else Color("#F06464")
|
||||
var safe: Color = _sandbox_theme.color("mode_colors.edit", Color("#22C6FF")) if _sandbox_theme != null else Color("#22C6FF")
|
||||
_style_spot_erase_titlebar(danger.darkened(0.58))
|
||||
_style_spot_erase_action_button(_spot_erase_dialog.get_ok_button(), danger.darkened(0.55), danger)
|
||||
_style_spot_erase_action_button(_spot_erase_dialog.get_cancel_button(), safe.darkened(0.72), safe)
|
||||
|
||||
|
||||
func _style_spot_erase_titlebar(color: Color) -> void:
|
||||
for style_name: String in ["embedded_border", "embedded_unfocused_border"]:
|
||||
var original: StyleBoxFlat = _spot_erase_dialog.get_theme_stylebox(style_name) as StyleBoxFlat
|
||||
if original == null:
|
||||
continue
|
||||
var tinted: StyleBoxFlat = original.duplicate() as StyleBoxFlat
|
||||
tinted.bg_color = color
|
||||
_spot_erase_dialog.add_theme_stylebox_override(style_name, tinted)
|
||||
_spot_erase_dialog.add_theme_color_override("title_color", Color("#FFFFFF"))
|
||||
|
||||
|
||||
func _style_spot_erase_action_button(button: Button, surface: Color, accent: Color) -> void:
|
||||
if button == null:
|
||||
return
|
||||
button.custom_minimum_size = Vector2(156.0, 56.0)
|
||||
button.add_theme_color_override("font_color", Color("#FFFFFF"))
|
||||
button.add_theme_color_override("font_hover_color", Color("#FFFFFF"))
|
||||
button.add_theme_color_override("font_pressed_color", Color("#FFFFFF"))
|
||||
for state: String in ["normal", "hover", "pressed", "hover_pressed"]:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = surface.lightened(0.10) if state == "hover" or state == "hover_pressed" else surface
|
||||
style.border_color = accent.lightened(0.18) if state == "hover" or state == "pressed" or state == "hover_pressed" else accent
|
||||
style.set_border_width_all(2 if state == "hover" or state == "pressed" or state == "hover_pressed" else 1)
|
||||
style.set_corner_radius_all(10)
|
||||
style.set_content_margin_all(12.0)
|
||||
button.add_theme_stylebox_override(state, style)
|
||||
_apply_dialog_theme(_spot_erase_dialog, _spot_erase_dialog.get_cancel_button(), [_spot_erase_dialog.get_ok_button()])
|
||||
|
||||
|
||||
func _stage_panel_integer(key: String, fallback: int, minimum: int, maximum: int) -> int:
|
||||
@@ -4153,8 +4097,9 @@ func _on_spot_erase_requested(spot_id: String) -> void:
|
||||
_spot_erase_prop_id = prop.stage_object_id
|
||||
_spot_erase_id = spot_id
|
||||
var warning: String = "\nThis spot is used in your story. Those steps will need a new spot." if _use_spot_is_referenced(prop.stage_object_id, spot_id) else ""
|
||||
_spot_erase_dialog.dialog_text = "Erase %s (spot %d)?%s" % [String(spot.get("name", "Spot")), int(spot.get("number", 0)), warning]
|
||||
_spot_erase_dialog.popup_centered(_spot_erase_dialog.min_size)
|
||||
_spot_erase_dialog.dialog_text = "The character will no longer be able to use this spot.%s" % warning
|
||||
STAGE_DIALOG_STYLER.popup(_spot_erase_dialog)
|
||||
STAGE_DIALOG_STYLER.visual_button(_spot_erase_dialog.get_cancel_button()).grab_focus()
|
||||
|
||||
|
||||
func _on_spot_erase_confirmed() -> void:
|
||||
@@ -5600,14 +5545,14 @@ func _start_scenery_repair_preview(entry: Dictionary) -> void:
|
||||
_scenery_repair_candidate = candidate
|
||||
_scenery_repair_path = path
|
||||
if _scenery_repair_confirm_dialog != null:
|
||||
_scenery_repair_confirm_dialog.dialog_text = "Use %s in this spot? Only this scenery piece will change." % candidate.display_name
|
||||
_scenery_repair_confirm_dialog.dialog_text = "Use %s in the selected spot?\n\nℹ️ Only this scenery piece will change." % candidate.display_name
|
||||
call_deferred("_popup_scenery_repair_confirm_after_frame")
|
||||
|
||||
|
||||
func _popup_scenery_repair_confirm_after_frame() -> void:
|
||||
await get_tree().process_frame
|
||||
if _scenery_repair_confirm_dialog != null and _scenery_repair_candidate != null and is_instance_valid(_scenery_repair_candidate):
|
||||
_scenery_repair_confirm_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_scenery_repair_confirm_dialog)
|
||||
|
||||
|
||||
func _confirm_scenery_repair() -> void:
|
||||
@@ -5667,7 +5612,7 @@ func _exit_scenery_edit_mode() -> void:
|
||||
_scenery_erase_piece_dialog.hide()
|
||||
_clear_erase_scenery_targets()
|
||||
if _scenery_repair_confirm_dialog != null:
|
||||
_scenery_repair_confirm_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_scenery_repair_confirm_dialog)
|
||||
_cancel_scenery_repair_preview()
|
||||
_scenery_repair_target = null
|
||||
if _scenery_inspector != null:
|
||||
@@ -6147,8 +6092,8 @@ func _request_scene_save(path: String = "", force_save_as: bool = false) -> void
|
||||
_speech_pending_scene_save_path = path
|
||||
_speech_pending_scene_save_as = force_save_as
|
||||
_speech_scene_save_pending = true
|
||||
if _speech_save_gate_dialog != null and not _speech_save_gate_dialog.visible:
|
||||
_speech_save_gate_dialog.popup_centered()
|
||||
if _speech_save_gate_dialog != null and not STAGE_DIALOG_STYLER.is_open(_speech_save_gate_dialog):
|
||||
STAGE_DIALOG_STYLER.popup(_speech_save_gate_dialog)
|
||||
return
|
||||
if _rule_edit_uncommitted:
|
||||
_show_toast("Finish the rule before saving the scene.")
|
||||
@@ -6207,12 +6152,12 @@ func _cancel_deferred_speech_scene_save(reason: String) -> void:
|
||||
|
||||
func _rename_scene_prompt() -> void:
|
||||
_scene_name_edit.text = _scene_name
|
||||
_rename_scene_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_rename_scene_dialog)
|
||||
call_deferred("_focus_scene_name_edit")
|
||||
|
||||
|
||||
func _focus_scene_name_edit() -> void:
|
||||
if _scene_name_edit == null or not _scene_name_edit.visible:
|
||||
if _scene_name_edit == null or not _scene_name_edit.visible or not _scene_name_edit.is_inside_tree():
|
||||
return
|
||||
_scene_name_edit.grab_focus()
|
||||
_scene_name_edit.select_all()
|
||||
@@ -6227,14 +6172,14 @@ func _request_pending_file_action(action: PendingFileAction, path: String = "")
|
||||
return
|
||||
_pending_file_action = action; _pending_open_path = path
|
||||
if _dirty:
|
||||
_unsaved_changes_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_unsaved_changes_dialog)
|
||||
else:
|
||||
_continue_pending_file_action()
|
||||
|
||||
|
||||
func _continue_pending_file_action() -> void:
|
||||
if _unsaved_changes_dialog != null:
|
||||
_unsaved_changes_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_unsaved_changes_dialog)
|
||||
var action := _pending_file_action; var path := _pending_open_path
|
||||
_pending_file_action = PendingFileAction.NONE; _pending_open_path = ""
|
||||
match action:
|
||||
@@ -6248,12 +6193,12 @@ func _continue_pending_file_action() -> void:
|
||||
|
||||
|
||||
func _on_unsaved_save() -> void:
|
||||
_unsaved_changes_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_unsaved_changes_dialog)
|
||||
_request_scene_save()
|
||||
|
||||
|
||||
func _on_unsaved_discard() -> void:
|
||||
_unsaved_changes_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_unsaved_changes_dialog)
|
||||
_continue_pending_file_action()
|
||||
|
||||
|
||||
@@ -6361,7 +6306,7 @@ func _on_action_popup_id_pressed(id: int) -> void:
|
||||
_open_speech_composer(_context_rig, {"type": "speak", "text": "", "duration": 2.0}, false, "direct")
|
||||
ACT_WAIT:
|
||||
_wait_spin.value = 1.0
|
||||
_wait_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_wait_dialog)
|
||||
ACT_RAGDOLL:
|
||||
_context_rig.queue_action({ "type": "ragdoll" })
|
||||
ACT_RECOVER:
|
||||
@@ -6657,7 +6602,7 @@ func _handle_action_target_click(world_pos: Vector2) -> void:
|
||||
"wait":
|
||||
_rule_step = RuleStep.PARAMS
|
||||
_wait_spin.value = 1.0
|
||||
_wait_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_wait_dialog)
|
||||
"pick_up_prop":
|
||||
_begin_capture(CaptureKind.PROP, "🫴 Click the prop to pick up (Esc or RMB to cancel)", _cb_rule_action_prop_target, _cb_rule_action_capture_cancel)
|
||||
"throw_prop":
|
||||
@@ -7521,9 +7466,9 @@ func _open_speech_composer(actor: StickmanRig, initial: Dictionary, reactive_opt
|
||||
var queue: Array[Dictionary] = actor.get_queue()
|
||||
if queue_index >= 0 and queue_index < queue.size() and String(queue[queue_index].get("type", "")) == "speak":
|
||||
speech_initial = queue[queue_index].duplicate(true)
|
||||
if _speech_recovery_matches_loaded_scene() and not _speech_recovery_dialog.visible and not _speech_recovery_in_progress:
|
||||
if _speech_recovery_matches_loaded_scene() and not STAGE_DIALOG_STYLER.is_open(_speech_recovery_dialog) and not _speech_recovery_in_progress:
|
||||
_speech_pending_open = {"actor_id": actor.get_instance_id(), "initial": speech_initial, "reactive": reactive_options, "context": context}
|
||||
_speech_recovery_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_speech_recovery_dialog)
|
||||
return
|
||||
_speech_actor = actor
|
||||
_speech_session_kind = context
|
||||
@@ -7547,7 +7492,7 @@ func _update_speech_composition() -> void:
|
||||
func _on_speech_committed(action: Dictionary) -> void:
|
||||
if _speech_session_kind == "recovered_unbound":
|
||||
_speech_pending_commit_action = action.duplicate(true)
|
||||
_speech_destination_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_speech_destination_dialog)
|
||||
return
|
||||
var speech_updates_uncommitted_rule: bool = _speech_session_kind == "rule_builder" or (
|
||||
_speech_session_kind == "action_editor" and _action_editor_kind in ["rule_add", "rule_edit"]
|
||||
@@ -7595,9 +7540,9 @@ func _finish_speech_composition(clear_recovery: bool = false) -> void:
|
||||
if _speech_session != null:
|
||||
_speech_session.close(clear_recovery, preserve_draft)
|
||||
if _speech_destination_dialog != null:
|
||||
_speech_destination_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_speech_destination_dialog)
|
||||
if _speech_save_gate_dialog != null:
|
||||
_speech_save_gate_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_speech_save_gate_dialog)
|
||||
_speech_restored_unbound = false
|
||||
|
||||
|
||||
@@ -7654,7 +7599,7 @@ func _canonicalize_speech_origin(origin: Dictionary) -> Dictionary:
|
||||
|
||||
func _check_speech_recovery() -> void:
|
||||
if _speech_recovery_dialog != null and _speech_recovery_matches_loaded_scene():
|
||||
_speech_recovery_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_speech_recovery_dialog)
|
||||
|
||||
|
||||
func _speech_recovery_matches_loaded_scene() -> bool:
|
||||
@@ -7983,9 +7928,21 @@ func _restore_queue_panel() -> void:
|
||||
|
||||
func _ask_confirm(title: String, message: String, callback: Callable) -> void:
|
||||
_confirm_callback = callback
|
||||
_confirm_dialog.title = title
|
||||
var normalized_title: String = title.to_lower()
|
||||
var destructive: bool = normalized_title.begins_with("delete") or normalized_title.begins_with("clear") or normalized_title.begins_with("remove")
|
||||
_confirm_dialog.title = ("🗑️ " if destructive else "❔ ") + title
|
||||
_confirm_dialog.dialog_text = message
|
||||
_confirm_dialog.popup_centered()
|
||||
if destructive:
|
||||
_confirm_dialog.ok_button_text = "Delete" if normalized_title.begins_with("delete") else ("Remove" if normalized_title.begins_with("remove") else "Clear")
|
||||
_confirm_dialog.cancel_button_text = "Keep"
|
||||
_apply_dialog_theme(_confirm_dialog, _confirm_dialog.get_cancel_button(), [_confirm_dialog.get_ok_button()])
|
||||
else:
|
||||
_confirm_dialog.ok_button_text = "Continue"
|
||||
_confirm_dialog.cancel_button_text = "Cancel"
|
||||
_apply_dialog_theme(_confirm_dialog)
|
||||
STAGE_DIALOG_STYLER.popup(_confirm_dialog)
|
||||
if destructive:
|
||||
STAGE_DIALOG_STYLER.visual_button(_confirm_dialog.get_cancel_button()).grab_focus()
|
||||
|
||||
|
||||
func _on_confirm_confirmed() -> void:
|
||||
|
||||
@@ -4,6 +4,7 @@ extends Control
|
||||
|
||||
const SPEECH_PAYLOAD := preload("res://scripts/speech/speech_payload.gd")
|
||||
const SANDBOX_THEME := preload("res://scripts/stage/sandbox_theme.gd")
|
||||
const STAGE_DIALOG_STYLER := preload("res://scripts/shared/stage_ui/stage_dialog_styler.gd")
|
||||
const SPEECH_BUBBLE := preload("res://scripts/speech/stickman_speech_bubble.gd")
|
||||
const FIVE_BUBBLE_HINT: String = "Long story? One at a time can be easier to follow."
|
||||
const MAX_BUBBLE_HINT: String = "You can add up to eight bubbles."
|
||||
@@ -510,7 +511,7 @@ func preview_elapsed() -> float:
|
||||
|
||||
|
||||
func contains_viewport_point(point: Vector2) -> bool:
|
||||
if _cancel_confirm_dialog != null and _cancel_confirm_dialog.visible:
|
||||
if _cancel_confirm_dialog != null and STAGE_DIALOG_STYLER.is_open(_cancel_confirm_dialog):
|
||||
return true
|
||||
if is_message_dialog_open():
|
||||
return true
|
||||
@@ -608,7 +609,7 @@ func close_without_commit() -> void:
|
||||
if _message_dialog != null:
|
||||
_message_dialog.hide()
|
||||
if _cancel_confirm_dialog != null:
|
||||
_cancel_confirm_dialog.hide()
|
||||
STAGE_DIALOG_STYLER.hide(_cancel_confirm_dialog)
|
||||
visible = false
|
||||
_replay_running = false
|
||||
_actor = null
|
||||
@@ -623,7 +624,8 @@ func set_cancel_requires_confirmation(required: bool) -> void:
|
||||
func request_cancel_speech() -> void:
|
||||
var changed: bool = _cancel_requires_confirmation or get_draft() != _initial_action
|
||||
if changed and _cancel_confirm_dialog != null:
|
||||
_cancel_confirm_dialog.popup_centered()
|
||||
STAGE_DIALOG_STYLER.popup(_cancel_confirm_dialog)
|
||||
STAGE_DIALOG_STYLER.visual_button(_cancel_confirm_dialog.get_cancel_button()).grab_focus()
|
||||
return
|
||||
cancelled.emit()
|
||||
|
||||
@@ -710,13 +712,21 @@ func _build_ui() -> void:
|
||||
_active_editor.text_changed.connect(_on_active_text_changed)
|
||||
_active_editor.caret_changed.connect(_remember_active_editor_state)
|
||||
_cancel_confirm_dialog = ConfirmationDialog.new()
|
||||
_cancel_confirm_dialog.title = "Discard speech changes?"
|
||||
_cancel_confirm_dialog.title = "🗣️ Discard speech changes?"
|
||||
_cancel_confirm_dialog.dialog_text = "Your speech edits will be lost. Do you want to discard them?"
|
||||
_cancel_confirm_dialog.get_ok_button().text = "Discard changes"
|
||||
_cancel_confirm_dialog.get_cancel_button().text = "Keep editing"
|
||||
_cancel_confirm_dialog.min_size = Vector2i(560, 300)
|
||||
_cancel_confirm_dialog.confirmed.connect(func() -> void: cancelled.emit())
|
||||
_cancel_confirm_dialog.dialog_hide_on_ok = true
|
||||
add_child(_cancel_confirm_dialog)
|
||||
STAGE_DIALOG_STYLER.apply(
|
||||
_cancel_confirm_dialog,
|
||||
_theme,
|
||||
_theme.font(),
|
||||
_cancel_confirm_dialog.get_cancel_button(),
|
||||
[_cancel_confirm_dialog.get_ok_button()]
|
||||
)
|
||||
_build_message_dialog()
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const SPEECH_SHOW_TOOLBAR_SCRIPT := preload("res://scripts/speech/speech_show_to
|
||||
const WAYPOINT_CONTEXT_SCRIPT := preload("res://scripts/stage/waypoint_context.gd")
|
||||
const PROP_LIBRARY_SCRIPT := preload("res://scripts/props/prop_library.gd")
|
||||
const PROP_STAGE_ROLES := preload("res://scripts/props/prop_stage_roles.gd")
|
||||
const DIALOG_STYLER := preload("res://scripts/shared/stage_ui/stage_dialog_styler.gd")
|
||||
|
||||
signal object_selected(node: Node2D)
|
||||
|
||||
@@ -28,6 +29,7 @@ var scene_name_edit: LineEdit
|
||||
var missing_assets_dialog: AcceptDialog
|
||||
var cli_error_dialog: AcceptDialog
|
||||
var unsaved_changes_dialog: ConfirmationDialog
|
||||
var unsaved_discard_button: Button
|
||||
var rig_context_popup: PopupMenu
|
||||
var waypoint_context: PopupMenu
|
||||
var confirm_dialog: ConfirmationDialog
|
||||
@@ -51,7 +53,7 @@ func configure(sandbox_theme: SandboxTheme, ui_font: Font) -> void:
|
||||
|
||||
func build_action_dialogs(parent: Node) -> void:
|
||||
speak_dialog = AcceptDialog.new()
|
||||
speak_dialog.title = "Speak"
|
||||
speak_dialog.title = "🗣️ Speak"
|
||||
_speak_box = VBoxContainer.new()
|
||||
speak_dialog.add_child(_speak_box)
|
||||
speak_edit = LineEdit.new()
|
||||
@@ -67,7 +69,7 @@ func build_action_dialogs(parent: Node) -> void:
|
||||
apply_dialog_theme(speak_dialog)
|
||||
|
||||
wait_dialog = AcceptDialog.new()
|
||||
wait_dialog.title = "Wait"
|
||||
wait_dialog.title = "⏱️ Wait"
|
||||
wait_spin = SpinBox.new()
|
||||
wait_spin.min_value = 0.1
|
||||
wait_spin.max_value = 10.0
|
||||
@@ -97,7 +99,7 @@ func build_file_dialogs(parent: Node) -> void:
|
||||
|
||||
speech_save_gate_dialog = ConfirmationDialog.new()
|
||||
speech_save_gate_dialog.name = "FinishSpeechBeforeSaveDialog"
|
||||
speech_save_gate_dialog.title = "Finish speech before saving"
|
||||
speech_save_gate_dialog.title = "✏️ Finish speech before saving"
|
||||
speech_save_gate_dialog.dialog_text = "Choose Done speech to finish editing before the scene is saved."
|
||||
speech_save_gate_dialog.ok_button_text = "Done speech"
|
||||
speech_save_gate_dialog.get_cancel_button().text = "Keep editing"
|
||||
@@ -106,50 +108,64 @@ func build_file_dialogs(parent: Node) -> void:
|
||||
|
||||
group_preview_save_dialog = ConfirmationDialog.new()
|
||||
group_preview_save_dialog.name = "GroupPreviewSaveDialog"
|
||||
group_preview_save_dialog.title = "Finish scenery preview?"
|
||||
group_preview_save_dialog.title = "🧱 Finish scenery preview?"
|
||||
group_preview_save_dialog.dialog_text = "Keep the scenery changes before saving, or cancel this save."
|
||||
group_preview_save_dialog.ok_button_text = "Keep changes"
|
||||
group_preview_save_dialog.cancel_button_text = "Cancel save"
|
||||
group_preview_save_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
group_preview_save_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
parent.add_child(group_preview_save_dialog)
|
||||
apply_dialog_theme(group_preview_save_dialog)
|
||||
|
||||
rename_scene_dialog = ConfirmationDialog.new()
|
||||
rename_scene_dialog.name = "RenameSceneDialog"
|
||||
rename_scene_dialog.title = "Rename Scene"
|
||||
rename_scene_dialog.title = "📝 Rename Scene"
|
||||
rename_scene_dialog.ok_button_text = "Save name"
|
||||
rename_scene_dialog.cancel_button_text = "Cancel"
|
||||
rename_scene_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
rename_scene_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
var scene_name_hint: Label = Label.new()
|
||||
scene_name_hint.name = "SceneNameHint"
|
||||
scene_name_hint.text = "Choose a short name that helps you find this scene later."
|
||||
scene_name_hint.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
scene_name_hint.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
var scene_name_label: Label = Label.new()
|
||||
scene_name_label.name = "SceneNameLabel"
|
||||
scene_name_label.text = "Scene name"
|
||||
scene_name_edit = LineEdit.new()
|
||||
scene_name_edit.name = "SceneNameEdit"
|
||||
scene_name_edit.placeholder_text = "Scene name"
|
||||
scene_name_edit.custom_minimum_size = Vector2(460.0, 0.0)
|
||||
scene_name_edit.custom_minimum_size = Vector2(460.0, 48.0)
|
||||
rename_scene_dialog.add_child(scene_name_hint)
|
||||
rename_scene_dialog.add_child(scene_name_label)
|
||||
rename_scene_dialog.add_child(scene_name_edit)
|
||||
parent.add_child(rename_scene_dialog)
|
||||
apply_dialog_theme(rename_scene_dialog)
|
||||
|
||||
missing_assets_dialog = AcceptDialog.new()
|
||||
missing_assets_dialog.name = "MissingAssetsDialog"
|
||||
missing_assets_dialog.title = "Missing Scene Assets"
|
||||
missing_assets_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
missing_assets_dialog.title = "⚠️ Some scene assets are missing"
|
||||
missing_assets_dialog.ok_button_text = "Got it"
|
||||
missing_assets_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
parent.add_child(missing_assets_dialog)
|
||||
apply_dialog_theme(missing_assets_dialog)
|
||||
|
||||
cli_error_dialog = AcceptDialog.new()
|
||||
cli_error_dialog.name = "CliErrorDialog"
|
||||
cli_error_dialog.title = "Could Not Load Scene"
|
||||
cli_error_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
cli_error_dialog.title = "⚠️ Could not load scene"
|
||||
cli_error_dialog.ok_button_text = "Got it"
|
||||
cli_error_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
parent.add_child(cli_error_dialog)
|
||||
apply_dialog_theme(cli_error_dialog)
|
||||
|
||||
unsaved_changes_dialog = ConfirmationDialog.new()
|
||||
unsaved_changes_dialog.name = "UnsavedChangesDialog"
|
||||
unsaved_changes_dialog.title = "⚠️ Unsaved Changes"
|
||||
unsaved_changes_dialog.title = "💾 Unsaved Changes"
|
||||
unsaved_changes_dialog.dialog_text = "Would you like to save your scene before continuing?"
|
||||
unsaved_changes_dialog.ok_button_text = "💾 Save"
|
||||
unsaved_changes_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
unsaved_changes_dialog.add_button("🗑️ Discard", false, "discard")
|
||||
unsaved_changes_dialog.ok_button_text = "Save scene"
|
||||
unsaved_changes_dialog.cancel_button_text = "Cancel"
|
||||
unsaved_changes_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
unsaved_discard_button = unsaved_changes_dialog.add_button("Discard", false, "discard")
|
||||
parent.add_child(unsaved_changes_dialog)
|
||||
apply_dialog_theme(unsaved_changes_dialog)
|
||||
apply_dialog_theme(unsaved_changes_dialog, unsaved_changes_dialog.get_ok_button(), [unsaved_discard_button])
|
||||
|
||||
|
||||
func build_context_and_editor_views(
|
||||
@@ -169,7 +185,11 @@ func build_context_and_editor_views(
|
||||
parent.add_child(waypoint_context)
|
||||
|
||||
confirm_dialog = ConfirmationDialog.new()
|
||||
confirm_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(620, 300))
|
||||
confirm_dialog.title = "❔ Confirm this change"
|
||||
confirm_dialog.dialog_text = "This action updates the current scene. Do you want to continue?"
|
||||
confirm_dialog.ok_button_text = "Continue"
|
||||
confirm_dialog.cancel_button_text = "Cancel"
|
||||
confirm_dialog.min_size = theme_dialog_size("confirm_size", Vector2i(560, 300))
|
||||
parent.add_child(confirm_dialog)
|
||||
apply_dialog_theme(confirm_dialog)
|
||||
|
||||
@@ -196,18 +216,20 @@ func build_context_and_editor_views(
|
||||
parent.add_child(speech_show_toolbar)
|
||||
|
||||
speech_recovery_dialog = ConfirmationDialog.new()
|
||||
speech_recovery_dialog.title = "Speech draft found"
|
||||
speech_recovery_dialog.dialog_text = "Restore your unsaved speech draft?"
|
||||
speech_recovery_dialog.get_ok_button().text = "Restore"
|
||||
speech_recovery_dialog.get_cancel_button().text = "Discard"
|
||||
speech_recovery_dialog.title = "🗣️ Speech draft found"
|
||||
speech_recovery_dialog.dialog_text = "There is an unsaved speech draft. Would you like to restore it?\n\nℹ️ Restore brings back the draft so you can continue editing."
|
||||
speech_recovery_dialog.get_ok_button().text = "Restore draft"
|
||||
speech_recovery_dialog.get_cancel_button().text = "Discard draft"
|
||||
parent.add_child(speech_recovery_dialog)
|
||||
apply_dialog_theme(speech_recovery_dialog, speech_recovery_dialog.get_ok_button(), [speech_recovery_dialog.get_cancel_button()])
|
||||
|
||||
speech_destination_dialog = ConfirmationDialog.new()
|
||||
speech_destination_dialog.title = "Save recovered speech"
|
||||
speech_destination_dialog.title = "🎭 Save recovered speech"
|
||||
speech_destination_dialog.dialog_text = "Add this speech to the chosen character’s action queue?"
|
||||
speech_destination_dialog.get_ok_button().text = "Add to queue"
|
||||
speech_destination_dialog.get_cancel_button().text = "Keep editing"
|
||||
parent.add_child(speech_destination_dialog)
|
||||
apply_dialog_theme(speech_destination_dialog)
|
||||
|
||||
|
||||
func show_objects_here(
|
||||
@@ -310,35 +332,11 @@ func theme_dialog_size(key: String, fallback: Vector2i) -> Vector2i:
|
||||
return fallback
|
||||
|
||||
|
||||
## Dialogs are separate native/embedded windows, so apply local visual overrides.
|
||||
func apply_dialog_theme(dialog: Window) -> void:
|
||||
## Keep the native dialog objects as signal sources and draw their shared surface in the stage CanvasLayer.
|
||||
func apply_dialog_theme(dialog: Window, primary_button: Button = null, destructive_buttons: Array = []) -> void:
|
||||
if dialog == null:
|
||||
return
|
||||
var size: int = _sandbox_theme.integer("chrome.dialogs.font_size", 22, 12, 48) if _sandbox_theme != null else 22
|
||||
var message_size: int = _sandbox_theme.integer("chrome.dialogs.message_font_size", 26, 12, 64) if _sandbox_theme != null else 26
|
||||
dialog.add_theme_font_size_override("font_size", size)
|
||||
if _ui_font != null:
|
||||
dialog.add_theme_font_override("font", _ui_font)
|
||||
if dialog is AcceptDialog:
|
||||
var message: Label = (dialog as AcceptDialog).get_label()
|
||||
if message != null:
|
||||
message.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
message.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
message.add_theme_font_size_override("font_size", message_size)
|
||||
if _ui_font != null:
|
||||
message.add_theme_font_override("font", _ui_font)
|
||||
var ok: Button = (dialog as AcceptDialog).get_ok_button()
|
||||
if ok != null:
|
||||
ok.add_theme_font_size_override("font_size", size)
|
||||
if _ui_font != null:
|
||||
ok.add_theme_font_override("font", _ui_font)
|
||||
if dialog is ConfirmationDialog:
|
||||
var cancel: Button = (dialog as ConfirmationDialog).get_cancel_button()
|
||||
if cancel != null:
|
||||
cancel.add_theme_font_size_override("font_size", size)
|
||||
if _ui_font != null:
|
||||
cancel.add_theme_font_override("font", _ui_font)
|
||||
_apply_dialog_font_to_children(dialog, size)
|
||||
DIALOG_STYLER.apply(dialog, _sandbox_theme, _ui_font, primary_button, destructive_buttons)
|
||||
|
||||
|
||||
func apply_dialog_font_to_children(node: Node, size: int) -> void:
|
||||
|
||||
Reference in New Issue
Block a user