Skip to content

Commit

Permalink
Rework logic around inherent attribute properties (#693)
Browse files Browse the repository at this point in the history
  • Loading branch information
MewPurPur authored Apr 29, 2024
1 parent 6b71094 commit 1d90d1e
Show file tree
Hide file tree
Showing 78 changed files with 432 additions and 445 deletions.
2 changes: 1 addition & 1 deletion AppInfo.gd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Stores basic information about GodSVG.
# Stores basic information about GodSVG.
class_name AppInfo extends RefCounted

const project_founder_and_manager: Array[String] = ["MewPurPur"]
Expand Down
120 changes: 120 additions & 0 deletions DB.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
class_name DB extends RefCounted

const known_tags = ["svg", "circle", "ellipse", "rect", "path", "line", "stop"]

const known_tag_attributes = { # Dictionary{String: Array[String]}
"svg": TagSVG.known_attributes,
"circle": TagCircle.known_attributes,
"ellipse": TagEllipse.known_attributes,
"rect": TagRect.known_attributes,
"path": TagPath.known_attributes,
"line": TagLine.known_attributes,
"stop": TagStop.known_attributes,
}

const attribute_defaults = {
"viewBox": "",
"width": "0",
"height": "0",
"x": "0",
"y": "0",
"x1": "0",
"y1": "0",
"x2": "0",
"y2": "0",
"cx": "0",
"cy": "0",
"r": "0",
"rx": "0",
"ry": "0",
"opacity": "1",
"fill": "black",
"fill-opacity": "1",
"stroke": "none",
"stroke-opacity": "1",
"stroke-width": "1",
"stroke-linecap": "butt",
"stroke-linejoin": "miter",
"d": "",
"transform": "",
"offset": "0",
"stop-color": "black",
"stop-opacity": "1",
}

const attribute_enum_values = {
"stroke-linecap": ["butt", "round", "square"],
"stroke-linejoin": ["miter", "round", "bevel"],
}

const attribute_numeric_bounds = {
"width": Vector2(0, INF),
"height": Vector2(0, INF),
"x": Vector2(-INF, INF),
"y": Vector2(-INF, INF),
"x1": Vector2(-INF, INF),
"y1": Vector2(-INF, INF),
"x2": Vector2(-INF, INF),
"y2": Vector2(-INF, INF),
"cx": Vector2(-INF, INF),
"cy": Vector2(-INF, INF),
"r": Vector2(0, INF),
"rx": Vector2(0, INF),
"ry": Vector2(0, INF),
"opacity": Vector2(0, 1),
"fill-opacity": Vector2(0, 1),
"stroke-opacity": Vector2(0, 1),
"stroke-width": Vector2(0, INF),
"offset": Vector2(0, 1),
"stop-opacity": Vector2(0, 1),
}


static func is_tag_known(tag_name: String) -> bool:
return tag_name in known_tags

static func is_attribute_known(tag_name: String, attribute_name: String) -> bool:
if not known_tag_attributes.has(tag_name):
return false
return attribute_name in known_tag_attributes[tag_name]

static func get_tag_icon(tag_name: String) -> Texture2D:
match tag_name:
"circle": return TagCircle.icon
"ellipse": return TagEllipse.icon
"rect": return TagRect.icon
"path": return TagPath.icon
"line": return TagLine.icon
"stop": return TagStop.icon
_: return TagUnknown.icon

static func attribute(name: String, initial_value := "") -> Attribute:
match name:
"viewBox": return AttributeList.new(name, initial_value)
"width": return AttributeNumeric.new(name, initial_value)
"height": return AttributeNumeric.new(name, initial_value)
"x": return AttributeNumeric.new(name, initial_value)
"y": return AttributeNumeric.new(name, initial_value)
"x1": return AttributeNumeric.new(name, initial_value)
"y1": return AttributeNumeric.new(name, initial_value)
"x2": return AttributeNumeric.new(name, initial_value)
"y2": return AttributeNumeric.new(name, initial_value)
"cx": return AttributeNumeric.new(name, initial_value)
"cy": return AttributeNumeric.new(name, initial_value)
"r": return AttributeNumeric.new(name, initial_value)
"rx": return AttributeNumeric.new(name, initial_value)
"ry": return AttributeNumeric.new(name, initial_value)
"opacity": return AttributeNumeric.new(name, initial_value)
"fill": return AttributeColor.new(name, initial_value)
"fill-opacity": return AttributeNumeric.new(name, initial_value)
"stroke": return AttributeColor.new(name, initial_value)
"stroke-opacity": return AttributeNumeric.new(name, initial_value)
"stroke-width": return AttributeNumeric.new(name, initial_value)
"stroke-linecap": return AttributeEnum.new(name, initial_value)
"stroke-linejoin": return AttributeEnum.new(name, initial_value)
"d": return AttributePath.new(name, initial_value)
"transform": return AttributeTransform.new(name, initial_value)
"offset": return AttributeNumeric.new(name, initial_value)
"stop-color": return AttributeColor.new(name, initial_value)
"stop-opacity": return AttributeNumeric.new(name, initial_value)
_: return AttributeUnknown.new(name)
4 changes: 1 addition & 3 deletions src/GlobalSettings.gd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## This singleton handles save data and settings.
# This singleton handles session data and settings.
extends Node

# Session data
Expand Down Expand Up @@ -47,7 +47,6 @@ const default_config = {
"handle_hovered_color": Color("#aaa"),
"handle_selected_color": Color("#46f"),
"handle_hovered_selected_color": Color("#f44"),
"default_value_opacity": 0.7,
"basic_color_valid": Color("9f9"),
"basic_color_error": Color("f99"),
"basic_color_warning": Color("ff9"),
Expand Down Expand Up @@ -128,7 +127,6 @@ var handle_color := Color("#111")
var handle_hovered_color := Color("#aaa")
var handle_selected_color := Color("#46f")
var handle_hovered_selected_color := Color("#f44")
var default_value_opacity := 0.7
var basic_color_valid := Color("9f9")
var basic_color_error := Color("f99")
var basic_color_warning := Color("ff9")
Expand Down
30 changes: 15 additions & 15 deletions src/Indications.gd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This singleton handles temporary editor information like zoom level and selections.
extends Node
## This singleton handles editor information like zoom level and selections.

const PathCommandPopup = preload("res://src/ui_elements/path_popup.tscn")

Expand Down Expand Up @@ -69,8 +69,8 @@ func _ready() -> void:
SVG.root_tag.changed_unknown.connect(clear_all_selections)


## Override the selected tags with a single new selected tag.
## If inner_idx is given, this will be an inner selection.
# Override the selected tags with a single new selected tag.
# If inner_idx is given, this will be an inner selection.
func normal_select(tid: PackedInt32Array, inner_idx := -1) -> void:
if tid.is_empty():
return
Expand Down Expand Up @@ -98,8 +98,8 @@ func normal_select(tid: PackedInt32Array, inner_idx := -1) -> void:
if inner_selections != old_inner_selections:
selection_changed.emit()

## If the tag was selected, unselect it. If it was unselected, select it.
## If inner_idx is given, this will be an inner selection.
# If the tag was selected, unselect it. If it was unselected, select it.
# If inner_idx is given, this will be an inner selection.
func ctrl_select(tid: PackedInt32Array, inner_idx := -1) -> void:
if tid.is_empty():
return
Expand Down Expand Up @@ -130,8 +130,8 @@ func ctrl_select(tid: PackedInt32Array, inner_idx := -1) -> void:

selection_changed.emit()

## Select all tags with the same depth from the tag to the last selected tag.
## Similarly for inner selections if inner_idx is given, but without tree logic.
# Select all tags with the same depth from the tag to the last selected tag.
# Similarly for inner selections if inner_idx is given, but without tree logic.
func shift_select(tid: PackedInt32Array, inner_idx := -1) -> void:
if tid.is_empty():
return
Expand Down Expand Up @@ -187,7 +187,7 @@ func shift_select(tid: PackedInt32Array, inner_idx := -1) -> void:

selection_changed.emit()

## Select all tags.
# Select all tags.
func select_all() -> void:
clear_inner_selection()
var tid_list := SVG.root_tag.get_all_tids()
Expand All @@ -200,22 +200,22 @@ func select_all() -> void:
selection_changed.emit()


## Clear the selected tags.
# Clear the selected tags.
func clear_selection() -> void:
if not selected_tids.is_empty():
selected_tids.clear()
selection_pivot_tid.clear()
selection_changed.emit()

## Clear the inner selection.
# Clear the inner selection.
func clear_inner_selection() -> void:
if not inner_selections.is_empty() or not semi_selected_tid.is_empty():
inner_selections.clear()
semi_selected_tid.clear()
inner_selection_pivot = -1
selection_changed.emit()

## Clear the selected tags or the inner selection.
# Clear the selected tags or the inner selection.
func clear_all_selections() -> void:
if not inner_selections.is_empty() or not semi_selected_tid.is_empty() or\
not selected_tids.is_empty():
Expand All @@ -225,7 +225,7 @@ func clear_all_selections() -> void:
selection_changed.emit()


## Set the hovered tag.
# Set the hovered tag.
func set_hovered(tid: PackedInt32Array, inner_idx := -1) -> void:
if inner_idx == -1:
if hovered_tid != tid:
Expand All @@ -247,7 +247,7 @@ func set_hovered(tid: PackedInt32Array, inner_idx := -1) -> void:
hovered_tid.clear()
hover_changed.emit()

## If the tag is hovered, make it not hovered.
# If the tag is hovered, make it not hovered.
func remove_hovered(tid: PackedInt32Array, inner_idx := -1) -> void:
if inner_idx == -1:
if hovered_tid == tid:
Expand All @@ -259,13 +259,13 @@ func remove_hovered(tid: PackedInt32Array, inner_idx := -1) -> void:
inner_hovered = -1
hover_changed.emit()

## Clear the hovered tag.
# Clear the hovered tag.
func clear_hovered() -> void:
if not hovered_tid.is_empty():
hovered_tid.clear()
hover_changed.emit()

## Clear the inner hover.
# Clear the inner hover.
func clear_inner_hovered() -> void:
if inner_hovered != -1:
inner_hovered = -1
Expand Down
4 changes: 2 additions & 2 deletions src/SVG.gd
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This singleton handles the two representations of the SVG:
# The SVG text, and the native TagSVG representation.
extends Node
## This singleton handles the two representations of the SVG:
## The SVG text, and the native [TagSVG] representation.


signal parsing_finished(error_id: SVGParser.ParseError)
Expand Down
2 changes: 1 addition & 1 deletion src/Utils.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ enum CustomNotification {
THEME_CHANGED = 302,
NUMBER_PRECISION_CHANGED = 303,
HIGHLIGHT_COLORS_CHANGED = 304,
DEFAULT_VALUE_OPACITY_CHANGED = 305,
BASIC_COLORS_CHANGED = 305,
HANDLE_VISUALS_CHANGED = 306,
}

Expand Down
14 changes: 12 additions & 2 deletions src/data_classes/Attribute.gd
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Abstract class for an attribute inside a tag, i.e. <tag attribute="value"/>
class_name Attribute extends RefCounted
## Abstract class for an attribute inside a [Tag], i.e. <tag attribute="value"/>

signal value_changed(new_value: String)
signal propagate_value_changed(undo_redo: bool)

var default: String
var name: String
var _value: String

enum SyncMode {LOUD, INTERMEDIATE, FINAL, NO_PROPAGATION, SILENT}
Expand Down Expand Up @@ -46,3 +46,13 @@ func _sync() -> void:

func autoformat(text: String) -> String:
return text

func get_default() -> String:
if name in DB.attribute_defaults:
return DB.attribute_defaults[name]
else:
return ""

func _init(new_name: String, init_value := "") -> void:
name = new_name
set_value(init_value, SyncMode.FINAL)
19 changes: 12 additions & 7 deletions src/data_classes/AttributeColor.gd
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
# An attribute representing a color string, or an url to the ID of a paint element.
class_name AttributeColor extends Attribute
## An attribute representing a color string, or an url to an ID.

# No direct color representation for this attribute type. There are too many quirks.

func _init(new_default: String, new_init := "") -> void:
default = new_default
set_value(new_init if !new_init.is_empty() else new_default, SyncMode.SILENT)

func set_value(new_value: String, sync_mode := SyncMode.LOUD) -> void:
super(new_value if ColorParser.is_valid(new_value) else default, sync_mode)
super(new_value if (new_value.is_empty() or ColorParser.is_valid(new_value))\
else get_default(), sync_mode)

func autoformat(text: String) -> String:
if GlobalSettings.color_enable_autoformatting:
var new_text := ColorParser.format_text(text)
return default if ColorParser.are_colors_same(new_text, default) else new_text
return get_default() if ColorParser.are_colors_same(new_text, get_default()) else\
new_text
else:
return text


func get_color() -> Color:
if _value.is_empty():
return ColorParser.string_to_color(DB.attribute_defaults[name])
else:
return ColorParser.string_to_color(_value)


const special_colors = ["none", "currentColor"]

const named_colors = { # Dictionary{String: String}
Expand Down
14 changes: 5 additions & 9 deletions src/data_classes/AttributeEnum.gd
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
# An attribute with only a set of meaningful values.
class_name AttributeEnum extends Attribute
## An attribute with only a set of meaningful values.

var possible_values: Array[String]

func _init(new_possible_values: Array[String], new_default_idx := 0) -> void:
possible_values = new_possible_values
default = possible_values[new_default_idx]
set_value(default, SyncMode.SILENT)

func set_value(new_value: String, sync_mode := SyncMode.LOUD) -> void:
super(new_value if new_value in possible_values else default, sync_mode)
if new_value.is_empty() or new_value in DB.attribute_enum_values[name]:
super(new_value, sync_mode)
else:
super(get_default(), sync_mode)
6 changes: 1 addition & 5 deletions src/data_classes/AttributeList.gd
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
# An attribute representing a list of numbers.
class_name AttributeList extends Attribute
## An attribute representing a list of numbers.

var _list: PackedFloat32Array

func _init() -> void:
default = ""
set_value("", SyncMode.SILENT)

func _sync() -> void:
_list = ListParser.string_to_list(get_value())

Expand Down
15 changes: 5 additions & 10 deletions src/data_classes/AttributeNumeric.gd
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
# An attribute representing a number.
class_name AttributeNumeric extends Attribute
## An attribute representing a number.

var _number := NAN
var min_value: float
var max_value: float

func _init(new_min: float, new_max: float, new_default: String, new_init := "") -> void:
min_value = new_min
max_value = new_max
default = new_default
set_value(new_init if !new_init.is_empty() else new_default, SyncMode.SILENT)

func _sync() -> void:
_number = NumberParser.text_to_num(get_value())
if _value.is_empty():
_number = NumberParser.text_to_num(DB.attribute_defaults[name])
else:
_number = NumberParser.text_to_num(_value)

func autoformat(text: String) -> String:
if GlobalSettings.number_enable_autoformatting:
Expand Down
Loading

0 comments on commit 1d90d1e

Please sign in to comment.