Skip to content

Commit

Permalink
Fix the path field focus bug and others (#759)
Browse files Browse the repository at this point in the history
  • Loading branch information
MewPurPur authored May 25, 2024
1 parent 4d16b40 commit 83d62f1
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 88 deletions.
13 changes: 7 additions & 6 deletions src/HandlerGUI.gd
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ func _parse_popup_overlay_event(event: InputEvent) -> void:
var last_mouse_click_double := false

func _input(event: InputEvent) -> void:
if event.is_action_pressed("quit"):
var confirm_dialog := ConfirmDialog.instantiate()
add_overlay(confirm_dialog)
confirm_dialog.setup(TranslationServer.translate("Quit GodSVG"),
TranslationServer.translate("Do you want to quit GodSVG?"),
TranslationServer.translate("Quit"), get_tree().quit)

# Clear popups or overlays.
if not popup_overlay_stack.is_empty() and event.is_action_pressed("ui_cancel"):
get_viewport().set_input_as_handled()
Expand Down Expand Up @@ -195,12 +202,6 @@ func _unhandled_input(event: InputEvent) -> void:
elif event.is_action_pressed("undo"):
get_viewport().set_input_as_handled()
SVG.undo()
elif event.is_action_pressed("quit"):
var confirm_dialog := ConfirmDialog.instantiate()
add_overlay(confirm_dialog)
confirm_dialog.setup(TranslationServer.translate("Quit GodSVG"),
TranslationServer.translate("Do you want to quit GodSVG?"),
TranslationServer.translate("Quit"), get_tree().quit)

if get_viewport().gui_is_dragging():
return
Expand Down
4 changes: 3 additions & 1 deletion src/ThemeGenerator.gd
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,9 @@ static func setup_lineedit(theme: Theme) -> void:

var mini_stylebox_hover := mini_stylebox.duplicate()
mini_stylebox_hover.draw_center = false
mini_stylebox_hover.border_color = line_edit_hover_border_overlay_color
var mini_line_edit_hover_border_overlay_color := line_edit_hover_border_overlay_color
mini_line_edit_hover_border_overlay_color.a *= 1.5
mini_stylebox_hover.border_color = mini_line_edit_hover_border_overlay_color
theme.set_stylebox("hover", "MiniLineEdit", mini_stylebox_hover)

var mini_stylebox_pressed := mini_stylebox.duplicate()
Expand Down
200 changes: 122 additions & 78 deletions src/ui_elements/pathdata_field.gd
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,20 @@ var mini_line_edit_font_color := get_theme_color("font_color", "MiniLineEdit")
@onready var commands_container: Control = $Commands

# Variables around the big optimization.
var active_idx := -1
var fields: Array[Control] = []
# The idea is that when the mouse enters a strip, it's remembered as hovered.
# If a numfield is focused, its strip is remembered as focused.
# If a numfield is hovered and then focused, the controls aren't re-added, instead
# the references are moved from the hovered to the focused fields array.
# If a focused field is hovered, no hovered fields are added.
var hovered_idx := -1
var focused_idx := -1
var hovered_strip: Control
var focused_strip: Control

var current_selections: Array[int] = []
var current_hovered: int = -1
@onready var ci := commands_container.get_canvas_item()
var add_move_button: Control


func set_value(new_value: String, update_type := Utils.UpdateType.REGULAR) -> void:
Expand Down Expand Up @@ -81,28 +89,31 @@ func sync(new_value: String) -> void:
line_edit.text = new_value
# A plus button for adding a move command if empty.
var cmd_count := attribute.get_command_count()
if cmd_count == 0:
var add_move := Button.new()
add_move.icon = plus_icon
add_move.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
add_move.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
add_move.focus_mode = Control.FOCUS_NONE
add_move.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
add_move.theme_type_variation = "FlatButton"
add_child(add_move)
add_move.pressed.connect(attribute.insert_command.bind(0, "M"))
add_move.pressed.connect(add_move.queue_free)
if cmd_count == 0 and not is_instance_valid(add_move_button):
add_move_button = Button.new()
add_move_button.icon = plus_icon
add_move_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
add_move_button.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
add_move_button.focus_mode = Control.FOCUS_NONE
add_move_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
add_move_button.theme_type_variation = "FlatButton"
add_child(add_move_button)
add_move_button.pressed.connect(attribute.insert_command.bind(0, "M"))
add_move_button.pressed.connect(add_move_button.queue_free)
# Rebuild the path commands.
commands_container.custom_minimum_size.y = cmd_count * COMMAND_HEIGHT
activate_hovered(-1)
var mm := InputEventMouseMotion.new()
mm.position = get_viewport().get_mouse_position()
Input.parse_input_event(mm)
commands_container.queue_redraw()


func update_value(new_value: float, property: String) -> void:
attribute.set_command_property(active_idx, property, new_value)
func update_value(new_value: float, property: String, idx: int) -> void:
attribute.set_command_property(idx, property, new_value)

func _on_relative_button_pressed() -> void:
attribute.toggle_relative_command(active_idx)
activate(active_idx, true)
attribute.toggle_relative_command(hovered_idx)


# Path commands editor orchestration.
Expand All @@ -127,15 +138,19 @@ func _on_commands_mouse_exited() -> void:
var cmd_idx := Indications.inner_hovered
Indications.remove_hovered(tid, cmd_idx)
if Indications.semi_hovered_tid == tid:
for field in fields:
if field.has_focus():
active_idx = cmd_idx
# Should switch out the controls for fake outs.
if active_idx != cmd_idx:
fields = []
deactivate()
activate_hovered(-1)


# Prevents buttons from selecting a whole subpath when double-clicked.
func _eat_double_clicks(event: InputEvent, button: Button) -> void:
if hovered_idx != -1 and event is InputEventMouseButton and event.double_click:
button.accept_event()
if event.is_pressed():
if button.toggle_mode:
button.toggled.emit(not button.button_pressed)
else:
button.pressed.emit()

func _on_commands_gui_input(event: InputEvent) -> void:
if not event is InputEventMouse:
return
Expand All @@ -145,7 +160,7 @@ func _on_commands_gui_input(event: InputEvent) -> void:

if event is InputEventMouseMotion and event.button_mask == 0:
Indications.set_hovered(tid, cmd_idx)
activate(cmd_idx)
activate_hovered(cmd_idx)
elif event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.is_pressed():
Expand Down Expand Up @@ -175,7 +190,8 @@ func _on_commands_gui_input(event: InputEvent) -> void:
var viewport := get_viewport()
var popup_pos := viewport.get_mouse_position()
HandlerGUI.popup_under_pos(Indications.get_selection_context(
HandlerGUI.popup_under_pos.bind(popup_pos, viewport), Indications.SelectionContext.TAG_EDITOR), popup_pos, viewport)
HandlerGUI.popup_under_pos.bind(popup_pos, viewport),
Indications.SelectionContext.TAG_EDITOR), popup_pos, viewport)


func commands_draw() -> void:
Expand All @@ -199,7 +215,7 @@ func commands_draw() -> void:
COMMAND_HEIGHT)))
# Draw the child controls. They are going to be drawn, not added as a node unless
# the mouse hovers them. This is a hack to significantly improve performance.
if i == active_idx:
if i == hovered_idx or i == focused_idx:
continue

var cmd := attribute.get_command(i)
Expand Down Expand Up @@ -270,32 +286,59 @@ path_command: PathCommand) -> void:
first_rect.position.x = first_rect.end.x + spacings[i]
draw_numfield(first_rect, names[i + 1], path_command)

# Prevents buttons from selecting a whole subpath when double-clicked.
func _eat_double_clicks(event: InputEvent, button: Button) -> void:
if active_idx and event is InputEventMouseButton and event.double_click:
button.accept_event()
if event.is_pressed():
if button.toggle_mode:
button.toggled.emit(not button.button_pressed)
else:
button.pressed.emit()

func activate_hovered(idx: int) -> void:
if idx == hovered_idx or idx >= attribute.get_command_count():
return

if is_instance_valid(hovered_strip):
hovered_strip.queue_free()
if focused_idx != idx:
hovered_strip = setup_path_command_controls(idx)
hovered_idx = idx
commands_container.queue_redraw()

func activate(idx: int, force := false) -> void:
if not force and active_idx == idx:
func activate_focused(idx: int) -> void:
if idx == focused_idx:
return

for child in commands_container.get_children():
child.queue_free()
if is_instance_valid(focused_strip):
focused_strip.queue_free()
if hovered_idx != idx:
focused_strip = setup_path_command_controls(idx)
if idx == -1:
hovered_strip = setup_path_command_controls(hovered_idx)
focused_strip = null
else:
focused_strip = hovered_strip
hovered_strip = null
focused_idx = idx
commands_container.queue_redraw()

func check_focused() -> void:
for child in focused_strip.get_children():
if child.has_focus():
return
activate_focused(-1)

func setup_path_command_controls(idx: int) -> Control:
if idx < 0:
return null

var cmd := attribute.get_command(idx)
var cmd_char := cmd.command_char
active_idx = idx
var is_absolute := Utils.is_string_upper(cmd_char)

var container := Control.new()
container.position.y = idx * COMMAND_HEIGHT
container.size = Vector2(commands_container.size.x, COMMAND_HEIGHT)
container.mouse_filter = Control.MOUSE_FILTER_PASS
commands_container.add_child(container)
# Setup the relative button.
var relative_button := Button.new()
relative_button.focus_mode = Control.FOCUS_NONE
relative_button.mouse_filter = Control.MOUSE_FILTER_PASS
relative_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
var is_absolute := Utils.is_string_upper(cmd_char)
relative_button.begin_bulk_theme_override()
relative_button.add_theme_font_override("font", code_font)
relative_button.add_theme_font_size_override("font_size", 13)
Expand All @@ -314,25 +357,27 @@ func activate(idx: int, force := false) -> void:
[TranslationUtils.get_command_description(cmd_char),
TranslationServer.translate("Absolute") if is_absolute\
else TranslationServer.translate("Relative")]
commands_container.add_child(relative_button)
container.add_child(relative_button)
relative_button.pressed.connect(_on_relative_button_pressed)
relative_button.gui_input.connect(_eat_double_clicks.bind(relative_button))
relative_button.position = Vector2(3, 2 + idx * COMMAND_HEIGHT)
relative_button.size = Vector2(18, COMMAND_HEIGHT - 4)
relative_button.position = Vector2(3, 2)
relative_button.size = Vector2(COMMAND_HEIGHT - 4, COMMAND_HEIGHT - 4)
# Setup the action button.
var action_button := Button.new()
action_button.icon = more_icon
action_button.theme_type_variation = "FlatButton"
action_button.focus_mode = Control.FOCUS_NONE
action_button.mouse_filter = Control.MOUSE_FILTER_PASS
action_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
commands_container.add_child(action_button)
container.add_child(action_button)
action_button.pressed.connect(_on_action_button_pressed.bind(action_button))
action_button.gui_input.connect(_eat_double_clicks.bind(action_button))
action_button.position = Vector2(commands_container.size.x - 21,
2 + idx * COMMAND_HEIGHT)
action_button.position = Vector2(commands_container.size.x - 21, 2)
action_button.size = Vector2(COMMAND_HEIGHT - 4, COMMAND_HEIGHT - 4)
# Setup the fields.
var fields: Array[Control] = []
var spacings: Array[int] = []
var property_names: Array[String] = []
match cmd_char.to_upper():
"A":
var field_rx: BetterLineEdit = numfield(idx)
Expand All @@ -347,60 +392,59 @@ func activate(idx: int, force := false) -> void:
field_sweep.gui_input.connect(_eat_double_clicks.bind(field_sweep))
fields = [field_rx, field_ry, field_rot, field_large_arc, field_sweep,
numfield(idx), numfield(idx)]
setup_fields(cmd, [3, 4, 4, 4, 4, 3],
["rx", "ry", "rot", "large_arc_flag", "sweep_flag", "x", "y"])
spacings = [3, 4, 4, 4, 4, 3]
property_names = ["rx", "ry", "rot", "large_arc_flag", "sweep_flag", "x", "y"]
"C":
fields = [numfield(idx), numfield(idx), numfield(idx), numfield(idx),
numfield(idx), numfield(idx)]
setup_fields(cmd, [3, 4, 3, 4, 3], ["x1", "y1", "x2", "y2", "x", "y"])
spacings = [3, 4, 3, 4, 3]
property_names = ["x1", "y1", "x2", "y2", "x", "y"]
"Q":
fields = [numfield(idx), numfield(idx), numfield(idx), numfield(idx)]
setup_fields(cmd, [3, 4, 3], ["x1", "y1", "x", "y"])
spacings = [3, 4, 3]
property_names = ["x1", "y1", "x", "y"]
"S":
fields = [numfield(idx), numfield(idx), numfield(idx), numfield(idx)]
setup_fields(cmd, [3, 4, 3], ["x2", "y2", "x", "y"])
spacings = [3, 4, 3]
property_names = ["x2", "y2", "x", "y"]
"M", "L", "T":
fields = [numfield(idx), numfield(idx)]
setup_fields(cmd, [3], ["x", "y"])
spacings = [3]
property_names = ["x", "y"]
"H":
fields = [numfield(idx)]
setup_fields(cmd, [], ["x"])
property_names = ["x"]
"V":
fields = [numfield(idx)]
setup_fields(cmd, [], ["y"])
"Z": fields.clear()
# Remove the graphics, as now there are real nodes.
commands_container.queue_redraw()
property_names = ["y"]
# Setup the fields.
if not fields.is_empty():
for i in fields.size():
var field := fields[i]
var property_name := property_names[i]
field.set_value(cmd.get(property_name))
field.tooltip_text = property_name
field.value_changed.connect(update_value.bind(property_name, idx))
field.focus_entered.connect(activate_focused.bind(idx))
field.focus_exited.connect(check_focused)
container.add_child(field)
field.position.y = 2
fields[0].position.x = 25
for i in fields.size() - 1:
fields[i + 1].position.x = fields[i].get_end().x + spacings[i]
return container

func deactivate() -> void:
active_idx = -1
for child in commands_container.get_children():
child.queue_free()
commands_container.queue_redraw()

func numfield(cmd_idx: int) -> BetterLineEdit:
var new_field := MiniNumberField.instantiate()
new_field.focus_entered.connect(Indications.normal_select.bind(tid, cmd_idx))
return new_field

func setup_fields(path_command: PathCommand, spacings: Array, names: Array) -> void:
for i in fields.size():
var property_str: String = names[i]
fields[i].set_value(path_command.get(property_str))
fields[i].tooltip_text = property_str
fields[i].value_changed.connect(update_value.bind(property_str))
commands_container.add_child(fields[i])
fields[i].position.y = 2 + active_idx * COMMAND_HEIGHT

fields[0].position.x = 25
for i in fields.size() - 1:
fields[i + 1].position.x = fields[i].get_end().x + spacings[i]


func _on_action_button_pressed(action_button_ref: Button) -> void:
# Update the selection immediately, since if this path command is
# in a multi-selection, only the mouse button release would change the selection.
Indications.normal_select(tid, active_idx)
Indications.normal_select(tid, hovered_idx)
var viewport := get_viewport()
var action_button_rect := action_button_ref.get_global_rect()
HandlerGUI.popup_under_rect_center(Indications.get_selection_context(
Expand Down
6 changes: 3 additions & 3 deletions src/ui_parts/update_menu.gd
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ func display_results() -> void:
# Check if there are results to be displayed.
var has_results := false
if prereleases_checkbox.button_pressed:
has_results = results.is_empty()
has_results = not results.is_empty()
else:
for version in results:
if results[version][1] == true:
if results[version][1] == false:
has_results = true
break
# Set the text.
if has_results:
if not has_results:
status_label.text = TranslationServer.translate("GodSVG is up-to-date.")
return
else:
Expand Down

0 comments on commit 83d62f1

Please sign in to comment.