Skip to content

Commit

Permalink
Keymap, Cursor Reset Fix, Distance Removed
Browse files Browse the repository at this point in the history
Fixed the keymap registration (again, and hopefully for the last time), I had been trying to make all the keymap changes on a single map, not realizing that I should have been switching between the addon keymap that I had created and the user's active keymap.

Cursor Reset behavior seems to have been updated in Blender so that the cursor will remember its last screen position. This is the 'patched' behavior that this addon previously had implemented, which made the Cursor Reset setting redundant.
I have implemented the previous 'cursor centering' behavior as the new 'Cursor Reset' behavior.
The settings have flipped, but now the old behavior remains.

The distance threshold did not have any effect on the addon, so it has been removed. If this is something that is missed, I can see about reimplementing it.
  • Loading branch information
SpectralVectors authored Apr 10, 2024
1 parent 19c7480 commit 258b228
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 98 deletions.
42 changes: 20 additions & 22 deletions Preferences.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import bpy
from bpy.props import (
BoolProperty,
FloatProperty,
)
from bpy.types import AddonPreferences


def update_node_keymap(self, context):
wm = context.window_manager
kc = wm.keyconfigs.user
for key in kc.keymaps['Node Editor'].keymap_items:
active_kc = wm.keyconfigs.active
for key in active_kc.keymaps['Node Editor'].keymap_items:
if (
key.idname == "wm.call_menu"
and key.type == "RIGHTMOUSE"
):
key.active = not key.active

addon_kc = wm.keyconfigs.addon
for key in addon_kc.keymaps['Node Editor'].keymap_items:
if (
key.idname == "blui.right_mouse_navigation"
and key.type == "RIGHTMOUSE"
):
key.active = not key.active


class RightMouseNavigationPreferences(bpy.types.AddonPreferences):
class RightMouseNavigationPreferences(AddonPreferences):
bl_idname = __package__

time: bpy.props.FloatProperty(
time: FloatProperty(
name="Time Threshold",
description="How long you have hold right mouse to open menu",
default=0.3,
default=1.0,
min=0.1,
max=2
max=10,
)

distance: bpy.props.FloatProperty(
name="Distance Threshold",
description="How far you have to move the mouse to trigger navigation",
default=20,
min=1,
max=200
)

reset_cursor_on_exit: bpy.props.BoolProperty(
reset_cursor_on_exit: BoolProperty(
name="Reset Cursor on Exit",
description="After exiting navigation, this determines if the cursor resets to where RMB was clicked (if checked) or stays in the center (if unchecked)",
default=True
description="After exiting navigation, this determines if the cursor stays "
"where RMB was clicked (if unchecked) or resets to the center (if checked)",
default=False,
)

enable_for_node_editors: bpy.props.BoolProperty(
enable_for_node_editors: BoolProperty(
name="Enable for Node Editors",
description="Right Mouse will pan the view / open the Node Add/Search Menu",
default=False,
Expand All @@ -55,14 +55,12 @@ def draw(self, context):

box = layout.box()
box.label(text="Menu / Movement", icon='DRIVER_DISTANCE')
row = box.row()
row.prop(self, 'time')
row.prop(self, 'distance')
box.prop(self, 'time')

row = layout.row()
box = row.box()
box.label(text="Cursor", icon='ORIENTATION_CURSOR')
box.prop(self, 'reset_cursor_on_exit')
box = row.box()
box.label(text='Node Editor', icon='NODETREE')
box.prop(self, 'enable_for_node_editors')
box.prop(self, 'enable_for_node_editors')
72 changes: 40 additions & 32 deletions RightMouseNavigation.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import bpy, ctypes, math, sys
import bpy
from bpy.types import Operator
import ctypes
import sys

class BLUI_OT_right_mouse_navigation(bpy.types.Operator):

class BLUI_OT_right_mouse_navigation(Operator):
"""Timer that decides whether to display a menu after Right Click"""
bl_idname = "blui.right_mouse_navigation"
bl_label = "Right Mouse Navigation"
bl_options = {'REGISTER', 'UNDO'}

_timer = None
_count = 0
_move_distance = 0
MOUSE_RIGHTUP = 0x0010
_finished = False
_callMenu = False
Expand All @@ -27,34 +30,44 @@ class BLUI_OT_right_mouse_navigation(bpy.types.Operator):
'PAINT_VERTEX': 'VIEW3D_PT_paint_vertex_context_menu',
'PAINT_WEIGHT': 'VIEW3D_PT_paint_weight_context_menu',
'PAINT_TEXTURE': 'VIEW3D_PT_paint_texture_context_menu',
'SCULPT': 'VIEW3D_PT_sculpt_context_menu'}
'SCULPT': 'VIEW3D_PT_sculpt_context_menu',
}

def modal(self, context, event):

preferences = context.preferences
addon_prefs = preferences.addons[__package__].preferences
enable_nodes = addon_prefs.enable_for_node_editors

space_type = context.space_data.type

if context.space_data.type == 'VIEW_3D':
if space_type == 'VIEW_3D':
# Check if the Viewport is Perspective or Orthographic
if bpy.context.region_data.is_perspective:
self._ortho = False
else:
self._back_to_ortho = True

# The _finished Boolean acts as a flag to exit the modal loop,
# The _finished Boolean acts as a flag to exit the modal loop,
# it is not made True until after the cancel function is called
if self._finished:

def reset_cursor():
# Reset blender window cursor to previous position
context.window.cursor_warp(self.view_x, self.view_y)
area = context.area
x = area.x
y = area.y
x += int(area.width / 2)
y += int(area.height / 2)
bpy.context.window.cursor_warp(x, y)

if self._callMenu:
# Always reset the cursor if menu is called, as that implies a canceled navigation
reset_cursor()
if addon_prefs.reset_cursor_on_exit and not space_type == 'NODE_EDITOR':
reset_cursor()
self.callMenu(context)
else:
# Exit of a full navigation. Only reset the cursor if the preference (default False) is enabled
# Exit of a full navigation. Only reset the cursor if the preference is enabled
if addon_prefs.reset_cursor_on_exit:
reset_cursor()

Expand All @@ -63,44 +76,37 @@ def reset_cursor():

return {'CANCELLED'}

if context.space_data.type == 'VIEW_3D' or addon_prefs.enable_for_node_editors and context.space_data.type == 'NODE_EDITOR':
# Calculate mousemove distance before it reaches threshold
if event.type == "MOUSEMOVE" and self._move_distance < addon_prefs.distance:
xDelta = event.mouse_x - event.mouse_prev_x
yDelta = event.mouse_y - event.mouse_prev_y
deltaDistance = math.sqrt(xDelta*xDelta + yDelta*yDelta)
self._move_distance += deltaDistance

if space_type == 'VIEW_3D' or space_type == 'NODE_EDITOR' and enable_nodes:
if event.type in {'RIGHTMOUSE'}:
if event.value in {'RELEASE'}:
if sys.platform == 'win32':
if sys.platform.startswith('win'):
# This fakes a Right Mouse Up event using Ctypes
ctypes.windll.user32.mouse_event(self.MOUSE_RIGHTUP)
# This brings back our mouse cursor to use with the menu
context.window.cursor_modal_restore()
# If the length of time you've been holding down
# Right Mouse and Mouse move distance is longer than the threshold value,
# If the length of time you've been holding down
# Right Mouse and Mouse move distance is longer than the threshold value,
# then set flag to call a context menu
if self._move_distance < addon_prefs.distance and self._count < addon_prefs.time:
if self._count < addon_prefs.time:
self._callMenu = True
self.cancel(context)
# We now set the flag to true to exit the modal operator on the next loop through
# We now set the flag to true to exit the modal operator on the next loop
self._finished = True
return {'PASS_THROUGH'}

if event.type == 'TIMER':
if self._count <= addon_prefs.time:
self._count += 0.01
self._count += 0.1
return {'PASS_THROUGH'}

def callMenu(self, context):
if context.space_data.type == 'NODE_EDITOR':
if context.space_data.node_tree:
bpy.ops.wm.search_menu('INVOKE_DEFAULT')
bpy.ops.wm.search_single_menu('INVOKE_DEFAULT', menu_idname='NODE_MT_add')
else:
try:
bpy.ops.wm.call_menu(name=self.menu_by_mode[context.mode])
except:
except RuntimeError:
bpy.ops.wm.call_panel(name=self.menu_by_mode[context.mode])

def invoke(self, context, event):
Expand All @@ -112,30 +118,32 @@ def invoke(self, context, event):
def execute(self, context):
preferences = context.preferences
addon_prefs = preferences.addons[__package__].preferences
enable_nodes = addon_prefs.enable_for_node_editors

space_type = context.space_data.type

# Execute is the first thing called in our operator, so we start by
# calling Blender's built-in Walk Navigation
if context.space_data.type == 'VIEW_3D':
if space_type == 'VIEW_3D':
bpy.ops.view3d.walk('INVOKE_DEFAULT')
# Adding the timer and starting the loop
wm = context.window_manager
self._timer = wm.event_timer_add(0.1, window=context.window)
wm.modal_handler_add(self)
return {'RUNNING_MODAL'}

elif addon_prefs.enable_for_node_editors and context.space_data.type == 'NODE_EDITOR':
elif space_type == 'NODE_EDITOR' and enable_nodes:
bpy.ops.view2d.pan('INVOKE_DEFAULT')

wm = context.window_manager
# Adding the timer and starting the loop
self._timer = wm.event_timer_add(0.1, window=context.window)
self._timer = wm.event_timer_add(0.01, window=context.window)
wm.modal_handler_add(self)
return {'RUNNING_MODAL'}
elif context.space_data.type == 'IMAGE_EDITOR':

elif space_type == 'IMAGE_EDITOR':
bpy.ops.wm.call_panel(name="VIEW3D_PT_paint_texture_context_menu")
return {'FINISHED'}

def cancel(self, context):
wm = context.window_manager
wm.event_timer_remove(self._timer)
wm.event_timer_remove(self._timer)
Loading

0 comments on commit 258b228

Please sign in to comment.