Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native global menu support (macOS) #644

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ SVG="*res://src/SVG.gd"
Indications="*res://src/Indications.gd"
HandlerGUI="*res://src/HandlerGUI.gd"

[debug]

gdscript/warnings/int_as_enum_without_cast=0
gdscript/warnings/int_as_enum_without_match=0

[display]

window/size/viewport_width=1024
Expand Down Expand Up @@ -309,7 +314,8 @@ view_overlay_reference={
}
find={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":70,"physical_keycode":0,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":70,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":true,"pressed":false,"keycode":70,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}

Expand Down
4 changes: 4 additions & 0 deletions src/GlobalSettings.gd
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# This singleton handles session data and settings.
extends Node


signal keybinds_changed

# Session data
var save_data := SaveData.new()
const save_path = "user://save.tres"
Expand Down Expand Up @@ -228,6 +231,7 @@ func save_palettes() -> void:
func save_keybind(action: String) -> void:
config.set_value("keybinds", action, InputMap.action_get_events(action))
config.save(config_path)
keybinds_changed.emit()


func modify_save_data(property: String, new_value: Variant) -> void:
Expand Down
8 changes: 5 additions & 3 deletions src/TranslationUtils.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ class_name TranslationUtils extends RefCounted

static func get_shortcut_description(action_name: String) -> String:
match action_name:
"export": return TranslationServer.translate("Export")
"import": return TranslationServer.translate("Import")
"save": return TranslationServer.translate("Save SVG")
"export": return TranslationServer.translate("Export Image")
"import": return TranslationServer.translate("Import SVG")
"save": return TranslationServer.translate("Export SVG")
"optimize": return TranslationServer.translate("Optimize")
"copy_svg_text": return TranslationServer.translate("Copy all text")
"reset_svg": return TranslationServer.translate("Reset SVG")
Expand All @@ -17,12 +17,14 @@ static func get_shortcut_description(action_name: String) -> String:
"delete": return TranslationServer.translate("Delete the selection")
"move_up": return TranslationServer.translate("Move the selected elements up")
"move_down": return TranslationServer.translate("Move the selected elements down")
"find": return TranslationServer.translate("Find")
"zoom_in": return TranslationServer.translate("Zoom in")
"zoom_out": return TranslationServer.translate("Zoom out")
"zoom_reset": return TranslationServer.translate("Zoom reset")
"view_show_grid": return TranslationServer.translate("Show grid")
"view_show_handles": return TranslationServer.translate("Show handles")
"view_rasterized_svg": return TranslationServer.translate("Show rasterized SVG")
"snap_toggle": return TranslationServer.translate("Enable snap")
"load_reference": return TranslationServer.translate("Load reference image")
"view_show_reference": return TranslationServer.translate("Show reference image")
"view_overlay_reference": return TranslationServer.translate("Overlay reference image")
Expand Down
223 changes: 223 additions & 0 deletions src/ui_parts/global_menu.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
extends Node


var global_rid: RID
var appl_rid: RID
var help_rid: RID

var file_rid: RID
var file_index: int
var file_clear_svg_index: int
var file_optimize_index: int
var file_clear_assoc_index: int
var file_reset_svg_index: int

var edit_rid: RID
var edit_index: int
var tool_rid: RID
var tool_index: int

var view_rid: RID
var view_index: int
var view_show_grid_index: int
var view_show_handles_index: int
var view_rasterized_svg_index: int

var snap_rid: RID
var snap_index: int
var snap_enable_index: int
var snap_0125_index: int
var snap_025_index: int
var snap_05_index: int
var snap_1_index: int
var snap_2_index: int
var snap_4_index: int


func _enter_tree() -> void:
if not DisplayServer.has_feature(DisplayServer.FEATURE_GLOBAL_MENU):
queue_free()
return
# Included menus.
global_rid = NativeMenu.get_system_menu(NativeMenu.MAIN_MENU_ID)
appl_rid = NativeMenu.get_system_menu(NativeMenu.APPLICATION_MENU_ID)
help_rid = NativeMenu.get_system_menu(NativeMenu.HELP_MENU_ID)
# Custom menus.
_generate_main_menus()
_setup_menu_items()
GlobalSettings.keybinds_changed.connect(_reset_menu_items)
SVG.text_changed.connect(_on_svg_text_changed)


func _notification(what: int) -> void:
if what == Utils.CustomNotification.LANGUAGE_CHANGED:
_clear_menu_items()
NativeMenu.remove_item(global_rid, snap_index)
NativeMenu.remove_item(global_rid, view_index)
NativeMenu.remove_item(global_rid, tool_index)
NativeMenu.remove_item(global_rid, edit_index)
NativeMenu.remove_item(global_rid, file_index)
NativeMenu.free_menu(file_rid)
NativeMenu.free_menu(edit_rid)
NativeMenu.free_menu(tool_rid)
NativeMenu.free_menu(view_rid)
NativeMenu.free_menu(snap_rid)
_generate_main_menus()
_setup_menu_items()


func _generate_main_menus() -> void:
file_rid = NativeMenu.create_menu()
edit_rid = NativeMenu.create_menu()
tool_rid = NativeMenu.create_menu()
view_rid = NativeMenu.create_menu()
snap_rid = NativeMenu.create_menu()
file_index = NativeMenu.add_submenu_item(global_rid, TranslationServer.translate("File"), file_rid)
edit_index = NativeMenu.add_submenu_item(global_rid, TranslationServer.translate("Edit"), edit_rid)
tool_index = NativeMenu.add_submenu_item(global_rid, TranslationServer.translate("Tool"), tool_rid)
view_index = NativeMenu.add_submenu_item(global_rid, TranslationServer.translate("View"), view_rid)
snap_index = NativeMenu.add_submenu_item(global_rid, TranslationServer.translate("Snap"), snap_rid)


func _reset_menu_items() -> void:
_setup_menu_items()


func _clear_menu_items() -> void:
NativeMenu.clear(appl_rid)
NativeMenu.clear(help_rid)
NativeMenu.clear(file_rid)
NativeMenu.clear(edit_rid)
NativeMenu.clear(tool_rid)
NativeMenu.clear(view_rid)
NativeMenu.clear(snap_rid)


func _setup_menu_items() -> void:
# Included App and Help menus.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TranslationUtils should be used here to get the translated string of "open_settings". Same for the lines of code below. That way it's ensured that they are the same everywhere.

_add_action(appl_rid, "open_settings")
_add_icon_item(help_rid, "open_settings", load("res://visual/icons/Gear.svg"))
_add_icon_item(help_rid, "about_repo", load("res://visual/icons/Link.svg"))
_add_icon_item(help_rid, "about_info", load("res://visual/icon.svg"))
_add_icon_item(help_rid, "about_donate", load("res://visual/icons/Heart.svg"))
_add_icon_item(help_rid, "about_website", load("res://visual/icons/Link.svg"))
_add_icon_item(help_rid, "check_updates", load("res://visual/icons/Reload.svg"))
# File menu.
_add_action(file_rid, "import")
_add_action(file_rid, "export")
_add_action(file_rid, "save")
NativeMenu.add_separator(file_rid)
_add_action(file_rid, "copy_svg_text")
file_clear_svg_index = _add_action(file_rid, "clear_svg")
file_optimize_index = _add_action(file_rid, "optimize")
NativeMenu.add_separator(file_rid)
file_clear_assoc_index = _add_action(file_rid, "clear_file_path")
file_reset_svg_index = _add_action(file_rid, "reset_svg")
_on_svg_text_changed()
# Edit and Tool menus.
_add_many_actions(edit_rid, GlobalSettings.keybinds_dict["edit"].keys())
_add_many_actions(tool_rid, GlobalSettings.keybinds_dict["tool"].keys())
# View menu.
view_show_grid_index = _add_check_item(view_rid, "view_show_grid")
view_show_handles_index = _add_check_item(view_rid, "view_show_handles")
view_rasterized_svg_index = _add_check_item(view_rid, "view_rasterized_svg")
_on_display_view_settings_updated(true, true, false)
NativeMenu.add_separator(view_rid)
_add_action(view_rid, "zoom_in")
_add_action(view_rid, "zoom_out")
_add_action(view_rid, "zoom_reset")
# Snap menu.
snap_enable_index = _add_check_item(snap_rid, "snap_toggle")
NativeMenu.add_separator(snap_rid)
snap_0125_index = NativeMenu.add_radio_check_item(snap_rid, "0.125", _set_snap, _set_snap, 0.125)
snap_025_index = NativeMenu.add_radio_check_item(snap_rid, "0.25", _set_snap, _set_snap, 0.25)
snap_05_index = NativeMenu.add_radio_check_item(snap_rid, "0.5", _set_snap, _set_snap, 0.5)
snap_1_index = NativeMenu.add_radio_check_item(snap_rid, "1", _set_snap, _set_snap, 1)
snap_2_index = NativeMenu.add_radio_check_item(snap_rid, "2", _set_snap, _set_snap, 2)
snap_4_index = NativeMenu.add_radio_check_item(snap_rid, "4", _set_snap, _set_snap, 4)


func _add_many_actions(menu_rid: RID, actions: Array) -> void:
for action in actions:
_add_action(menu_rid, action)


func _add_action(menu_rid: RID, action_name: StringName) -> int:
var display_name = _get_action_display_name(action_name)
var key = _get_keycode_for_events(InputMap.action_get_events(action_name))
return NativeMenu.add_item(menu_rid, display_name, _action_call, _action_call, action_name, key)


func _add_check_item(menu_rid: RID, action_name: StringName) -> int:
var display_name = _get_action_display_name(action_name)
return NativeMenu.add_check_item(menu_rid, display_name, _action_call, _action_call, action_name)


func _add_icon_item(menu_rid: RID, action_name: StringName, icon: Texture2D) -> int:
var display_name = _get_action_display_name(action_name)
return NativeMenu.add_icon_item(menu_rid, icon, display_name, _action_call, _action_call, action_name)


func _get_action_display_name(action_name: StringName) -> String:
var display_name = TranslationUtils.get_shortcut_description(action_name)
if display_name.is_empty():
display_name = action_name.capitalize().replace("Svg", "SVG")
return display_name


func _get_keycode_for_events(input_events: Array[InputEvent]) -> Key:
for input_event in input_events:
if input_event is InputEventKey:
var key = input_event.get_keycode_with_modifiers()
if key != KEY_NONE:
return key
key = input_event.get_physical_keycode_with_modifiers()
if key != KEY_NONE:
return key
return KEY_NONE


func _on_svg_text_changed() -> void:
NativeMenu.set_item_disabled(file_rid, file_clear_svg_index, SVG.text == SVG.DEFAULT)
var empty_path: bool = GlobalSettings.save_data.current_file_path.is_empty()
NativeMenu.set_item_disabled(file_rid, file_clear_assoc_index, empty_path)
NativeMenu.set_item_disabled(file_rid, file_reset_svg_index, empty_path)


func _on_display_view_settings_updated(show_grid: bool, show_handles: bool, rasterized_svg: bool) -> void:
NativeMenu.set_item_checked(view_rid, view_show_grid_index, show_grid)
NativeMenu.set_item_checked(view_rid, view_show_handles_index, show_handles)
NativeMenu.set_item_checked(view_rid, view_rasterized_svg_index, rasterized_svg)


func _on_display_snap_settings_updated(snap_enabled: bool, snap_amount: float) -> void:
NativeMenu.set_item_checked(snap_rid, snap_enable_index, snap_enabled)
NativeMenu.set_item_checked(snap_rid, snap_0125_index, false)
NativeMenu.set_item_checked(snap_rid, snap_025_index, false)
NativeMenu.set_item_checked(snap_rid, snap_05_index, false)
NativeMenu.set_item_checked(snap_rid, snap_1_index, false)
NativeMenu.set_item_checked(snap_rid, snap_2_index, false)
NativeMenu.set_item_checked(snap_rid, snap_4_index, false)
if is_equal_approx(snap_amount, 0.125):
NativeMenu.set_item_checked(snap_rid, snap_0125_index, true)
elif is_equal_approx(snap_amount, 0.25):
NativeMenu.set_item_checked(snap_rid, snap_025_index, true)
elif is_equal_approx(snap_amount, 0.5):
NativeMenu.set_item_checked(snap_rid, snap_05_index, true)
elif is_equal_approx(snap_amount, 1):
NativeMenu.set_item_checked(snap_rid, snap_1_index, true)
elif is_equal_approx(snap_amount, 2):
NativeMenu.set_item_checked(snap_rid, snap_2_index, true)
elif is_equal_approx(snap_amount, 4):
NativeMenu.set_item_checked(snap_rid, snap_4_index, true)


func _set_snap(tag: float) -> void:
%Display.set_snap_amount(tag)


func _action_call(tag: StringName) -> void:
var a = InputEventAction.new()
a.action = tag
a.pressed = true
Input.parse_input_event(a)
9 changes: 8 additions & 1 deletion src/ui_parts/main_scene.tscn
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[gd_scene load_steps=6 format=3 uid="uid://ce6j54x27pom"]
[gd_scene load_steps=7 format=3 uid="uid://ce6j54x27pom"]

[ext_resource type="PackedScene" uid="uid://cr1fdlmbknnko" path="res://src/ui_parts/code_editor.tscn" id="1_0jgh3"]
[ext_resource type="Texture2D" uid="uid://co75w07yqmcro" path="res://visual/icons/theme/SplitGrabber2.svg" id="1_7y812"]
[ext_resource type="PackedScene" uid="uid://ccynisiuyn5qn" path="res://src/ui_parts/inspector.tscn" id="1_afxvd"]
[ext_resource type="Script" path="res://src/ui_parts/main_scene.gd" id="1_c0fkj"]
[ext_resource type="PackedScene" uid="uid://bvrncl7e6yn5b" path="res://src/ui_parts/display.tscn" id="3_qbqbs"]
[ext_resource type="Script" path="res://src/ui_parts/global_menu.gd" id="5_wda5e"]

[node name="MainScene" type="HBoxContainer"]
anchors_preset = 15
Expand Down Expand Up @@ -42,3 +43,9 @@ layout_mode = 2
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3

[node name="GlobalMenu" type="Node" parent="."]
script = ExtResource("5_wda5e")

[connection signal="snap_settings_updated" from="HSplitContainer/Display" to="GlobalMenu" method="_on_display_snap_settings_updated"]
[connection signal="view_settings_updated" from="HSplitContainer/Display" to="GlobalMenu" method="_on_display_view_settings_updated"]
Loading