1- import bpy , ctypes , math
2-
3- class POINT (ctypes .Structure ):
4- _fields_ = [("x" , ctypes .c_long ), ("y" , ctypes .c_long )]
1+ import bpy , ctypes , math , sys
52
63class BLUI_OT_right_mouse_navigation (bpy .types .Operator ):
74 """Timer that decides whether to display a menu after Right Click"""
@@ -13,19 +10,36 @@ class BLUI_OT_right_mouse_navigation(bpy.types.Operator):
1310 _count = 0
1411 _move_distance = 0
1512 MOUSE_RIGHTUP = 0x0010
16- MOUSE_MOVE = 0x0001
1713 _finished = False
1814 _callMenu = False
1915 _ortho = False
16+ _back_to_ortho = False
17+ menu_by_mode = {
18+ 'OBJECT' : 'VIEW3D_MT_object_context_menu' ,
19+ 'EDIT_MESH' : 'VIEW3D_MT_edit_mesh_context_menu' ,
20+ 'EDIT_SURFACE' : 'VIEW3D_MT_edit_surface' ,
21+ 'EDIT_TEXT' : 'VIEW3D_MT_edit_font_context_menu' ,
22+ 'EDIT_ARMATURE' : 'VIEW3D_MT_edit_armature' ,
23+ 'EDIT_CURVE' : 'VIEW3D_MT_edit_curve_context_menu' ,
24+ 'EDIT_METABALL' : 'VIEW3D_MT_edit_metaball_context_menu' ,
25+ 'EDIT_LATTICE' : 'VIEW3D_MT_edit_lattice_context_menu' ,
26+ 'POSE' : 'VIEW3D_MT_pose_context_menu' ,
27+ 'PAINT_VERTEX' : 'VIEW3D_PT_paint_vertex_context_menu' ,
28+ 'PAINT_WEIGHT' : 'VIEW3D_PT_paint_weight_context_menu' ,
29+ 'PAINT_TEXTURE' : 'VIEW3D_PT_paint_texture_context_menu' ,
30+ 'SCULPT' : 'VIEW3D_PT_sculpt_context_menu' }
2031
2132 def modal (self , context , event ):
22-
33+
2334 preferences = context .preferences
2435 addon_prefs = preferences .addons [__package__ ].preferences
2536
26- # Check if the Viewport is Perspective or Orthographic
27- if bpy .context .region_data .is_perspective :
28- self ._ortho = False
37+ if context .space_data .type == 'VIEW_3D' :
38+ # Check if the Viewport is Perspective or Orthographic
39+ if bpy .context .region_data .is_perspective :
40+ self ._ortho = False
41+ else :
42+ self ._back_to_ortho = True
2943
3044 # The _finished Boolean acts as a flag to exit the modal loop,
3145 # it is not made True until after the cancel function is called
@@ -34,8 +48,6 @@ def modal(self, context, event):
3448 def reset_cursor ():
3549 # Reset blender window cursor to previous position
3650 context .window .cursor_warp (self .view_x , self .view_y )
37- # Reset Windows OS cursor to previous position
38- ctypes .windll .user32 .SetCursorPos (self .mouse_x , self .mouse_y )
3951
4052 if self ._callMenu :
4153 # Always reset the cursor if menu is called, as that implies a canceled navigation
@@ -45,98 +57,83 @@ def reset_cursor():
4557 # Exit of a full navigation. Only reset the cursor if the preference (default False) is enabled
4658 if addon_prefs .reset_cursor_on_exit :
4759 reset_cursor ()
48-
60+
61+ if self ._back_to_ortho :
62+ bpy .ops .view3d .view_persportho ()
63+
4964 return {'CANCELLED' }
50-
51- if context .space_data .type == 'VIEW_3D' :
65+
66+ if context .space_data .type == 'VIEW_3D' or addon_prefs . enable_for_node_editors and context . space_data . type == 'NODE_EDITOR' :
5267 # Calculate mousemove distance before it reaches threshold
53- if event .type == "MOUSEMOVE" and self ._move_distance < addon_prefs .distancepreference :
68+ if event .type == "MOUSEMOVE" and self ._move_distance < addon_prefs .distance :
5469 xDelta = event .mouse_x - event .mouse_prev_x
5570 yDelta = event .mouse_y - event .mouse_prev_y
5671 deltaDistance = math .sqrt (xDelta * xDelta + yDelta * yDelta )
5772 self ._move_distance += deltaDistance
5873
5974 if event .type in {'RIGHTMOUSE' }:
6075 if event .value in {'RELEASE' }:
61- # This fakes a Right Mouse Up event using Ctypes
62- ctypes .windll .user32 .mouse_event (self .MOUSE_RIGHTUP )
76+ if sys .platform == 'win32' :
77+ # This fakes a Right Mouse Up event using Ctypes
78+ ctypes .windll .user32 .mouse_event (self .MOUSE_RIGHTUP )
6379 # This brings back our mouse cursor to use with the menu
6480 context .window .cursor_modal_restore ()
6581 # If the length of time you've been holding down
6682 # Right Mouse and Mouse move distance is longer than the threshold value,
6783 # then set flag to call a context menu
68- if self ._move_distance < addon_prefs .distancepreference and self ._count < addon_prefs .timepreference :
69- # context.window.cursor_warp(self.view_x, self.view_y)
84+ if self ._move_distance < addon_prefs .distance and self ._count < addon_prefs .time :
7085 self ._callMenu = True
7186 self .cancel (context )
7287 # We now set the flag to true to exit the modal operator on the next loop through
7388 self ._finished = True
7489 return {'PASS_THROUGH' }
7590
7691 if event .type == 'TIMER' :
77- if self ._count <= addon_prefs .timepreference :
92+ if self ._count <= addon_prefs .time :
7893 self ._count += 0.01
7994 return {'PASS_THROUGH' }
8095
8196 def callMenu (self , context ):
82- # try to call a context menu, and if that fails, call a context panel.
83- # Most of Blender's context menus can be called with the code in the
84- # 'try: except:' section, but there are a few modes that don't follow
85- # the same conventions, these are accounted for here
86- if context .mode == 'EDIT_ARMATURE' :
87- bpy .ops .wm .call_menu (
88- name = "VIEW3D_MT_" + context .mode .lower ()[5 :].strip () + "_context_menu"
89- )
90- return {'PASS_THROUGH' }
91- if context .mode == 'EDIT_SURFACE' :
92- bpy .ops .wm .call_menu (
93- name = "VIEW3D_MT_" + context .mode .lower ()
94- )
95- return {'PASS_THROUGH' }
96- if context .mode == 'EDIT_TEXT' :
97- bpy .ops .wm .call_menu (
98- name = "VIEW3D_MT_edit_font_context_menu"
99- )
100- return {'PASS_THROUGH' }
97+ if context .space_data .type == 'NODE_EDITOR' :
98+ if context .space_data .node_tree :
99+ bpy .ops .node .add_search ('INVOKE_DEFAULT' )
101100 else :
102101 try :
103- bpy .ops .wm .call_menu (
104- name = "VIEW3D_MT_" + context .mode .lower () + "_context_menu"
105- )
102+ bpy .ops .wm .call_menu (name = self .menu_by_mode [context .mode ])
106103 except :
107- bpy .ops .wm .call_panel (
108- name = "VIEW3D_PT_" + context .mode .lower () + "_context_menu"
109- )
104+ bpy .ops .wm .call_panel (name = self .menu_by_mode [context .mode ])
110105
111106 def invoke (self , context , event ):
112- # Store Windows OS cursor position
113- cursor = POINT ()
114- ctypes .windll .user32 .GetCursorPos (ctypes .byref (cursor ))
115- self .mouse_x = cursor .x
116- self .mouse_y = cursor .y
117107 # Store Blender cursor position
118108 self .view_x = event .mouse_x
119109 self .view_y = event .mouse_y
120110 return self .execute (context )
121111
122112 def execute (self , context ):
113+ preferences = context .preferences
114+ addon_prefs = preferences .addons [__package__ ].preferences
115+
123116 # Execute is the first thing called in our operator, so we start by
124117 # calling Blender's built-in Walk Navigation
125118 if context .space_data .type == 'VIEW_3D' :
126119 bpy .ops .view3d .walk ('INVOKE_DEFAULT' )
127-
128- if self ._ortho :
129- bpy .ops .view3d .view_persportho ()
120+ # Adding the timer and starting the loop
121+ wm = context .window_manager
122+ self ._timer = wm .event_timer_add (0.1 , window = context .window )
123+ wm .modal_handler_add (self )
124+ return {'RUNNING_MODAL' }
125+
126+ elif addon_prefs .enable_for_node_editors and context .space_data .type == 'NODE_EDITOR' :
127+ bpy .ops .view2d .pan ('INVOKE_DEFAULT' )
130128
131129 wm = context .window_manager
132130 # Adding the timer and starting the loop
133131 self ._timer = wm .event_timer_add (0.1 , window = context .window )
134132 wm .modal_handler_add (self )
135133 return {'RUNNING_MODAL' }
134+
136135 elif context .space_data .type == 'IMAGE_EDITOR' :
137- bpy .ops .wm .call_panel (
138- name = "VIEW3D_PT_" + context .mode .lower () + "_context_menu"
139- )
136+ bpy .ops .wm .call_panel (name = "VIEW3D_PT_paint_texture_context_menu" )
140137 return {'FINISHED' }
141138
142139 def cancel (self , context ):
0 commit comments