Skip to content

Commit

Permalink
[icon_explorer] Update icon explorer
Browse files Browse the repository at this point in the history
  • Loading branch information
IceflowRE committed Jan 7, 2024
1 parent 8f942f4 commit 258e5e5
Show file tree
Hide file tree
Showing 21 changed files with 617 additions and 165 deletions.
22 changes: 13 additions & 9 deletions addons/icon_explorer/internal/scripts/collection.gd
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const Icon := preload("res://addons/icon_explorer/internal/scripts/icon.gd")

enum Id {
MATERIAL_DESIGN,
SIMPLE_ICONS,
}

signal install_finished(status: Error)
Expand All @@ -20,6 +21,11 @@ var web: String

var __remove_thread: Thread

func _notification(what):
if what == NOTIFICATION_PREDELETE:
if self.__remove_thread != null:
self.__remove_thread.wait_to_finish()

# VIRTUAL
func load() -> Array[Icon]:
assert(false, "virtual function")
Expand All @@ -39,11 +45,15 @@ func remove() -> void:
self.__remove_thread = Thread.new()
self.__remove_thread.start(self.__remove)

# THREADED
func __remove() -> void:
if Io.rrm_dir(self.directory()):
self.remove_finished.emit(Error.OK)
self.__remove_done.bind(Error.OK).call_deferred()
else:
self.remove_finished.emit(Error.FAILED)
self.__remove_done.bind(Error.FAILED).call_deferred()

func __remove_done(status: Error) -> void:
self.remove_finished.emit(status)

# VIRTUAL
func icon_directory() -> String:
Expand All @@ -55,7 +65,7 @@ func possible_versions() -> Array[String]:
return []

func is_installed() -> bool:
return DirAccess.dir_exists_absolute(self.directory())
return DirAccess.dir_exists_absolute(self.icon_directory())

func directory() -> String:
if Engine.is_editor_hint() || true: # TODO: remove development true
Expand All @@ -68,9 +78,3 @@ func directory() -> String:

func directory_name() -> String:
return self.name.to_snake_case()

static func collection_name(coll: Id) -> String:
match coll:
Id.MATERIAL_DESIGN:
return "Material Design"
return "unknown"
69 changes: 49 additions & 20 deletions addons/icon_explorer/internal/scripts/collection_mdi.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ extends "res://addons/icon_explorer/internal/scripts/collection.gd"

const IconMdi := preload("res://addons/icon_explorer/internal/scripts/icon_mdi.gd")

const _DOWNLOAD_FILE: String = "https://github.com/Templarian/MaterialDesign-SVG/archive/master.zip"

var _unpack_thread: Thread

func _init() -> void:
self.id = Id.MATERIAL_DESIGN
self.name = "Material Design"
Expand All @@ -10,21 +14,26 @@ func _init() -> void:
self.license = "Apache 2.0"
self.web = "https://github.com/Templarian/MaterialDesign-SVG"

func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
if self._unpack_thread != null:
self._unpack_thread.wait_to_finish()

# OVERRIDE
func load() -> Array[Icon]:
var parser: JSON = JSON.new()
var res: int = parser.parse(FileAccess.get_file_as_string(self.directory().path_join("meta.json")))
if res != OK:
push_warning("could not parse '%s': '%s'", [self.directory().path_join("meta.json"), parser.get_error_message()])
return []

var parser_version: JSON = JSON.new()
var res_version: int = parser_version.parse(FileAccess.get_file_as_string(self.directory().path_join("package.json")))
var res_version: int = parser_version.parse(FileAccess.get_file_as_string(self.directory().path_join("MaterialDesign-SVG-master/package.json")))
if res_version != OK:
push_warning("could not parse '%s': '%s'", [self.directory().path_join("package.json"), parser_version.get_error_message()])
push_warning("could not parse MDI package.json: '%s'", [parser_version.get_error_message()])
return []
self.version = parser_version.data["version"]

var parser: JSON = JSON.new()
var res: int = parser.parse(FileAccess.get_file_as_string(self.directory().path_join("MaterialDesign-SVG-master/meta.json")))
if res != OK:
push_warning("could not parse MDI meta.json: '%s'", [parser.get_error_message()])
return []

var icon_path: String = self.icon_directory()
var icons: Array[Icon] = []
for item: Dictionary in parser.data:
Expand All @@ -33,41 +42,61 @@ func load() -> Array[Icon]:
icon.name = item["name"]
icon.icon_path = icon_path.path_join(icon.name + ".svg")

icon.author = item.get("author", "")
icon.version = item.get("version", "")
icon.aliases = item.get("aliases", PackedStringArray())
icon.tags = item.get("tags", PackedStringArray())
icon.deprecated = item.get("deprecated", false)

var buffer: PackedByteArray = FileAccess.get_file_as_bytes(icon.icon_path)
if buffer.size() == 0:
push_warning("could not load '" + icon.icon_path + "'")
continue
var img: Image = Image.new()
var success: int = img.load_svg_from_buffer(FileAccess.get_file_as_bytes(icon.icon_path), 4.0)
var success: int = img.load_svg_from_buffer(buffer, 4.0)
if success != OK:
push_warning("could not load '" + icon.icon_path + "'")
continue
img.fix_alpha_edges()
icon.texture = ImageTexture.create_from_image(img)

icon.author = item.get("author", "")
icon.version = item.get("version", "")
icon.aliases = item.get("aliases", PackedStringArray())
icon.tags = item.get("tags", PackedStringArray())
icon.deprecated = item.get("deprecated", false)
icons.append(icon)
return icons

# OVERRIDE
func install(http: HTTPRequest, version: String) -> void:
if self._unpack_thread != null && self._unpack_thread.is_alive():
return
if !http.request_completed.is_connected(self._on_request_completed):
http.request_completed.connect(_on_request_completed)

self.install_finished.emit(Error.FAILED)
DirAccess.make_dir_recursive_absolute(self.directory())
http.download_file = self.directory().path_join("icons.zip")
http.request(_DOWNLOAD_FILE)

func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
if result != HTTPRequest.RESULT_SUCCESS:
self.install_finished.emit(Error.FAILED)
return


self.install_finished.emit(Error.OK)

if self._unpack_thread != null && self._unpack_thread.is_started():
self._unpack_thread.wait_to_finish()
self._unpack_thread = Thread.new()
self._unpack_thread.start(self._unpack_fn)

# THREADED
func _unpack_fn() -> void:
Io.MtUnzip.new().unpack_zip(self.directory().path_join("icons.zip"), self.directory(), maxi(OS.get_processor_count() / 2, 1))
DirAccess.remove_absolute(self.directory().path_join("icons.zip"))
self._install_finished.bind(Error.OK).call_deferred()

func _install_finished(status: Error) -> void:
self.install_finished.emit(status)

# OVERRIDE
func remove() -> void:
super.remove()
self.version = ""

# OVERRIDE
func icon_directory() -> String:
return self.directory().path_join("svg")
return self.directory().path_join("MaterialDesign-SVG-master/svg/")
121 changes: 121 additions & 0 deletions addons/icon_explorer/internal/scripts/collection_simpleicons.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
extends "res://addons/icon_explorer/internal/scripts/collection.gd"

const IconSimpleIcons := preload("res://addons/icon_explorer/internal/scripts/icon_simpleicons.gd")

const _DOWNLOAD_FILE: String = "https://github.com/simple-icons/simple-icons/archive/master.zip"

var _unpack_thread: Thread

func _init() -> void:
self.id = Id.SIMPLE_ICONS
self.name = "Simple Icons"
self.version = ""
self.author = ""
self.license = "CC0 1.0 Universal / Others"
self.web = "https://github.com/simple-icons/simple-icons"

func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
if self._unpack_thread != null:
self._unpack_thread.wait_to_finish()

# OVERRIDE
func load() -> Array[Icon]:
var parser_version: JSON = JSON.new()
var res_version: int = parser_version.parse(FileAccess.get_file_as_string(self.directory().path_join("simple-icons-master/package.json")))
if res_version != OK:
push_warning("could not parse simple icons package.json: '%s'", [parser_version.get_error_message()])
return []
self.version = parser_version.data["version"]

var parser: JSON = JSON.new()
var res: int = parser.parse(FileAccess.get_file_as_string(self.directory().path_join("simple-icons-master/_data/simple-icons.json")))
if res != OK:
push_warning("could not parse simple icons simple-icons.json: '%s'", [parser.get_error_message()])
return []

var icon_path: String = self.icon_directory()
var icons: Array[Icon] = []
for item: Dictionary in parser.data.get("icons", []):
var icon: IconSimpleIcons = self._load_item(item, icon_path)
if icon == null:
continue
icons.append(icon)
for dup: Dictionary in item.get("aliases", {}).get("dup", []):
var dup_item: Dictionary = item.duplicate(true)
dup_item.merge(dup, true)
icon = self._load_item(item, icon_path)
if icon != null:
icons.append(icon)

return icons

func _load_item(item: Dictionary, icon_path: String) -> IconSimpleIcons:
var icon: IconSimpleIcons = IconSimpleIcons.new()
icon.collection = self
icon.name = item["title"]
icon.icon_path = icon_path.path_join(icon.name.to_lower().replace(".", "dot").replace(" ", "") + ".svg")

var hex: String = item.get("hex", "")
if hex != "":
icon.hex = Color.from_string(hex, Color())
icon.source = item.get("source", "")
var aliases: Dictionary = item.get("aliases", {})
icon.aliases = aliases.get("aka", PackedStringArray())
icon.aliases.append_array(aliases.get("loc", {}).values())
icon.aliases.append_array(aliases.get("old", PackedStringArray()))
icon.license = item.get("license", {}).get("type", "")
icon.license_link = item.get("license", {}).get("url", "")
icon.guidelines = item.get("guidelines", "")

var buffer: PackedByteArray = FileAccess.get_file_as_bytes(icon.icon_path)
if buffer.size() == 0:
push_warning("could not load '" + icon.icon_path + "'")
return null
var img: Image = Image.new()
var success: int = img.load_svg_from_buffer(buffer, 4.0)
if success != OK:
push_warning("could not load '" + icon.icon_path + "'")
return null
img.fix_alpha_edges()
icon.texture = ImageTexture.create_from_image(img)
return icon

# OVERRIDE
func install(http: HTTPRequest, version: String) -> void:
if self._unpack_thread != null && self._unpack_thread.is_alive():
return
if !http.request_completed.is_connected(self._on_request_completed):
http.request_completed.connect(_on_request_completed)

DirAccess.make_dir_recursive_absolute(self.directory())
http.download_file = self.directory().path_join("icons.zip")
http.request(_DOWNLOAD_FILE)

func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
if result != HTTPRequest.RESULT_SUCCESS:
self.install_finished.emit(Error.FAILED)
return

if self._unpack_thread != null && self._unpack_thread.is_started():
self._unpack_thread.wait_to_finish()
self._unpack_thread = Thread.new()
self._unpack_thread.start(self._unpack_fn)

# THREADED
func _unpack_fn() -> void:
Io.MtUnzip.new().unpack_zip(self.directory().path_join("icons.zip"), self.directory(), maxi(OS.get_processor_count() / 2, 1))
DirAccess.remove_absolute(self.directory().path_join("icons.zip"))
self._install_finished.bind(Error.OK).call_deferred()

func _install_finished(status: Error) -> void:
self.install_finished.emit(status)

# OVERRIDE
func remove() -> void:
super.remove()
self.version = ""

# OVERRIDE
func icon_directory() -> String:
return self.directory().path_join("simple-icons-master/icons/")
21 changes: 18 additions & 3 deletions addons/icon_explorer/internal/scripts/icon_database.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@ extends RefCounted

const Collection := preload("res://addons/icon_explorer/internal/scripts/collection.gd")
const CollectionMdi := preload("res://addons/icon_explorer/internal/scripts/collection_mdi.gd")
const CollectionSimpleIcons := preload("res://addons/icon_explorer/internal/scripts/collection_simpleicons.gd")
const Icon := preload("res://addons/icon_explorer/internal/scripts/icon.gd")

signal changed()
signal loaded()

var _collections: Array[Collection] = [
CollectionMdi.new(),
CollectionSimpleIcons.new(),
]
var _icons: Array[Icon]

var _is_loaded: bool = false
var _load_progress: float
var _load_thread: Thread

func _notification(what):
if what == NOTIFICATION_PREDELETE:
if self._load_thread != null:
self._load_thread.wait_to_finish()

func collections() -> Array[Collection]:
return self._collections

Expand All @@ -23,24 +33,25 @@ func icons() -> Array[Icon]:
return self._icons

func load() -> void:
assert(false, "do not use: https://github.com/godotengine/godot/issues/86796")
self._load()
return
# TODO: https://github.com/godotengine/godot/issues/86796
if self._load_thread != null && self._load_thread.is_alive():
return
if self._load_thread != null:
self._load_thread.wait_to_finish()

self._is_loaded = false
self._load_thread = Thread.new()
var load_fn: Callable = Callable(self, "_load")
self._load_thread.start(load_fn)
self._load_thread.start(self._load)

func is_loaded() -> bool:
return self._is_loaded

func load_progress() -> float:
return self._load_progress

# THREADED
func _load() -> void:
var new_icons: Array[Icon] = []
self._load_progress = 0.0
Expand All @@ -50,5 +61,9 @@ func _load() -> void:
new_icons.append_array(coll.load())
self._load_progress = float(idx) / self._collections.size() * 100.0

self._load_finished.bind(new_icons).call_deferred()

func _load_finished(new_icons: Array[Icon]) -> void:
self._icons = new_icons
self._is_loaded = true
self.loaded.emit()
15 changes: 15 additions & 0 deletions addons/icon_explorer/internal/scripts/icon_simpleicons.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
extends "res://addons/icon_explorer/internal/scripts/icon.gd"

var hex: Color
var source: String
var aliases: PackedStringArray
var license: String
var license_link: String
var guidelines: String


func is_filtered(keyword: String) -> bool:
for alias: String in self.aliases:
if alias.to_lower().contains(keyword):
return true
return super.is_filtered(keyword) || self.hex.to_html().to_lower().contains(keyword)
Loading

0 comments on commit 258e5e5

Please sign in to comment.