Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ env:
# Commit hash
GODOT_COMMIT_HASH: daa4b058ee9272dd4ee9033bb093afb21ad558b7
PROJECT_NAME: GodSVG Mobile
BUILD_OPTIONS: target=template_release lto=full production=yes deprecated=no minizip=no brotli=no vulkan=no openxr=no use_volk=no disable_3d=yes modules_enabled_by_default=no module_freetype_enabled=yes module_gdscript_enabled=yes module_svg_enabled=yes module_jpg_enabled=yes module_text_server_adv_enabled=yes graphite=no module_webp_enabled=yes swappy=no build_profile=../godsvg/.github/disabled_classes.build
BUILD_OPTIONS: target=template_release lto=full production=yes deprecated=no minizip=no brotli=no vulkan=no openxr=no use_volk=no disable_3d=yes modules_enabled_by_default=no module_freetype_enabled=yes module_gdscript_enabled=yes module_svg_enabled=yes module_jpg_enabled=yes module_text_server_adv_enabled=yes graphite=no module_webp_enabled=yes module_mbedtls_enabled=yes swappy=no build_profile=../godsvg/.github/disabled_classes.build
GODOT_REPO: https://github.com/godotengine/godot.git

jobs:
Expand Down
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Godot 4+ specific ignores
# Godot-specific ignores
.godot/
.DS_Store
.nomedia

# Imported translations (automatically generated from CSV files)
*.translation
# Others
.~lock.*
.DS_Store
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Contributions don't need to be perfect, but they must move GodSVG in the right d

GodSVG is made in Godot using its GDScript language. Refer to the [README](https://github.com/MewPurPur/GodSVG?tab=readme-ov-file#how-to-get-it) on how to get GodSVG running.

Git must be configured, then you can clone the repository to your local machine: `git clone git@github.com:MewPurPur/GodSVG.git`
Git must be configured, then you can clone the repository to your local machine: `git clone https://github.com/MewPurPur/GodSVG.git`

The documentation won't go into detail about how to use Git. Refer to outside resources if you are unfamiliar with it.

Expand Down
1 change: 1 addition & 0 deletions assets/icons/Expand.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions assets/icons/Expand.svg.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://0xptbhyrvsk1"
path="res://.godot/imported/Expand.svg-54a09277a628926ed67d8eb42ef8926e.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://assets/icons/Expand.svg"
dest_files=["res://.godot/imported/Expand.svg-54a09277a628926ed67d8eb42ef8926e.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
2 changes: 1 addition & 1 deletion assets/icons/PresetGrayscale.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion export_presets.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ permissions/install_location_provider=false
permissions/install_packages=false
permissions/install_shortcut=false
permissions/internal_system_window=false
permissions/internet=false
permissions/internet=true
permissions/kill_background_processes=false
permissions/location_hardware=false
permissions/manage_accounts=false
Expand Down
3 changes: 2 additions & 1 deletion godot_only/scripts/tests.gd
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ func pathdata_tests(print_success := false) -> bool:
"M 0 0 z 2 3": [["M", 0.0, 0.0], ["z"]],
"M3e1 4e-2": [["M", 3e1, 4e-2]],
"M5,1 A4,4,0,1,1,5,9": [["M", 5.0, 1.0], ["A", 4.0, 4.0, 0.0, 1, 1, 5.0, 9.0]],
"M4 1 2 - 4 4z": [["M", 4.0, 1.0]]
"M4 1 2 - 4 4z": [["M", 4.0, 1.0]],
"M1 6.9e-1": [["M", 1.0, 0.69]],
}

var tests_passed := true
Expand Down
19 changes: 19 additions & 0 deletions godot_only/scripts/update_translations.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
@tool
extends EditorScript

const COMMENTS_DICT = {
"Viewport": "The viewport is the area where the graphic is displayed. In similar applications, it's often called the canvas.",
"CDATA color": "CDATA shouldn't be translated. It's a type of XML section.",
"Editor formatter": "Refers to the formatter used for GodSVG's code editor.",
"Export formatter": "Refers to the formatter used when exporting.",
"Handle colors": "Refers to the colors of the draggable handles.",
"Handle size": "Refers to the size of the draggable handles.",
"Excluded": "Refers to the zero, one, or multiple UI parts to not be shown in the final layout. It's of plural cardinality.",
"Update check failed": "When checking for updates.",
}

const TRANSLATIONS_DIR = "translations"

const HEADER = """#, fuzzy
Expand Down Expand Up @@ -80,11 +91,15 @@ func search_directory(dir: String) -> void:


func update_translations() -> void:
var used_comments := PackedStringArray()
var location := ProjectSettings.globalize_path(TRANSLATIONS_DIR + "/GodSVG.pot")
var fa := FileAccess.open(location, FileAccess.WRITE)
fa.store_string(HEADER)

for msg in messages:
if COMMENTS_DICT.has(msg.msgid):
fa.store_string("#. %s\n" % COMMENTS_DICT[msg.msgid])
used_comments.append(msg.msgid)
fa.store_string(msg.to_string())
fa = null
print("Created " + TRANSLATIONS_DIR + "/GodSVG.pot with %d strings" % (messages.size() + 1))
Expand All @@ -105,3 +120,7 @@ func update_translations() -> void:
print("Updated " + TRANSLATIONS_DIR + "/%s: %s" % [file, output[0].rstrip("\n")])
else:
print("Updated " + TRANSLATIONS_DIR + "%s" % file)

for id in COMMENTS_DICT:
if not used_comments.has(id):
print_rich("[color=#f66]The \"%s\" string, which has a comment defined for it, wasn't encountered." % id)
8 changes: 8 additions & 0 deletions project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -336,11 +336,19 @@ toggle_snap={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
toggle_fullscreen={
"deadzone": 0.2,
"events": [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":false,"pressed":false,"keycode":4194342,"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":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}

[input_devices]

pointing/android/enable_long_press_as_right_click=true
pointing/android/enable_pan_and_scale_gestures=true
pen_tablet/driver="dummy"
pen_tablet/driver.windows="dummy"

[internationalization]

Expand Down
26 changes: 26 additions & 0 deletions src/autoload/HandlerGUI.gd
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ func _unhandled_input(event: InputEvent) -> void:
if ShortcutUtils.is_action_pressed(event, action):
match action:
"quit": prompt_quit()
"toggle_fullscreen": toggle_fullscreen()
"about_info": open_about()
"about_donate": open_donate()
"check_updates": open_update_checker()
Expand Down Expand Up @@ -401,6 +402,31 @@ func prompt_quit() -> void:
Translator.translate("Do you want to quit GodSVG?"),
Translator.translate("Quit"), get_tree().quit)


var was_window_maximized: bool
var window_old_rect: Rect2

func toggle_fullscreen() -> void:
if DisplayServer.window_get_mode() != DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN:
if DisplayServer.window_get_mode() == DisplayServer.WindowMode.WINDOW_MODE_MAXIMIZED:
was_window_maximized = true
else:
was_window_maximized = false
window_old_rect = Rect2(DisplayServer.window_get_position(),
DisplayServer.window_get_size())
DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN)
else:
if was_window_maximized:
DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_MAXIMIZED)
else:
DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED)
DisplayServer.window_set_size(window_old_rect.size)
# TODO Without at least 3 frames of wait, on my laptop the window would
# sometimes go a little higher than before after setting its position.
for i in 3:
await get_tree().process_frame
DisplayServer.window_set_position(window_old_rect.position)

func open_update_checker() -> void:
remove_all_menus()
var confirmation_dialog := ConfirmDialogScene.instantiate()
Expand Down
16 changes: 10 additions & 6 deletions src/autoload/State.gd
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func get_export_text() -> String:
signal hover_changed
signal selection_changed

signal requested_scroll_to_element_editor(xid: PackedInt32Array, inner_idx: int)
signal requested_scroll_to_selection(xid: PackedInt32Array, inner_idx: int)

# The viewport listens for this signal to put you in handle-placing mode.
signal handle_added
Expand Down Expand Up @@ -705,10 +705,10 @@ func _move_selected(down: bool) -> void:
#xnode.get_attribute("d").move_subpath(inner_selections[0], down)
queue_svg_save()

func view_in_list(xid: PackedInt32Array, inner_index := -1) -> void:
func view_in_inspector(xid: PackedInt32Array, inner_index := -1) -> void:
if xid.is_empty():
return
requested_scroll_to_element_editor.emit(xid, inner_index)
requested_scroll_to_selection.emit(xid, inner_index)

func duplicate_selected() -> void:
root_element.duplicate_xnodes(selected_xids)
Expand Down Expand Up @@ -761,7 +761,7 @@ func get_selection_context(popup_method: Callable, context: Utils.LayoutPart) ->
can_move_down = true
if context == Utils.LayoutPart.VIEWPORT:
btn_arr.append(ContextPopup.create_button(Translator.translate("View in Inspector"),
view_in_list.bind(selected_xids[0]), false,
view_in_inspector.bind(selected_xids[0]), false,
load("res://assets/icons/Inspector.svg")))

btn_arr.append(ContextPopup.create_shortcut_button("duplicate"))
Expand Down Expand Up @@ -791,7 +791,7 @@ func get_selection_context(popup_method: Callable, context: Utils.LayoutPart) ->
if idx < inner_idx:
inner_idx = idx
btn_arr.append(ContextPopup.create_button(Translator.translate("View in Inspector"),
view_in_list.bind(semi_selected_xid, inner_idx), false,
view_in_inspector.bind(semi_selected_xid, inner_idx), false,
load("res://assets/icons/Inspector.svg")))
match element_ref.name:
"path":
Expand Down Expand Up @@ -886,8 +886,12 @@ func popup_insert_command_after_context(popup_method: Callable) -> void:
# Disable invalid commands. Z is syntactically invalid, so disallow it even harder.
var warned_commands: PackedStringArray
var disabled_commands: PackedStringArray
# S commands are deliberately warned against in most cases, even though
# there is some sense in using them without a C or S command before them.
# Same for T commands in most cases, even though
# there is a notion of letting them determine the next shorthand quadratic curve.
match cmd_char.to_upper():
"M": warned_commands = PackedStringArray(["M", "Z", "T"])
"M": warned_commands = PackedStringArray(["M", "Z", "S", "T"])
"L", "H", "V", "A": warned_commands = PackedStringArray(["S", "T"])
"C", "S": warned_commands = PackedStringArray(["T"])
"Q", "T": warned_commands = PackedStringArray(["S"])
Expand Down
10 changes: 8 additions & 2 deletions src/config_classes/Palette.gd
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,11 @@ static func is_valid_palette(text: String) -> bool:
var parser := XMLParser.new()
parser.open_buffer(text.to_utf8_buffer())
parser.read()
return parser.get_node_type() == XMLParser.NODE_ELEMENT and\
parser.get_node_name() == "palette"
while parser.read() == OK:
if parser.get_node_type() in [XMLParser.NODE_COMMENT, XMLParser.NODE_TEXT,
XMLParser.NODE_UNKNOWN]:
continue

return parser.get_node_type() == XMLParser.NODE_ELEMENT and\
parser.get_node_name() == "palette"
return false
5 changes: 5 additions & 0 deletions src/config_classes/SVGHighlighter.gd
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func _get_line_syntax_highlighting(line: int) -> Dictionary:
if svg_text.is_empty():
return {}

# We only return a color map, so this should deal with non-ASCII.
for i in svg_text.length():
if svg_text.unicode_at(i) >= 256:
svg_text[i] = "a"

var color_map: Dictionary[int, Dictionary] = {}
var parser := XMLParser.new()
parser.open_buffer(svg_text.to_utf8_buffer())
Expand Down
2 changes: 1 addition & 1 deletion src/config_classes/SaveData.gd
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func get_setting_default(setting: String) -> Variant:
"highlighting_comment_color": return Color("cdcfd280")
"highlighting_text_color": return Color("cdcfeaac")
"highlighting_cdata_color": return Color("ffeda1ac")
"highlighting_error_color": return Color("ff866b")
"highlighting_error_color": return Color("ff5555")
"handle_inner_color": return Color("fff")
"handle_color": return Color("111")
"handle_hovered_color": return Color("aaa")
Expand Down
23 changes: 21 additions & 2 deletions src/data_classes/AttributeColor.gd
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,27 @@ func _format(text: String, formatter: Formatter) -> String:

var named_colors_usage := formatter.color_use_named_colors
# First make sure we have a 6-digit hex.
if ColorParser.is_valid_rgb(text) or ColorParser.is_valid_hsl(text):
text = "#" + ColorParser.text_to_color(text).to_html(false)
if ColorParser.is_valid_rgb(text):
var args_start_pos := text.find("(") + 1
var inside_brackets := text.substr(args_start_pos, text.length() - args_start_pos - 1)
var args := inside_brackets.split(",", false)
var r := String.num_uint64(args[0].strip_edges(false, true).to_int(), 16)
var g := String.num_uint64(args[1].strip_edges(false, true).to_int(), 16)
var b := String.num_uint64(args[2].strip_edges(false, true).to_int(), 16)
text = "#" + (r if r.length() == 2 else "0" + r) +\
(g if g.length() == 2 else "0" + g) + (b if b.length() == 2 else "0" + b)
elif ColorParser.is_valid_hsl(text):
var args_start_pos := text.find("(") + 1
var inside_brackets := text.substr(args_start_pos, text.length() - args_start_pos - 1)
var args := inside_brackets.split(",", false)
var h := posmod(args[0].to_int(), 360)
var s := clampf(int(args[1].strip_edges(false, true).left(-1).to_float()) * 0.01, 0.0, 1.0)
var l := clampf(int(args[2].strip_edges(false, true).left(-1).to_float()) * 0.01, 0.0, 1.0)
var r := String.num_uint64(ColorParser.hsl_get_r(h, s, l), 16)
var g := String.num_uint64(ColorParser.hsl_get_g(h, s, l), 16)
var b := String.num_uint64(ColorParser.hsl_get_b(h, s, l), 16)
text = "#" + (r if r.length() == 2 else "0" + r) +\
(g if g.length() == 2 else "0" + g) + (b if b.length() == 2 else "0" + b)
if text in get_named_colors():
text = get_named_colors()[text]
if ColorParser.is_valid_hex(text) and text.length() == 4:
Expand Down
11 changes: 4 additions & 7 deletions src/data_classes/AttributePathdata.gd
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ static func pathdata_to_arrays(text: String) -> Array[Array]:
var new_commands: Array[Array] = []
var curr_command := ""
var prev_command := ""
var nums: Array[float] = []
var nums: Array = [] # Not a float array because the arcs are ints.
var args_left := 0
var comma_exhausted := false # Can ignore many whitespaces, but only one comma.

Expand Down Expand Up @@ -366,12 +366,9 @@ static func pathdata_to_arrays(text: String) -> Array[Array]:
comma_exhausted = true
number_proceed = false
"e", "E":
if passed_decimal_point:
return new_commands
else:
end_idx += 1
idx += 1
exponent_just_passed = true
end_idx += 1
idx += 1
exponent_just_passed = true
_:
if args_left >= 1 and\
not text.substr(start_idx, end_idx - start_idx).is_valid_float():
Expand Down
9 changes: 3 additions & 6 deletions src/data_classes/AttributeTransformList.gd
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,9 @@ static func text_to_transform_list(text: String) -> Array[Transform]:
comma_exhausted = true
number_proceed = false
"e", "E":
if passed_decimal_point:
return []
else:
end_idx += 1
idx += 1
exponent_just_passed = true
end_idx += 1
idx += 1
exponent_just_passed = true
_:
if not transform_params.substr(
start_idx, end_idx - start_idx).is_valid_float():
Expand Down
11 changes: 6 additions & 5 deletions src/data_classes/ColorParser.gd
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static func is_valid_url(color: String) -> bool:

static func _get_url_id(stripped_color: String) -> String:
return stripped_color.substr(4,
stripped_color.length() - 5).strip_edges().trim_prefix("#")
stripped_color.length() - 5).strip_edges().right(-1)

# URL doesn't have a color interpretation, so it'll give the backup.
static func text_to_color(color: String, backup := Color.BLACK,
Expand All @@ -109,9 +109,9 @@ allow_alpha := false) -> Color:
return Color(AttributeColor.get_named_colors(allow_alpha)[color])
elif color == "none":
return Color(0, 0, 0, 0)

elif is_valid_rgb(color, allow_alpha):
var inside_brackets := color.substr(4, color.length() - 5)
var args_start_pos := color.find("(") + 1
var inside_brackets := color.substr(args_start_pos, color.length() - args_start_pos - 1)
var args := inside_brackets.split(",", false)
if not (args.size() == 3 or (args.size() == 4 and allow_alpha)):
return backup
Expand All @@ -131,7 +131,8 @@ allow_alpha := false) -> Color:
return Color.from_rgba8(clampi(r, 0, 255), clampi(g, 0, 255), clampi(b, 0, 255),
clampi(a, 0, 255))
elif is_valid_hsl(color, allow_alpha):
var inside_brackets := color.substr(4, color.length() - 5)
var args_start_pos := color.find("(") + 1
var inside_brackets := color.substr(args_start_pos, color.length() - args_start_pos - 1)
var args := inside_brackets.split(",", false)
if not (args.size() == 3 or (args.size() == 4 and allow_alpha)):
return backup
Expand Down Expand Up @@ -213,4 +214,4 @@ static func hsl_get_b(h: int, s: float, l: float) -> int:
static func decompose_hsl(n: int, h: int, s: float, l: float) -> int:
var k := fmod(n + h/30.0, 12)
var a := s * minf(l, 1 - l)
return int((l - a * maxf(-1, minf(minf(k - 3, 9 - k), 1))) * 255)
return roundi((l - a * maxf(-1, minf(minf(k - 3, 9 - k), 1))) * 255)
Loading