Skip to content

Instantly share code, notes, and snippets.

@Qubus0
Last active March 5, 2023 03:14
Show Gist options
  • Save Qubus0/4f0077675647a986cb9e83b9cb9e0d87 to your computer and use it in GitHub Desktop.
Save Qubus0/4f0077675647a986cb9e83b9cb9e0d87 to your computer and use it in GitHub Desktop.
Godot Plugins: Changing icons and other parts of the FileSystemDock

How to change icons, their color, and other parts in the FileSystemDock

The easiest way to go about this is to switch the underlying icon which represents the file type.

tool
extends EditorPlugin

func _enter_tree() -> void:
    var base_theme := get_editor_interface().get_base_control().theme
    base_theme.set_icon("GDScript", "EditorIcons", preload("res://icon.png"))
    # Other common icons
    base_theme.set_icon("PackedScene", "EditorIcons", preload("res://icon.png"))
    base_theme.set_icon("Folder", "EditorIcons", preload("res://icon.png"))

    # If you are changing folder icons, you probably want to remove the blue tint
    base_theme.set_color("folder_icon_modulate", "FileDialog", Color.white)

    # If you want to use an existing editor icon, you can also get it from the base theme
    base_theme.set_icon("GDScript", "EditorIcons", base_theme.get_icon("Clear", "EditorIcons"))

If you want to change a different icon, there is a very helpful plugin to see them and get their code names: the Editor Theme Explorer by YuriSizov https://godotengine.org/asset-library/asset/557

Be careful though, as these icons will change everywhere they are used in the editor - GDScript, for example, is also used in the little file menu next to the code editor.

All of these icons will reset once the project reloads. If you want to be clean about it though, you should save the previous icons into a variable and apply those back again in _exit_tree()

More precision

Now, if you want more fine grained control over what should change, you can use methods like the ones below to get a result like this.

Custom File System Tree

To start, we need to get the file system Tree.

This code is borrowed from https://gist.github.com/Qubus0/#file-context_actions-md

func _enter_tree() -> void:
	var file_tree: Tree
	var file_list: ItemList
	for node in get_editor_interface().get_file_system_dock().get_children():
	# Only the parent of the file tree and file list is a VSplit
		if node is VSplitContainer:
			file_tree = node.get_child(0)
			file_list = node.get_child(1).get_child(1)
			break

Now that we have the tree (I won't be covering the ItemList for now) we can change it.

Note that the Tree is completely discarded and built again every time something changes in the file system. You can circumvent that by connecting to the file system's signal - not the dock, the file system itself.

	# Refresh the tree appearance when enabling for the first time and also every time it changes
	change_tree_appearance(file_tree)
	var file_system := get_editor_interface().get_resource_filesystem()
	file_system.connect("filesystem_changed", self, "change_tree_appearance", [file_tree])


# Get the first TreeItem. This is the entrypoint to our recursive method.
func change_tree_appearance(tree: Tree) -> void:
	change_item_appearance(tree.get_root())


# Recursive
func change_item_appearance(tree_item: TreeItem) -> void:
	while tree_item:
		var metadata = tree_item.get_metadata(0)
		var file_path: String
		if metadata is String:
			file_path = metadata

		# A few examples for what to change and how. Not a style guide, make yours prettier
		if file_path:
			if file_path.ends_with("res://"):
				tree_item.set_icon(0, preload("res://icon.png"))
				# Make sure the icon doesn't become oversized
				tree_item.set_icon_max_width(0, 32)
				# Folder icons have a blue tint, reset that
				tree_item.set_icon_modulate(0, Color.white)

			# Stop teammates from messing with the singletons
			if "global" in file_path:
				tree_item.set_suffix(0, "(DO NOT TOUCH)")

			# Full match. Directories end with a slash
			if file_path == "res://addons/":
				tree_item.set_custom_color(0, Color.lightgreen)

			if file_path.ends_with(".gd"):
				tree_item.set_icon_modulate(0, Color.black)
				
			if Directory.new().dir_exists(file_path):
				tree_item.set_custom_bg_color(0, Color(0, 0, 0, .1))

				# Check if a plugin has an icon and use it as icon for the folder
				if file_path.begins_with("res://addons/"):
					var icon_path := file_path.plus_file("plugin-icon.png")
					if File.new().file_exists(icon_path):
						tree_item.set_icon(0, load(icon_path))
						tree_item.set_icon_max_width(0, 32)

		change_item_appearance(tree_item.get_children())
		tree_item = tree_item.get_next()

Also, don't forget to clean up when the plugin is disabled

func _exit_tree() -> void:
	var file_system := get_editor_interface().get_resource_filesystem()
	file_system.disconnect("filesystem_changed", self, "change_tree_appearance")
	# Refresh the file system to discard visual changes
	file_system.scan()

Have fun with it! If you have more questions, feel free to ask.

If you want to support me, you can

Buy Me A Coffee
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment