diff --git a/addons/material_maker/engine/nodes/gen_graph.gd b/addons/material_maker/engine/nodes/gen_graph.gd
index 09fa29f52..05fcb182b 100644
--- a/addons/material_maker/engine/nodes/gen_graph.gd
+++ b/addons/material_maker/engine/nodes/gen_graph.gd
@@ -13,6 +13,7 @@ var transmits_seed : bool = true
var current_mesh : Mesh = null
+var bookmarks = null
var scroll_offset = null
signal graph_changed()
@@ -23,11 +24,14 @@ signal hierarchy_changed()
func _ready() -> void:
super._ready()
-func emit_hierarchy_changed():
+func get_top() -> MMGenGraph:
var top = self
while top.get_parent() != null and top.get_parent().get_script() == get_script():
top = top.get_parent()
- top.emit_signal("hierarchy_changed")
+ return top
+
+func emit_hierarchy_changed():
+ get_top().emit_signal("hierarchy_changed")
func fix_remotes() -> void:
for c in get_children():
@@ -535,6 +539,8 @@ func _serialize(data: Dictionary) -> Dictionary:
data.nodes.append(c.serialize())
#data.connections = connections_to_compact(connections)
data.connections = connections
+ if self == get_top() and bookmarks != null:
+ data.bookmarks = bookmarks
if scroll_offset:
data.scroll_offset = { x=scroll_offset.x, y=scroll_offset.y }
return data
@@ -556,6 +562,8 @@ func _deserialize(data : Dictionary) -> void:
if data.has("scroll_offset"):
scroll_offset = Vector2(data.scroll_offset.x, data.scroll_offset.y)
var new_stuff = await mm_loader.add_to_gen_graph(self, nodes, connection_array)
+ if self == get_top() and data.has("bookmarks"):
+ bookmarks = data.bookmarks
#region Node Graph Diff
func apply_diff_from(graph : MMGenGraph) -> void:
diff --git a/material_maker/main_window.gd b/material_maker/main_window.gd
index 48db5a475..4fbfcaf9c 100644
--- a/material_maker/main_window.gd
+++ b/material_maker/main_window.gd
@@ -22,7 +22,7 @@ var preview_tesselation_detail : int = 256
@onready var node_library_manager = $NodeLibraryManager
@onready var brush_library_manager = $BrushLibraryManager
-
+@onready var bookmark_manager = $BookmarkManager
@onready var projects_panel = $VBoxContainer/Layout/FlexibleLayout/Main
diff --git a/material_maker/main_window.tscn b/material_maker/main_window.tscn
index ce6dc267e..28d8ca35a 100644
--- a/material_maker/main_window.tscn
+++ b/material_maker/main_window.tscn
@@ -1,8 +1,9 @@
-[gd_scene load_steps=21 format=3 uid="uid://cgfeik04a5qqs"]
+[gd_scene load_steps=22 format=3 uid="uid://cgfeik04a5qqs"]
[ext_resource type="Script" uid="uid://cbfcjtm6e4t8h" path="res://material_maker/main_window.gd" id="1"]
[ext_resource type="Theme" uid="uid://b628lwfk6ig2c" path="res://material_maker/theme/default.tres" id="1_2qcba"]
[ext_resource type="Script" uid="uid://csdtiyrrw4pxg" path="res://material_maker/main_window_layout.gd" id="2"]
+[ext_resource type="Script" uid="uid://dbvsf3qyuyrq6" path="res://material_maker/tools/bookmark_manager/bookmark_manager.gd" id="5_jxedr"]
[ext_resource type="PackedScene" uid="uid://eiq3i53x72m2" path="res://addons/flexible_layout/flexible_layout.tscn" id="6_ygla4"]
[ext_resource type="PackedScene" uid="uid://clw8sb0p8webl" path="res://material_maker/windows/add_node_popup/add_node_popup.tscn" id="7"]
[ext_resource type="PackedScene" uid="uid://bnqq3vhwmudkw" path="res://material_maker/projects_panel.tscn" id="7_ih0ps"]
@@ -141,6 +142,9 @@ config_section = "brush_lib"
[node name="EnvironmentManager" parent="." instance=ExtResource("13")]
+[node name="BookmarkManager" type="Node" parent="."]
+script = ExtResource("5_jxedr")
+
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
@@ -166,10 +170,14 @@ allow_undock = true
[node name="Main" parent="VBoxContainer/Layout/FlexibleLayout" instance=ExtResource("7_ih0ps")]
layout_mode = 0
anchors_preset = 0
+anchor_right = 0.0
+anchor_bottom = 0.0
offset_left = 18.0
offset_top = 4.0
offset_right = 263.0
offset_bottom = 32.0
+grow_horizontal = 1
+grow_vertical = 1
[node name="ConsoleResizer" type="Control" parent="VBoxContainer"]
unique_name_in_owner = true
diff --git a/material_maker/nodes/reroute/reroute.gd b/material_maker/nodes/reroute/reroute.gd
index 8bbcbb9fb..b3431dd64 100644
--- a/material_maker/nodes/reroute/reroute.gd
+++ b/material_maker/nodes/reroute/reroute.gd
@@ -1,6 +1,5 @@
extends MMGraphNodeMinimal
-
const PREVIEW_SIZES : Array[int] = [ 0, 64, 128, 192]
diff --git a/material_maker/panels/common/menu_bar_button_with_panel.gd b/material_maker/panels/common/menu_bar_button_with_panel.gd
index 8fed42568..231c12413 100644
--- a/material_maker/panels/common/menu_bar_button_with_panel.gd
+++ b/material_maker/panels/common/menu_bar_button_with_panel.gd
@@ -70,6 +70,8 @@ func _input(event : InputEvent) -> void:
if event is InputEventMouseButton:
var node := get_viewport().gui_get_hovered_control()
+ if node == null:
+ return
if node != self and not is_ancestor_of(node) and (not pinned or (node and node.script == self.script) and node.owner == owner):
button_pressed = false
diff --git a/material_maker/panels/graph_edit/graph_bookmark_menu.gd b/material_maker/panels/graph_edit/graph_bookmark_menu.gd
new file mode 100644
index 000000000..023bf2d04
--- /dev/null
+++ b/material_maker/panels/graph_edit/graph_bookmark_menu.gd
@@ -0,0 +1,179 @@
+extends PanelContainer
+
+@onready var tree := $Tree
+
+var bookmark_manager : BookmarkManager
+
+var is_unpinned_double_click_edited := false
+
+enum ContextMenu {
+ RENAME,
+ DELETE,
+}
+
+
+func _open() -> void:
+ update_bookmarks()
+
+func _ready() -> void:
+ if not mm_globals.main_window.is_node_ready():
+ await mm_globals.main_window.ready
+
+ bookmark_manager = mm_globals.main_window.bookmark_manager
+ bookmark_manager.bookmarks_added.connect(update_bookmarks)
+ bookmark_manager.updated_from_graph.connect(update_bookmarks)
+ bookmark_manager.bookmarks_edit_removed.connect(save_bookmarks)
+
+ %Projects.tab_changed.connect(projects_panel_tab_changed.unbind(1))
+
+func projects_panel_tab_changed() -> void:
+ var graph : MMGraphEdit = owner.current_graph_edit
+ if not graph.view_updated.is_connected(update_bookmarks):
+ graph.view_updated.connect(update_bookmarks)
+ if graph.top_generator == null:
+ await get_tree().process_frame
+ update_bookmarks(graph.top_generator)
+
+func update_bookmarks(updated_view : MMGenGraph = null) -> void:
+ validate_bookmarks(updated_view)
+ save_bookmarks()
+ rebuild_bookmark_tree()
+
+func save_bookmarks() -> void:
+ var graph : MMGraphEdit = owner.current_graph_edit
+ if graph.top_generator != null:
+ var current_bookmarks = bookmark_manager.bookmarks.duplicate()
+ graph.top_generator.bookmarks = current_bookmarks
+
+func validate_bookmarks(updated_view : MMGenGraph = null) -> void:
+ # Update and remove invalid references
+ var graph : MMGraphEdit = owner.current_graph_edit
+ if updated_view != null and updated_view == graph.top_generator:
+ if updated_view.bookmarks != null:
+ bookmark_manager.bookmarks = updated_view.bookmarks
+
+ if tree.get_root() == null:
+ return
+
+ for item : TreeItem in tree.get_root().get_children():
+ var bookmark_path : String = item.get_metadata(0)
+
+ # Material & Brush nodes are always in top level graph
+ if bookmark_path in ["./Material"]:
+ continue
+
+ # Bookmarked path does not point to anything
+ if not graph.top_generator.has_node(bookmark_path):
+ var target_node : String = bookmark_path.split("/")[-1]
+
+ # Remove invalid reference
+ bookmark_manager.remove_bookmark(bookmark_path)
+
+ # Check if the node is part of the updated graph view
+ # i.e. from grouping the currently bookmarked node
+ if updated_view != null:
+ var node_path := "node_" + target_node
+ if graph.has_node(node_path):
+ var gen : MMGenBase = graph.get_node(node_path).generator
+ var new_path : String = "./" + str(graph.top_generator.get_path_to(gen))
+ bookmark_manager.add_bookmark_from_path(new_path, item.get_text(0))
+
+func rebuild_bookmark_tree() -> void:
+ await get_tree().process_frame
+ tree.clear()
+ var root : TreeItem = tree.create_item()
+
+ var bookmarks := bookmark_manager.bookmarks
+ for path : String in bookmarks:
+ var new_item : TreeItem = tree.create_item(root)
+ new_item.set_metadata(0, path)
+ new_item.set_text(0, bookmarks[path])
+
+ # Show placeholder/hint if bookmarks are empty
+ tree.visible = tree.get_root().get_child_count() != 0
+ $MarginContainer.visible = tree.get_root().get_child_count() == 0
+
+func _on_tree_item_lmb_selected() -> void:
+ var selected_item : TreeItem = tree.get_selected()
+ var path : String = selected_item.get_metadata(0)
+ path = path.get_slice("./", 1)
+ var graph : MMGraphEdit = owner.current_graph_edit
+
+ var target_gen : MMGenBase
+
+ # Top-level bookmark
+ if "/" not in path:
+ # Jump back to top if we are in a subgraph
+ if graph.generator != graph.top_generator:
+ graph.update_view(graph.top_generator)
+
+ # Get bookmarked node from current graph
+ var node_path := NodePath("node_" + path)
+ if graph.has_node(node_path):
+ target_gen = graph.get_node(node_path).generator
+ else:
+ # Subgraph bookmark
+ target_gen = graph.top_generator.get_node(NodePath(path))
+
+ if target_gen == null:
+ # bookmark no longer exists
+ return
+
+ if target_gen.get_parent() is MMGenGraph and target_gen.get_parent() != graph.top_generator:
+ # Jump to node's subgraph if we are not already in it
+ if graph.generator != target_gen.get_parent():
+ graph.update_view(target_gen.get_parent())
+
+ # Center view on bookmarked node
+ var bookmark_node_path : NodePath = "node_" + target_gen.name
+ if graph.has_node(bookmark_node_path):
+ var node : GraphElement = graph.get_node(bookmark_node_path)
+ if node != null:
+ graph.select_none()
+ node.selected = true
+ var center := node.position_offset + 0.5 * node.size
+ var tween := get_tree().create_tween()
+ var target_offset := center * graph.zoom - 0.5 * graph.size
+ tween.tween_property(graph, "scroll_offset", target_offset, 0.5).set_ease(
+ Tween.EASE_IN_OUT).set_trans(Tween.TRANS_CUBIC)
+
+func _on_tree_item_rmb_selected(_mouse_position : Vector2i) -> void:
+ mm_globals.popup_menu($ContextMenu, self)
+
+func _on_tree_gui_input(event : InputEvent) -> void:
+ if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
+ if event.double_click:
+ accept_event()
+ if tree.get_selected():
+ _on_context_menu_id_pressed(ContextMenu.RENAME)
+ # keep panel pinned while editing
+ is_unpinned_double_click_edited = not get_parent().pinned
+ get_parent().pinned = true
+ elif event is InputEventKey and event.pressed:
+ match event.get_keycode_with_modifiers():
+ KEY_X, KEY_DELETE:
+ _on_context_menu_id_pressed(ContextMenu.DELETE)
+
+func _on_tree_item_mouse_selected(mouse_position : Vector2, mouse_button_index : int) -> void:
+ if mouse_button_index == MOUSE_BUTTON_RIGHT:
+ _on_tree_item_rmb_selected(mouse_position)
+ elif mouse_button_index == MOUSE_BUTTON_LEFT:
+ _on_tree_item_lmb_selected()
+
+func _on_context_menu_id_pressed(id : int) -> void:
+ var item : TreeItem = tree.get_selected()
+ match id:
+ ContextMenu.RENAME:
+ item.set_editable(0, true)
+ tree.edit_selected()
+ ContextMenu.DELETE:
+ if item != null:
+ bookmark_manager.remove_bookmark(item.get_metadata(0))
+ rebuild_bookmark_tree()
+
+func _on_tree_item_edited() -> void:
+ var item : TreeItem = tree.get_selected()
+ item.set_editable(0, false)
+ bookmark_manager.edit_bookmark(item.get_metadata(0), item.get_text(0))
+ if is_unpinned_double_click_edited:
+ get_parent().pinned = false
diff --git a/material_maker/panels/graph_edit/graph_bookmark_menu.gd.uid b/material_maker/panels/graph_edit/graph_bookmark_menu.gd.uid
new file mode 100644
index 000000000..18570c75d
--- /dev/null
+++ b/material_maker/panels/graph_edit/graph_bookmark_menu.gd.uid
@@ -0,0 +1 @@
+uid://dxreq1nitp4as
diff --git a/material_maker/panels/graph_edit/graph_edit.gd b/material_maker/panels/graph_edit/graph_edit.gd
index ee4079441..a837990fa 100644
--- a/material_maker/panels/graph_edit/graph_edit.gd
+++ b/material_maker/panels/graph_edit/graph_edit.gd
@@ -294,6 +294,8 @@ func _gui_input(event) -> void:
accept_event()
KEY_F:
colorize_nodes()
+ KEY_B:
+ bookmark_node()
KEY_G:
if not get_selected_nodes().is_empty():
has_grab = true
@@ -376,6 +378,7 @@ func add_node(node) -> void:
add_child(node)
move_child(node, 0)
node.connect("delete_request", Callable(self, "remove_node").bind(node))
+ add_default_bookmark(node)
func swap_node_inputs() -> void:
var selected_nodes : Array = get_selected_nodes()
@@ -536,6 +539,7 @@ func do_remove_node(node) -> void:
remove_child(node)
node.queue_free()
send_changed_signal()
+ get_node("/root/MainWindow/BookmarkManager").updated_from_graph.emit()
# Global operations on graph
@@ -1065,7 +1069,6 @@ func create_subgraph() -> void:
if subgraph != null:
update_view(subgraph)
-
func _on_ButtonShowTree_pressed() -> void:
var graph_tree : Popup = preload("res://material_maker/widgets/graph_tree/graph_tree.tscn").instantiate()
add_child(graph_tree)
@@ -1868,6 +1871,28 @@ func colorize_nodes() -> void:
popup.popup_hide.connect(popup.queue_free)
popup.popup()
+func bookmark_node() -> void:
+ var bookmark_manager : BookmarkManager = mm_globals.main_window.bookmark_manager
+ var selected_nodes := get_selected_nodes()
+ if selected_nodes.size() != 1:
+ return
+ var selected_node : GraphElement = selected_nodes[0]
+ var generator_path : String = "./" + str(top_generator.get_path_to(selected_node.generator))
+ if not bookmark_manager.bookmarks.has(generator_path):
+ var tween := get_tree().create_tween()
+ tween.tween_property(selected_node, "modulate", Color.DIM_GRAY, 0.1)
+ tween.parallel().tween_property(selected_node, "modulate", Color.WHITE, 0.15).set_delay(0.15)
+ bookmark_manager.add_bookmark(selected_node, generator_path)
+
+func add_default_bookmark(node : GraphElement) -> void:
+ if BookmarkManager.is_default_bookmark_node(node):
+ if not node.generator:
+ await get_tree().process_frame
+ var node_path = "./" + str(top_generator.get_path_to(node.generator))
+ var bookmark_manager : BookmarkManager = mm_globals.main_window.bookmark_manager
+ if not bookmark_manager.bookmarks.has(node_path) and node.name == "node_Material":
+ bookmark_manager.add_bookmark_from_path(node_path, BookmarkManager.get_label_from_node(node))
+
func _on_resized() -> void:
$GraphUI.size = Vector2.ZERO
$GraphUI.position = global_position
diff --git a/material_maker/panels/hierarchy/hierarchy_panel.gd b/material_maker/panels/hierarchy/hierarchy_panel.gd
index ef6d63ad8..bb6c3ff70 100644
--- a/material_maker/panels/hierarchy/hierarchy_panel.gd
+++ b/material_maker/panels/hierarchy/hierarchy_panel.gd
@@ -96,6 +96,7 @@ func fill_item(item : TreeItem, generator : MMGenGraph, selected : MMGenGraph, i
func _on_Hierarchy_item_double_clicked() -> void:
if tree.get_selected() != null:
+ print(tree.get_selected().get_metadata(0).get_tree_string())
emit_signal("group_selected", tree.get_selected().get_metadata(0))
func on_view_updated(generator) -> void:
diff --git a/material_maker/projects_panel.tscn b/material_maker/projects_panel.tscn
index 538bfa34d..b3caf9fc3 100644
--- a/material_maker/projects_panel.tscn
+++ b/material_maker/projects_panel.tscn
@@ -11,6 +11,7 @@
[ext_resource type="Script" uid="uid://bxwor0k6svci8" path="res://material_maker/panels/graph_edit/graph_view_menu.gd" id="8_nl2qi"]
[ext_resource type="Script" path="res://material_maker/panels/graph_edit/graph_align_menu.gd" id="8_r5hxx"]
[ext_resource type="ButtonGroup" uid="uid://biv6we3po8wbb" path="res://material_maker/line_style_btn_group.tres" id="10_lbgjg"]
+[ext_resource type="Script" uid="uid://dxreq1nitp4as" path="res://material_maker/panels/graph_edit/graph_bookmark_menu.gd" id="11_qnupl"]
[sub_resource type="Shader" id="1"]
resource_local_to_scene = true
@@ -174,7 +175,7 @@ layout_mode = 0
offset_left = 283.0
offset_top = -56.0
offset_right = 391.0
-offset_bottom = -32.0
+offset_bottom = -31.0
theme_type_variation = &"MM_PanelMenuSubPanel"
script = ExtResource("7_lbgjg")
@@ -407,6 +408,56 @@ max_value = 2.0
step = 0.01
float_only = true
+[node name="BookmarkMenu" type="Button" parent="MenuBar/HBox/MainGraphMenuBar/HBox"]
+custom_minimum_size = Vector2(40, 25)
+layout_mode = 2
+tooltip_text = "Bookmarks"
+theme_type_variation = &"MM_PanelMenuButton"
+toggle_mode = true
+button_mask = 3
+script = ExtResource("6_r5hxx")
+icon_name = "bookmark"
+
+[node name="BookmarkMenuPanel" type="PanelContainer" parent="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu"]
+top_level = true
+custom_minimum_size = Vector2(200, 200)
+layout_mode = 0
+offset_right = 200.0
+offset_bottom = 200.0
+theme_type_variation = &"MM_PanelMenuSubPanel"
+script = ExtResource("11_qnupl")
+
+[node name="MarginContainer" type="MarginContainer" parent="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel"]
+visible = false
+layout_mode = 2
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+
+[node name="EmptyLabel" type="Label" parent="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel/MarginContainer"]
+modulate = Color(1, 1, 1, 0.51900005)
+layout_mode = 2
+text = "No Bookmarks"
+horizontal_alignment = 1
+vertical_alignment = 1
+
+[node name="Tree" type="Tree" parent="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel"]
+layout_mode = 2
+theme_override_constants/item_margin = 0
+allow_reselect = true
+allow_search = false
+hide_root = true
+auto_tooltip = false
+
+[node name="ContextMenu" type="PopupMenu" parent="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel"]
+allow_search = false
+item_count = 2
+item_0/text = "Rename"
+item_0/id = 0
+item_1/text = "Delete"
+item_1/id = 1
+
[node name="ArrangeNodes" type="Button" parent="MenuBar/HBox/MainGraphMenuBar/HBox"]
visible = false
custom_minimum_size = Vector2(25, 25)
@@ -522,6 +573,10 @@ Scroll to zoom the 3D Preview."
[connection signal="value_changed" from="MenuBar/HBox/MainGraphMenuBar/HBox/ViewMenu/ViewMenuPanel/VBoxContainer/VBoxContainer2/Thickness/LineThickness" to="MenuBar/HBox/MainGraphMenuBar/HBox/ViewMenu/ViewMenuPanel" method="_on_line_thickness_value_changed"]
[connection signal="value_changed" from="MenuBar/HBox/MainGraphMenuBar/HBox/ViewMenu/ViewMenuPanel/VBoxContainer/VBoxContainer2/Curvature/LineCurvature" to="MenuBar/HBox/MainGraphMenuBar/HBox/ViewMenu" method="_on_line_curvature_value_changed"]
[connection signal="value_changed" from="MenuBar/HBox/MainGraphMenuBar/HBox/ViewMenu/ViewMenuPanel/VBoxContainer/VBoxContainer2/Curvature/LineCurvature" to="MenuBar/HBox/MainGraphMenuBar/HBox/ViewMenu/ViewMenuPanel" method="_on_line_curvature_value_changed"]
+[connection signal="gui_input" from="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel/Tree" to="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel" method="_on_tree_gui_input"]
+[connection signal="item_edited" from="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel/Tree" to="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel" method="_on_tree_item_edited"]
+[connection signal="item_mouse_selected" from="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel/Tree" to="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel" method="_on_tree_item_mouse_selected"]
+[connection signal="id_pressed" from="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel/ContextMenu" to="MenuBar/HBox/MainGraphMenuBar/HBox/BookmarkMenu/BookmarkMenuPanel" method="_on_context_menu_id_pressed"]
[connection signal="pressed" from="MenuBar/HBox/AlignMenu/HBox/AlignStart" to="MenuBar/HBox/AlignMenu" method="_on_align_start_pressed"]
[connection signal="pressed" from="MenuBar/HBox/AlignMenu/HBox/AlignCenter" to="MenuBar/HBox/AlignMenu" method="_on_align_center_pressed"]
[connection signal="pressed" from="MenuBar/HBox/AlignMenu/HBox/AlignEnd" to="MenuBar/HBox/AlignMenu" method="_on_align_end_pressed"]
diff --git a/material_maker/theme/classic.tres b/material_maker/theme/classic.tres
index ee3307336..c5c391144 100644
--- a/material_maker/theme/classic.tres
+++ b/material_maker/theme/classic.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Theme" script_class="EnhancedTheme" load_steps=4 format=3 uid="uid://wuddtc1fglln"]
+[gd_resource type="Theme" script_class="EnhancedTheme" format=3 uid="uid://wuddtc1fglln"]
[ext_resource type="Theme" uid="uid://w487nirev8y5" path="res://material_maker/theme/classic_base.tres" id="1_meah8"]
[ext_resource type="Script" uid="uid://3ga2k3abkk0d" path="res://material_maker/theme/enhanced_theme_system/color_swap.gd" id="5_qpu3p"]
diff --git a/material_maker/theme/default.tres b/material_maker/theme/default.tres
index f19d6072b..69e05b275 100644
--- a/material_maker/theme/default.tres
+++ b/material_maker/theme/default.tres
@@ -367,8 +367,7 @@ metadata/recolor = true
[sub_resource type="AtlasTexture" id="AtlasTexture_8xpdb"]
atlas = ExtResource("1_s43fy")
-region = Rect2(48, 208, 16, 16)
-metadata/recolor = true
+region = Rect2(32, 224, 16, 16)
[sub_resource type="AtlasTexture" id="AtlasTexture_5aawx"]
atlas = ExtResource("1_s43fy")
@@ -1329,6 +1328,7 @@ MM_Icons/icons/arrange_nodes = SubResource("AtlasTexture_60g77")
MM_Icons/icons/arrow_left = SubResource("AtlasTexture_q32qs")
MM_Icons/icons/arrow_right = SubResource("AtlasTexture_r3xak")
MM_Icons/icons/arrow_updown = SubResource("AtlasTexture_ovvp6")
+MM_Icons/icons/bookmark = SubResource("AtlasTexture_8xpdb")
MM_Icons/icons/camera = SubResource("AtlasTexture_cscp3")
MM_Icons/icons/color_picker = SubResource("AtlasTexture_j13i3")
MM_Icons/icons/connection_bezier = SubResource("AtlasTexture_rqjp3")
diff --git a/material_maker/theme/default_theme_icons.svg b/material_maker/theme/default_theme_icons.svg
index 1f2fe015f..5b3da27e0 100644
--- a/material_maker/theme/default_theme_icons.svg
+++ b/material_maker/theme/default_theme_icons.svg
@@ -28,9 +28,9 @@
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
- inkscape:zoom="8"
- inkscape:cx="36.6875"
- inkscape:cy="213.8125"
+ inkscape:zoom="22.627417"
+ inkscape:cx="40.150407"
+ inkscape:cy="225.01464"
inkscape:window-width="1152"
inkscape:window-height="981"
inkscape:window-x="0"
@@ -2739,6 +2739,18 @@
d="m 17.836663,238.07516 2.571223,-9.97576 9.642069,-2.23754"
inkscape:label="bevel"
style="stroke-linecap:round;stroke-linejoin:round" />
+
+
+
void:
+ bookmarks = new_bookmarks
+
+static func is_default_bookmark_node(node : GraphElement) -> bool:
+ return (node.get_script() in [MMGraphComment, MMGraphCommentLine]
+ or node.name in ["node_Material"])
+
+static func get_label_from_node(node : GraphElement) -> String:
+ var label : String = ""
+ if node is GraphNode and node.get("title"):
+ label = node.title
+ elif node is MMGraphComment:
+ label = node.generator.title
+ elif node is MMGraphCommentLine:
+ label = node.generator.text
+ if label == "":
+ label = node.name.trim_prefix("node_")
+ return label
+
+func add_bookmark(node : GraphElement, gen_path : String) -> void:
+ if not bookmarks.has(gen_path):
+ var label : String = get_label_from_node(node)
+ bookmarks[gen_path] = label
+ mm_globals.set_tip_text("Added bookmark for \"%s\"" % label, 1.0, 1)
+ bookmarks_added.emit()
+
+func add_bookmark_from_path(path : String, label : String) -> void:
+ bookmarks[path] = label
+ bookmarks_added.emit()
+
+func remove_bookmark(path : String) -> void:
+ bookmarks.erase(path)
+ bookmarks_edit_removed.emit()
+
+func edit_bookmark(path : String, new_label : String) -> void:
+ bookmarks[path] = new_label
+ bookmarks_edit_removed.emit()
diff --git a/material_maker/tools/bookmark_manager/bookmark_manager.gd.uid b/material_maker/tools/bookmark_manager/bookmark_manager.gd.uid
new file mode 100644
index 000000000..61843aeb3
--- /dev/null
+++ b/material_maker/tools/bookmark_manager/bookmark_manager.gd.uid
@@ -0,0 +1 @@
+uid://dbvsf3qyuyrq6