diff --git a/node_wrangler/operators.py b/node_wrangler/operators.py index 6b541badc..9bfe2172f 100644 --- a/node_wrangler/operators.py +++ b/node_wrangler/operators.py @@ -20,6 +20,7 @@ from mathutils import Vector from os import path from glob import glob from copy import copy +import json from itertools import chain from .interface import NWConnectionListInputs, NWConnectionListOutputs @@ -486,6 +487,54 @@ class NWAddAttrNode(Operator, NWBase): return {'FINISHED'} +class NWFastPreview(Operator): + bl_idname = "node.nw_fast_preview" + bl_label = "Fast Preview" + bl_description = "Preview favorite nodes by pressing 1, 2, 3, 4 and 5" + bl_options = {'REGISTER', 'UNDO'} + + # Workaround: simulating a dict with json.loads() and json.dumps() + node_preview_map : StringProperty(name="") + update_map : BoolProperty(default=False) + + viewer_index : IntProperty() + + @classmethod + def poll(self, context): + return bpy.ops.node.link_viewer.poll() + + def execute(self, context): + nodes, _ = get_nodes_links(context) + + if self.node_preview_map != '': + temp_dict = json.loads(self.node_preview_map) + else: + temp_dict = {} + + selected_nodes = context.selected_nodes + if self.update_map: + if len(selected_nodes) > 0: + n1 = selected_nodes[0] + + temp_dict[self.viewer_index] = n1.name + self.node_preview_map = json.dumps(temp_dict) + bpy.ops.node.link_viewer() + self.report({'INFO'}, "Set node %s to shortcut %i" % (n1.name, self.viewer_index)) + else: + self.report({'ERROR'}, "No previews to set. Reason: No nodes selected.") + + else: + if str(self.viewer_index) in temp_dict: + if temp_dict[str(self.viewer_index)] in nodes: + n = nodes[temp_dict[str(self.viewer_index)]] + n.select = True + nodes.active = n + bpy.ops.node.link_viewer() + n.select = False + + return {'FINISHED'} + + class NWPreviewNode(Operator, NWBase): bl_idname = "node.nw_preview_node" bl_label = "Preview Node" @@ -2592,6 +2641,63 @@ class NWAddMultipleImages(Operator, NWBase, ImportHelper): return {'FINISHED'} +class NWViewerFocus(bpy.types.Operator): + """Set the viewer tile center to the mouse position""" + bl_idname = "node.nw_viewer_focus" + bl_label = "Viewer Focus" + + x: bpy.props.IntProperty() + y: bpy.props.IntProperty() + + @classmethod + def poll(cls, context): + return (nw_check(cls, context) + and nw_check_space_type(cls, context, {'CompositorNodeTree'})) + + def execute(self, context): + return {'FINISHED'} + + def invoke(self, context, event): + render = context.scene.render + space = context.space_data + percent = render.resolution_percentage * 0.01 + + nodes, links = get_nodes_links(context) + viewers = [n for n in nodes if n.type == 'VIEWER'] + + if viewers: + mlocx = event.mouse_region_x + mlocy = event.mouse_region_y + select_node = bpy.ops.node.select(location=(mlocx, mlocy), extend=False) + + if 'FINISHED' not in select_node: # only run if we're not clicking on a node + region_x = context.region.width + region_y = context.region.height + + region_center_x = context.region.width / 2 + region_center_y = context.region.height / 2 + + bd_x = render.resolution_x * percent * space.backdrop_zoom + bd_y = render.resolution_y * percent * space.backdrop_zoom + + backdrop_center_x = (bd_x / 2) - space.backdrop_offset[0] + backdrop_center_y = (bd_y / 2) - space.backdrop_offset[1] + + margin_x = region_center_x - backdrop_center_x + margin_y = region_center_y - backdrop_center_y + + abs_mouse_x = (mlocx - margin_x) / bd_x + abs_mouse_y = (mlocy - margin_y) / bd_y + + for node in viewers: + node.center_x = abs_mouse_x + node.center_y = abs_mouse_y + else: + return {'PASS_THROUGH'} + + return self.execute(context) + + class NWSaveViewer(bpy.types.Operator, ExportHelper): """Save the current viewer node to an image file""" bl_idname = "node.nw_save_viewer" @@ -2759,6 +2865,7 @@ classes = ( NWSwapLinks, NWResetBG, NWAddAttrNode, + NWFastPreview, NWPreviewNode, NWFrameSelected, NWReloadImages, @@ -2781,6 +2888,7 @@ classes = ( NWCallInputsMenu, NWAddSequence, NWAddMultipleImages, + NWViewerFocus, NWSaveViewer, NWResetNodes, ) diff --git a/node_wrangler/preferences.py b/node_wrangler/preferences.py index f80da0075..f6521e3a5 100644 --- a/node_wrangler/preferences.py +++ b/node_wrangler/preferences.py @@ -338,6 +338,35 @@ kmi_defs = ( False, (('run_in_geometry_nodes', False),), "Preview node output"), (operators.NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', False, True, True, (('run_in_geometry_nodes', True),), "Preview node output"), + + + # Fast Preview + (operators.NWFastPreview.bl_idname, 'ONE', 'PRESS', False, False, False, + (('viewer_index', 1), ('update_map', False)), "Fast Preview"), + (operators.NWFastPreview.bl_idname, 'ONE', 'PRESS', True, False, False, + (('viewer_index', 1), ('update_map', True)), "Fast Preview"), + + (operators.NWFastPreview.bl_idname, 'TWO', 'PRESS', False, False, False, + (('viewer_index', 2), ('update_map', False)), "Fast Preview"), + (operators.NWFastPreview.bl_idname, 'TWO', 'PRESS', True, False, False, + (('viewer_index', 2), ('update_map', True)), "Fast Preview"), + + (operators.NWFastPreview.bl_idname, 'THREE', 'PRESS', False, False, False, + (('viewer_index', 3), ('update_map', False)), "Fast Preview"), + (operators.NWFastPreview.bl_idname, 'THREE', 'PRESS', True, False, False, + (('viewer_index', 3), ('update_map', True)), "Fast Preview"), + + (operators.NWFastPreview.bl_idname, 'FOUR', 'PRESS', False, False, False, + (('viewer_index', 4), ('update_map', False)), "Fast Preview"), + (operators.NWFastPreview.bl_idname, 'FOUR', 'PRESS', True, False, False, + (('viewer_index', 4), ('update_map', True)), "Fast Preview"), + + (operators.NWFastPreview.bl_idname, 'FIVE', 'PRESS', False, False, False, + (('viewer_index', 5), ('update_map', False)), "Fast Preview"), + (operators.NWFastPreview.bl_idname, 'FIVE', 'PRESS', True, False, False, + (('viewer_index', 5), ('update_map', True)), "Fast Preview"), + + # Reload Images (operators.NWReloadImages.bl_idname, 'R', 'PRESS', False, False, True, None, "Reload images"), # Lazy Mix @@ -347,6 +376,8 @@ kmi_defs = ( # Lazy Connect with Menu (operators.NWLazyConnect.bl_idname, 'RIGHTMOUSE', 'PRESS', False, True, True, (('with_menu', True),), "Lazy Connect with Socket Menu"), + # Viewer Tile Center + (operators.NWViewerFocus.bl_idname, 'LEFTMOUSE', 'DOUBLE_CLICK', False, False, False, None, "Set Viewers Tile Center"), # Align Nodes (operators.NWAlignNodes.bl_idname, 'EQUAL', 'PRESS', False, True, False, None, "Align selected nodes neatly in a row/column"),