diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index a45c505d4cb..5f25f3ea6db 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -329,12 +329,18 @@ typedef enum { GHOST_kStandardCursorCrosshairA, GHOST_kStandardCursorCrosshairB, GHOST_kStandardCursorCrosshairC, + GHOST_kStandardCursorCrosshairD, + GHOST_kStandardCursorBlank, + GHOST_kStandardCursorBox, + GHOST_kStandardCursorBoxDot, + GHOST_kStandardCursorBoxPointer, GHOST_kStandardCursorPencil, GHOST_kStandardCursorUpArrow, GHOST_kStandardCursorDownArrow, GHOST_kStandardCursorVerticalSplit, GHOST_kStandardCursorHorizontalSplit, GHOST_kStandardCursorEraser, + GHOST_kStandardCursorPointer, GHOST_kStandardCursorKnife, GHOST_kStandardCursorEyedropper, GHOST_kStandardCursorZoomIn, diff --git a/intern/ghost/intern/GHOST_WindowWin32.cc b/intern/ghost/intern/GHOST_WindowWin32.cc index d6c5e184705..2dec3b141bd 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cc +++ b/intern/ghost/intern/GHOST_WindowWin32.cc @@ -728,6 +728,9 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const case GHOST_kStandardCursorHorizontalSplit: cursor = ::LoadImage(module, "splith_cursor", IMAGE_CURSOR, cx, cy, flags); break; + case GHOST_kStandardCursorPointer: + cursor = ::LoadImage(module, "pointer_cursor", IMAGE_CURSOR, cx, cy, flags); + break; case GHOST_kStandardCursorKnife: cursor = ::LoadImage(module, "knife_cursor", IMAGE_CURSOR, cx, cy, flags); break; @@ -773,6 +776,21 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const case GHOST_kStandardCursorCrosshairC: cursor = ::LoadImage(module, "crossC_cursor", IMAGE_CURSOR, cx, cy, flags); break; /* Minimal Crosshair C */ + case GHOST_kStandardCursorCrosshairD: + cursor = ::LoadImage(module, "crossD_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Open Crosshair D */ + case GHOST_kStandardCursorBlank: + cursor = ::LoadImage(module, "blank_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Blank */ + case GHOST_kStandardCursorBox: + cursor = ::LoadImage(module, "box_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Box */ + case GHOST_kStandardCursorBoxDot: + cursor = ::LoadImage(module, "boxdot_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Box Dot */ + case GHOST_kStandardCursorBoxPointer: + cursor = ::LoadImage(module, "boxpointer_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Box Pointer */ case GHOST_kStandardCursorBottomSide: case GHOST_kStandardCursorUpDown: cursor = ::LoadImage(module, "movens_cursor", IMAGE_CURSOR, cx, cy, flags); diff --git a/release/datafiles/blender_icons16/icon16_auto_xray.dat b/release/datafiles/blender_icons16/icon16_auto_xray.dat new file mode 100644 index 00000000000..09c5685b63a Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_auto_xray.dat differ diff --git a/release/datafiles/blender_icons16/icon16_edge_default.dat b/release/datafiles/blender_icons16/icon16_edge_default.dat new file mode 100644 index 00000000000..b0ed5dc1fff Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_edge_default.dat differ diff --git a/release/datafiles/blender_icons16/icon16_edge_enclose.dat b/release/datafiles/blender_icons16/icon16_edge_enclose.dat new file mode 100644 index 00000000000..41eb65f7d69 Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_edge_enclose.dat differ diff --git a/release/datafiles/blender_icons16/icon16_edge_touch.dat b/release/datafiles/blender_icons16/icon16_edge_touch.dat new file mode 100644 index 00000000000..d0394925361 Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_edge_touch.dat differ diff --git a/release/datafiles/blender_icons16/icon16_face_default.dat b/release/datafiles/blender_icons16/icon16_face_default.dat new file mode 100644 index 00000000000..01012922c77 Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_face_default.dat differ diff --git a/release/datafiles/blender_icons16/icon16_face_enclose.dat b/release/datafiles/blender_icons16/icon16_face_enclose.dat new file mode 100644 index 00000000000..2158a9ad07b Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_face_enclose.dat differ diff --git a/release/datafiles/blender_icons16/icon16_face_touch.dat b/release/datafiles/blender_icons16/icon16_face_touch.dat new file mode 100644 index 00000000000..af000aea0a9 Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_face_touch.dat differ diff --git a/release/datafiles/blender_icons16/icon16_object_enclose.dat b/release/datafiles/blender_icons16/icon16_object_enclose.dat new file mode 100644 index 00000000000..23fb698e158 Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_object_enclose.dat differ diff --git a/release/datafiles/blender_icons16/icon16_object_touch.dat b/release/datafiles/blender_icons16/icon16_object_touch.dat new file mode 100644 index 00000000000..1690949f5bd Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_object_touch.dat differ diff --git a/release/datafiles/blender_icons16/icon16_select_through.dat b/release/datafiles/blender_icons16/icon16_select_through.dat new file mode 100644 index 00000000000..edc77847d56 Binary files /dev/null and b/release/datafiles/blender_icons16/icon16_select_through.dat differ diff --git a/release/datafiles/blender_icons32/icon32_auto_xray.dat b/release/datafiles/blender_icons32/icon32_auto_xray.dat new file mode 100644 index 00000000000..33439f223bd Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_auto_xray.dat differ diff --git a/release/datafiles/blender_icons32/icon32_edge_default.dat b/release/datafiles/blender_icons32/icon32_edge_default.dat new file mode 100644 index 00000000000..0cbc0105e7d Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_edge_default.dat differ diff --git a/release/datafiles/blender_icons32/icon32_edge_enclose.dat b/release/datafiles/blender_icons32/icon32_edge_enclose.dat new file mode 100644 index 00000000000..420e3d9806f Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_edge_enclose.dat differ diff --git a/release/datafiles/blender_icons32/icon32_edge_touch.dat b/release/datafiles/blender_icons32/icon32_edge_touch.dat new file mode 100644 index 00000000000..3c1bbafd7a5 Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_edge_touch.dat differ diff --git a/release/datafiles/blender_icons32/icon32_face_default.dat b/release/datafiles/blender_icons32/icon32_face_default.dat new file mode 100644 index 00000000000..a47509e8787 Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_face_default.dat differ diff --git a/release/datafiles/blender_icons32/icon32_face_enclose.dat b/release/datafiles/blender_icons32/icon32_face_enclose.dat new file mode 100644 index 00000000000..5e49bd8e9bb Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_face_enclose.dat differ diff --git a/release/datafiles/blender_icons32/icon32_face_touch.dat b/release/datafiles/blender_icons32/icon32_face_touch.dat new file mode 100644 index 00000000000..b5f57e73a8f Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_face_touch.dat differ diff --git a/release/datafiles/blender_icons32/icon32_object_enclose.dat b/release/datafiles/blender_icons32/icon32_object_enclose.dat new file mode 100644 index 00000000000..280b4ffb1f2 Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_object_enclose.dat differ diff --git a/release/datafiles/blender_icons32/icon32_object_touch.dat b/release/datafiles/blender_icons32/icon32_object_touch.dat new file mode 100644 index 00000000000..02be7426e6d Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_object_touch.dat differ diff --git a/release/datafiles/blender_icons32/icon32_select_through.dat b/release/datafiles/blender_icons32/icon32_select_through.dat new file mode 100644 index 00000000000..b764b178840 Binary files /dev/null and b/release/datafiles/blender_icons32/icon32_select_through.dat differ diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index ebcdab98815..03eddfd4490 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -196,6 +196,8 @@ const UserDef U_default = { .render_display_type = USER_RENDER_DISPLAY_WINDOW, .filebrowser_display_type = USER_TEMP_SPACE_DISPLAY_WINDOW, .viewport_aa = 8, + .selection_radius = 75.0f, + .header_highlight = 5, .walk_navigation = { diff --git a/release/windows/icons/cursors/blank.cur b/release/windows/icons/cursors/blank.cur new file mode 100644 index 00000000000..704dfd369aa Binary files /dev/null and b/release/windows/icons/cursors/blank.cur differ diff --git a/release/windows/icons/cursors/box.cur b/release/windows/icons/cursors/box.cur new file mode 100644 index 00000000000..c74fccde721 Binary files /dev/null and b/release/windows/icons/cursors/box.cur differ diff --git a/release/windows/icons/cursors/boxdot.cur b/release/windows/icons/cursors/boxdot.cur new file mode 100644 index 00000000000..8b2562b7775 Binary files /dev/null and b/release/windows/icons/cursors/boxdot.cur differ diff --git a/release/windows/icons/cursors/boxpointer.cur b/release/windows/icons/cursors/boxpointer.cur new file mode 100644 index 00000000000..e9fcb933312 Binary files /dev/null and b/release/windows/icons/cursors/boxpointer.cur differ diff --git a/release/windows/icons/cursors/crossd.cur b/release/windows/icons/cursors/crossd.cur new file mode 100644 index 00000000000..1b623879cd7 Binary files /dev/null and b/release/windows/icons/cursors/crossd.cur differ diff --git a/release/windows/icons/cursors/pointera.cur b/release/windows/icons/cursors/pointera.cur new file mode 100644 index 00000000000..7127a53365f Binary files /dev/null and b/release/windows/icons/cursors/pointera.cur differ diff --git a/release/windows/icons/winblender.rc b/release/windows/icons/winblender.rc index a0dff81b711..2f5bcf9ad0a 100644 --- a/release/windows/icons/winblender.rc +++ b/release/windows/icons/winblender.rc @@ -12,7 +12,6 @@ APPICON ICON DISCARDABLE "winblender.ico" BLENDERFILE ICON DISCARDABLE "winblenderfile.ico" -pointer_cursor CURSOR "cursors/pointer.cur" moveew_cursor CURSOR "cursors/moveew.cur" movens_cursor CURSOR "cursors/movens.cur" arrowdown_cursor CURSOR "cursors/arrowdown.cur" @@ -23,6 +22,12 @@ cross_cursor CURSOR "cursors/cross.cur" crossA_cursor CURSOR "cursors/crossa.cur" crossB_cursor CURSOR "cursors/crossb.cur" crossC_cursor CURSOR "cursors/crossc.cur" +crossD_cursor CURSOR "cursors/crossd.cur" +blank_cursor CURSOR "cursors/blank.cur" +box_cursor CURSOR "cursors/box.cur" +boxdot_cursor CURSOR "cursors/boxdot.cur" +boxpointer_cursor CURSOR "cursors/boxpointer.cur" +pointer_cursor CURSOR "cursors/pointera.cur" eraser_cursor CURSOR "cursors/eraser.cur" eyedropper_cursor CURSOR "cursors/eyedropper.cur" handopen_cursor CURSOR "cursors/handopen.cur" diff --git a/scripts/modules/rna_keymap_ui.py b/scripts/modules/rna_keymap_ui.py index 46a7f01e23c..a1154dfb331 100644 --- a/scripts/modules/rna_keymap_ui.py +++ b/scripts/modules/rna_keymap_ui.py @@ -182,7 +182,24 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level): if map_type in {'KEYBOARD', 'MOUSE'} and kmi.value == 'CLICK_DRAG': subrow = sub.row() - subrow.prop(kmi, "direction") + if bpy.context.preferences.inputs.keymap_direction == 'LEFT_RIGHT': + if kmi.direction == 'ANY' or kmi.direction == 'EAST' or kmi.direction == 'WEST': + subrow.label(text="Direction:") + else: + subrow.label(text="WARNING - No Direction:") + subrow.operator("preferences.set_direction_any", text="Any", depress= kmi.direction == 'ANY').item_id = kmi.id + subrow.operator("preferences.set_direction_left", text="Left", depress= kmi.direction == 'WEST').item_id = kmi.id + subrow.operator("preferences.set_direction_right", text="Right", depress= kmi.direction == 'EAST').item_id = kmi.id + elif bpy.context.preferences.inputs.keymap_direction == 'UP_DOWN': + if kmi.direction == 'ANY' or kmi.direction == 'NORTH' or kmi.direction == 'SOUTH': + subrow.label(text="Direction:") + else: + subrow.label(text="WARNING - No Direction:") + subrow.operator("preferences.set_direction_any", text="Any", depress= kmi.direction == 'ANY').item_id = kmi.id + subrow.operator("preferences.set_direction_up", text="Up", depress= kmi.direction == 'NORTH').item_id = kmi.id + subrow.operator("preferences.set_direction_down", text="Down", depress= kmi.direction == 'SOUTH').item_id = kmi.id + else: + subrow.prop(kmi, "direction") subrow = sub.row() subrow.scale_x = 0.75 diff --git a/scripts/startup/bl_operators/userpref.py b/scripts/startup/bl_operators/userpref.py index 059fae0b440..fd9ba0a2cbc 100644 --- a/scripts/startup/bl_operators/userpref.py +++ b/scripts/startup/bl_operators/userpref.py @@ -405,6 +405,336 @@ class PREFERENCES_OT_keyitem_remove(Operator): return {'FINISHED'} +class PREFERENCES_OT_quick_assign(Operator): + """Assign values to the new drag select settings""" + bl_idname = "preferences.quick_assign" + bl_label = "Quick Assign" + + def execute(self, context): + inputs = context.preferences.inputs + + if inputs.quick_assign_mode == 'BETTER': + inputs.drag_control_mode = 'USERPREF' + inputs.userpref_mode = 'EASY' + inputs.object_select_box = 'OBJECT_TOUCH' + inputs.face_select_box = 'FACE_TOUCH' + inputs.edge_select_box = 'EDGE_TOUCH' + inputs.backface_box = 'NEAR' + inputs.auto_xray_box = 'AUTO_XRAY_DISABLE' + inputs.select_through_box = 'SELECT_THROUGH_OBJECT' + inputs.object_header = True + inputs.face_header = True + inputs.edge_header = True + inputs.backface_header = True + inputs.auto_xray_header = False + inputs.select_through_header = True + inputs.wireless_touch_object = True + inputs.object_cycle_touch = True + inputs.object_cycle_enclose = True + inputs.object_cycle_origin = False + inputs.face_cycle_default = False + inputs.face_cycle_touch = True + inputs.face_cycle_enclose = True + inputs.face_cycle_center = False + inputs.edge_cycle_default = False + inputs.edge_cycle_touch = True + inputs.edge_cycle_enclose = True + inputs.backface_toggle = 'NEAR' + inputs.select_through_toggle = 'CURRENT' + inputs.object_pie = 'SHOW' + inputs.face_pie = 'SHOW' + inputs.edge_pie = 'SHOW' + inputs.backface_pie = 'SHOW' + inputs.auto_xray_pie = 'HIDE' + inputs.select_through_pie = 'SHOW' + inputs.box_pie = 'CONTEXT' + inputs.circle_pie = 'CONTEXT' + inputs.lasso_pie = 'CONTEXT' + elif inputs.quick_assign_mode == 'AUTO_XRAY': + inputs.drag_control_mode = 'USERPREF' + inputs.userpref_mode = 'EASY' + inputs.object_select_box = 'OBJECT_TOUCH' + inputs.face_select_box = 'FACE_TOUCH' + inputs.edge_select_box = 'EDGE_TOUCH' + inputs.backface_box = 'DEFAULT' + inputs.auto_xray_box = 'AUTO_XRAY_BOTH' + inputs.select_through_box = 'SELECT_THROUGH_OBJECT' + inputs.object_header = True + inputs.face_header = True + inputs.edge_header = True + inputs.backface_header = True + inputs.auto_xray_header = True + inputs.select_through_header = False + inputs.object_cycle_touch = True + inputs.object_cycle_enclose = True + inputs.object_cycle_origin = False + inputs.face_cycle_default = False + inputs.face_cycle_touch = True + inputs.face_cycle_enclose = True + inputs.face_cycle_center = False + inputs.edge_cycle_default = False + inputs.edge_cycle_touch = True + inputs.edge_cycle_enclose = True + inputs.auto_xray_toggle = 'BOTH' + inputs.backface_toggle = 'NONE' + inputs.object_pie = 'SHOW' + inputs.face_pie = 'SHOW' + inputs.edge_pie = 'SHOW' + inputs.backface_pie = 'SHOW' + inputs.auto_xray_pie = 'SHOW' + inputs.select_through_pie = 'HIDE' + inputs.box_pie = 'SHOW' + inputs.circle_pie = 'SHOW' + inputs.lasso_pie = 'SHOW' + elif inputs.quick_assign_mode == 'DIRECTIONAL': + inputs.drag_control_mode = 'USERPREF' + inputs.userpref_mode = 'SPLIT' + inputs.drag_direction_box = 'LEFT_RIGHT' + inputs.drag_direction_lasso = 'ANY' + inputs.drag_direction_mode = 'SELECTIVE' + inputs.direction_object = True + inputs.direction_face = True + inputs.direction_edge = True + inputs.direction_backface = False + inputs.direction_auto_xray = False + inputs.direction_select_through = False + inputs.object_select_box = 'OBJECT_TOUCH' + inputs.object_select_downright_box = 'OBJECT_ENCLOSE' + inputs.face_select_box = 'FACE_TOUCH' + inputs.face_select_downright_box = 'FACE_ENCLOSE' + inputs.edge_select_box = 'EDGE_TOUCH' + inputs.edge_select_downright_box = 'EDGE_ENCLOSE' + inputs.backface_box = 'DEFAULT' + inputs.auto_xray_box = 'AUTO_XRAY_DISABLE' + inputs.select_through_box = 'SELECT_THROUGH_OBJECT' + inputs.object_select_circle = 'OBJECT_TOUCH' + inputs.face_select_circle = 'FACE_TOUCH' + inputs.edge_select_circle = 'EDGE_TOUCH' + inputs.backface_circle = 'DEFAULT' + inputs.auto_xray_circle = 'AUTO_XRAY_DISABLE' + inputs.select_through_circle = 'SELECT_THROUGH_OBJECT' + inputs.object_select_lasso = 'OBJECT_TOUCH' + inputs.face_select_lasso = 'FACE_TOUCH' + inputs.edge_select_lasso = 'EDGE_TOUCH' + inputs.backface_lasso = 'DEFAULT' + inputs.auto_xray_lasso = 'AUTO_XRAY_DISABLE' + inputs.select_through_lasso = 'SELECT_THROUGH_OBJECT' + inputs.object_header = False + inputs.face_header = False + inputs.edge_header = False + inputs.backface_header = False + inputs.auto_xray_header = False + inputs.select_through_header = False + inputs.object_pie = 'HIDE' + inputs.face_pie = 'HIDE' + inputs.edge_pie = 'HIDE' + else: + inputs.drag_control_mode = 'USERPREF' + inputs.userpref_mode = 'SPLIT' + inputs.drag_direction_box = 'ANY' + inputs.drag_direction_lasso = 'ANY' + inputs.object_select_box = 'OBJECT_TOUCH' + inputs.face_select_box = 'FACE_DEFAULT' + inputs.edge_select_box = 'EDGE_DEFAULT' + inputs.backface_box = 'DEFAULT' + inputs.auto_xray_box = 'AUTO_XRAY_DISABLE' + inputs.select_through_box = 'SELECT_THROUGH_OBJECT' + inputs.object_select_circle = 'OBJECT_ORIGIN' + inputs.face_select_circle = 'FACE_DEFAULT' + inputs.edge_select_circle = 'EDGE_TOUCH' + inputs.backface_circle = 'DEFAULT' + inputs.auto_xray_circle = 'AUTO_XRAY_DISABLE' + inputs.select_through_circle = 'SELECT_THROUGH_OBJECT' + inputs.object_select_lasso = 'OBJECT_ORIGIN' + inputs.face_select_lasso = 'FACE_DEFAULT' + inputs.edge_select_lasso = 'EDGE_DEFAULT' + inputs.backface_lasso = 'DEFAULT' + inputs.auto_xray_lasso = 'AUTO_XRAY_DISABLE' + inputs.select_through_lasso = 'SELECT_THROUGH_OBJECT' + inputs.sync_box = False + inputs.sync_circle = False + inputs.sync_lasso = False + inputs.object_header = False + inputs.face_header = False + inputs.edge_header = False + inputs.backface_header = False + inputs.auto_xray_header = False + inputs.select_through_header = False + inputs.object_cycle_touch = True + inputs.object_cycle_enclose = True + inputs.object_cycle_origin = True + inputs.object_cycle_touch_circle = True + inputs.object_cycle_enclose_circle = True + inputs.object_cycle_origin_circle = True + inputs.object_cycle_touch_lasso = True + inputs.object_cycle_enclose_lasso = True + inputs.object_cycle_origin_lasso = True + inputs.face_cycle_default = True + inputs.face_cycle_touch = True + inputs.face_cycle_enclose = True + inputs.face_cycle_center = True + inputs.face_cycle_default_circle = True + inputs.face_cycle_touch_circle = True + inputs.face_cycle_enclose_circle = True + inputs.face_cycle_center_circle = True + inputs.face_cycle_default_lasso = True + inputs.face_cycle_touch_lasso = True + inputs.face_cycle_enclose_lasso = True + inputs.face_cycle_center_lasso = True + inputs.edge_cycle_default = True + inputs.edge_cycle_touch = True + inputs.edge_cycle_enclose = True + inputs.edge_cycle_touch_circle = True + inputs.edge_cycle_enclose_circle = True + inputs.edge_cycle_default_lasso = True + inputs.edge_cycle_touch_lasso = True + inputs.edge_cycle_enclose_lasso = True + inputs.backface_toggle = 'NONE' + inputs.backface_toggle_circle = 'NONE' + inputs.backface_toggle_lasso = 'NONE' + inputs.select_through_toggle = 'BOTH' + inputs.select_through_toggle_circle = 'BOTH' + inputs.select_through_toggle_lasso = 'BOTH' + inputs.auto_xray_toggle = 'BOTH' + inputs.auto_xray_toggle_circle = 'BOTH' + inputs.auto_xray_toggle_lasso = 'BOTH' + inputs.object_pie = 'SHOW' + inputs.face_pie = 'SHOW' + inputs.edge_pie = 'SHOW' + inputs.backface_pie = 'SHOW' + inputs.auto_xray_pie = 'SHOW' + inputs.select_through_pie = 'SHOW' + inputs.box_pie = 'SHOW' + inputs.circle_pie = 'SHOW' + inputs.lasso_pie = 'SHOW' + inputs.wireless_touch_object = False + inputs.square_select = False + return {'FINISHED'} + + +class PREFERENCES_OT_reset_cursors(Operator): + """Reset mouse cursors""" + bl_idname = "preferences.reset_cursors" + bl_label = "Reset Cursors" + + def execute(self, context): + inputs = context.preferences.inputs + inputs.system_cursor = 'SYSTEM' + inputs.edit_cursor = 'CROSS' + inputs.paint_cursor = 'CROSS_DOT' + inputs.dot_cursor = 'DOT' + inputs.knife_cursor = 'KNIFE' + inputs.eyedropper_cursor = 'EYEDROPPER' + inputs.eraser_cursor = 'ERASER' + inputs.pencil_cursor = 'PENCIL' + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_any(Operator): + """Drag in any direction""" + bl_idname = "preferences.set_direction_any" + bl_label = "Set Direction Any" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to set direction for", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_any(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_left(Operator): + """Drag to the left""" + bl_idname = "preferences.set_direction_left" + bl_label = "Set Direction Left" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to set direction for", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_left(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_right(Operator): + """Drag to the right""" + bl_idname = "preferences.set_direction_right" + bl_label = "Set Direction Right" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to set direction for", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_right(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_up(Operator): + """Drag upwards""" + bl_idname = "preferences.set_direction_up" + bl_label = "Set Direction Up" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to set direction for", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_up(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_down(Operator): + """Drag downwards""" + bl_idname = "preferences.set_direction_down" + bl_label = "Set Direction Down" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to set direction for", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_down(kmi) + return {'FINISHED'} + + class PREFERENCES_OT_keyconfig_remove(Operator): """Remove key config""" bl_idname = "preferences.keyconfig_remove" @@ -1226,6 +1556,13 @@ classes = ( PREFERENCES_OT_keyconfig_test, PREFERENCES_OT_keyitem_add, PREFERENCES_OT_keyitem_remove, + PREFERENCES_OT_quick_assign, + PREFERENCES_OT_reset_cursors, + PREFERENCES_OT_set_direction_any, + PREFERENCES_OT_set_direction_left, + PREFERENCES_OT_set_direction_right, + PREFERENCES_OT_set_direction_up, + PREFERENCES_OT_set_direction_down, PREFERENCES_OT_keyitem_restore, PREFERENCES_OT_keymap_restore, PREFERENCES_OT_theme_install, diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index 42d5fa6c726..c14bfe93101 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -2312,6 +2312,13 @@ class WM_OT_tool_set_by_id(Operator): space_type = context.space_data.type fn = activate_by_id_or_cycle if self.cycle else activate_by_id + + for area in context.window.screen.areas: + if area.type == 'VIEW_3D': + for region in area.regions: + if region.type == 'HEADER': + region.tag_redraw() + if fn(context, space_type, self.name, as_fallback=self.as_fallback): if self.as_fallback: tool_settings = context.tool_settings diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 94b4bb5fbb1..f60ba57a87a 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -426,6 +426,9 @@ class _defs_view3d_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + if _context.mode == 'OBJECT': + inputs = _context.preferences.inputs + layout.prop(inputs, "wireless_touch_object") return dict( idname="builtin.select_box", label="Select Box", @@ -442,6 +445,9 @@ class _defs_view3d_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + if _context.mode == 'OBJECT': + inputs = _context.preferences.inputs + layout.prop(inputs, "wireless_touch_object") return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -459,6 +465,10 @@ class _defs_view3d_select: row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) layout.prop(props, "radius") + inputs = _context.preferences.inputs + if _context.mode == 'OBJECT': + layout.prop(inputs, "wireless_touch_object") + layout.prop(inputs, "square_select") def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d @@ -2274,6 +2284,8 @@ class _defs_gpencil_edit: layout.prop(props, "radius") if _defs_gpencil_edit.is_segment(context): layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + inputs = context.preferences.inputs + layout.prop(inputs, "square_select") def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index dfa42725cc6..0ebf92e346a 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -189,6 +189,587 @@ class CenterAlignMixIn: row.label() # Needed so col above is centered. +# ----------------------------------------------------------------------------- +# Custom Panels + +class CustomPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "custom" + + +class USERPREF_PT_custom_drag_select(CustomPanel, CenterAlignMixIn, Panel): + bl_label = "Drag Select" + + def draw_centered(self, context, layout): + inputs = context.preferences.inputs + + row = layout.row(align=True) + row.prop(inputs, "keymap_direction", expand=True) + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "drag_control_mode", expand=True) + + if inputs.drag_control_mode == 'USERPREF': + tool_settings = context.tool_settings + row = layout.row(align=True) + row.prop(inputs, "userpref_mode", expand=True) + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "quick_assign", text="Quick Assign", emboss=False) + if inputs.quick_assign: + sub = row.row(align=True) + sub.prop(inputs, "quick_assign", icon='DOWNARROW_HLT', emboss=False) + sub = layout.row(align=True) + sub.prop(inputs, "quick_assign_mode") + sub.operator("preferences.quick_assign") + else: + row.prop(inputs, "quick_assign", icon='RIGHTARROW', emboss=False) + + if inputs.userpref_mode == 'SPLIT': + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "direction_controls", text="Drag Direction", emboss=False) + + if inputs.direction_controls: + row.prop(inputs, "direction_controls", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "drag_direction_box", expand=True) + row = layout.row(align=True) + row.prop(inputs, "drag_direction_lasso", expand=True) + row = layout.row(align=True) + sub = row.row(align=True) + sub.enabled = inputs.drag_direction_box !='ANY' or inputs.drag_direction_lasso !='ANY' + sub.prop(inputs, "drag_direction_mode", expand=True) + row = layout.row(align=True) + sub = row.row(align=True) + sub.enabled = inputs.drag_direction_mode == 'SELECTIVE' and (inputs.drag_direction_box !='ANY' or inputs.drag_direction_lasso !='ANY') + sub.prop(inputs, "direction_object", toggle=True) + sub.prop(inputs, "direction_backface", toggle=True) + row = layout.row(align=True) + sub = row.row(align=True) + sub.enabled = inputs.drag_direction_mode == 'SELECTIVE' and (inputs.drag_direction_box !='ANY' or inputs.drag_direction_lasso !='ANY') + sub.prop(inputs, "direction_face", toggle=True) + sub.prop(inputs, "direction_auto_xray", toggle=True) + row = layout.row(align=True) + sub = row.row(align=True) + sub.enabled = inputs.drag_direction_mode == 'SELECTIVE' and (inputs.drag_direction_box !='ANY' or inputs.drag_direction_lasso !='ANY') + sub.prop(inputs, "direction_edge", toggle=True) + sub.prop(inputs, "direction_select_through", toggle=True) + else: + row.prop(inputs, "direction_controls", icon='RIGHTARROW', emboss=False) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "box_controls", text="Box", emboss=False) + + if inputs.box_controls: + row.prop(inputs, "box_controls", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "box_controls", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + + if inputs.drag_direction_box == 'LEFT_RIGHT': + row.prop(inputs, "blank_text", text="Left", emboss=False) + row.prop(inputs, "blank_text", text="Right", emboss=False) + elif inputs.drag_direction_box == 'UP_DOWN': + row.prop(inputs, "blank_text", text="Up", emboss=False) + row.prop(inputs, "blank_text", text="Down", emboss=False) + row = layout.row(align=True) + row.prop(inputs, "object_select_box") + if inputs.drag_direction_box != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_object): + row.prop(inputs, "object_select_downright_box", text="") + row = layout.row(align=True) + row.prop(inputs, "face_select_box") + if inputs.drag_direction_box != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_face): + row.prop(inputs, "face_select_downright_box", text="") + row = layout.row(align=True) + row.prop(inputs, "edge_select_box") + if inputs.drag_direction_box != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_edge): + row.prop(inputs, "edge_select_downright_box", text="") + row = layout.row(align=True) + row.prop(inputs, "backface_box") + if inputs.drag_direction_box != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_backface): + row.prop(inputs, "backface_downright_box", text="") + row = layout.row(align=True) + row.prop(inputs, "auto_xray_box") + if inputs.drag_direction_box != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray): + row.prop(inputs, "auto_xray_downright_box", text="") + row = layout.row(align=True) + row.prop(inputs, "select_through_box") + if inputs.drag_direction_box != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through): + row.prop(inputs, "select_through_downright_box", text="") + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "circle_controls", text="Circle", emboss=False) + + if inputs.circle_controls: + row.prop(inputs, "circle_controls", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "circle_controls", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "object_select_circle") + row = layout.row(align=True) + row.prop(inputs, "face_select_circle") + row = layout.row(align=True) + row.prop(inputs, "edge_select_circle") + row = layout.row(align=True) + row.prop(inputs, "backface_circle") + row = layout.row(align=True) + row.prop(inputs, "auto_xray_circle") + row = layout.row(align=True) + row.prop(inputs, "select_through_circle") + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "lasso_controls", text="Lasso", emboss=False) + if inputs.lasso_controls: + row.prop(inputs, "lasso_controls", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "lasso_controls", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + + if inputs.drag_direction_lasso == 'LEFT_RIGHT': + row.prop(inputs, "blank_text", text="Left", emboss=False) + row.prop(inputs, "blank_text", text="Right", emboss=False) + elif inputs.drag_direction_lasso == 'UP_DOWN': + row.prop(inputs, "blank_text", text="Up", emboss=False) + row.prop(inputs, "blank_text", text="Down", emboss=False) + row = layout.row(align=True) + row.prop(inputs, "object_select_lasso") + if inputs.drag_direction_lasso != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_object): + row.prop(inputs, "object_select_downright_lasso", text="") + row = layout.row(align=True) + row.prop(inputs, "face_select_lasso") + if inputs.drag_direction_lasso != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_face): + row.prop(inputs, "face_select_downright_lasso", text="") + row = layout.row(align=True) + row.prop(inputs, "edge_select_lasso") + if inputs.drag_direction_lasso != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_edge): + row.prop(inputs, "edge_select_downright_lasso", text="") + row = layout.row(align=True) + row.prop(inputs, "backface_lasso") + if inputs.drag_direction_lasso != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_backface): + row.prop(inputs, "backface_downright_lasso", text="") + row = layout.row(align=True) + row.prop(inputs, "auto_xray_lasso") + if inputs.drag_direction_lasso != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray): + row.prop(inputs, "auto_xray_downright_lasso", text="") + row = layout.row(align=True) + row.prop(inputs, "select_through_lasso") + if inputs.drag_direction_lasso != 'ANY' and (inputs.drag_direction_mode == 'EVERYTHING' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through): + row.prop(inputs, "select_through_downright_lasso", text="") + else: + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "object_select_box") + row = layout.row(align=True) + row.prop(inputs, "face_select_box") + row = layout.row(align=True) + row.prop(inputs, "edge_select_box") + row = layout.row(align=True) + row.prop(inputs, "backface_box") + row = layout.row(align=True) + row.prop(inputs, "auto_xray_box") + row = layout.row(align=True) + row.prop(inputs, "select_through_box") + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "operator_controls", text="Operator Controls", emboss=False) + if inputs.operator_controls: + row.prop(inputs, "operator_controls", icon='DOWNARROW_HLT', emboss=False) + if inputs.userpref_mode == 'SPLIT': + row = layout.row(align=True, heading="Synchronize") + row.prop(inputs, "sync_box", toggle=True) + row.prop(inputs, "sync_circle", toggle=True) + row.prop(inputs, "sync_lasso", toggle=True) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "object_operator", text="Object Operator", emboss=False) + if inputs.object_operator: + row.prop(inputs, "object_operator", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "object_operator", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "object_header", toggle=True) + box_op = inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_object) + lasso_op = inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_object) + box_circle_lasso = inputs.sync_box and box_op and inputs.sync_circle and inputs.sync_lasso and lasso_op + box_circle = inputs.sync_box and box_op and inputs.sync_circle + box_lasso = inputs.sync_box and box_op and inputs.sync_lasso and lasso_op + circle_lasso = inputs.sync_circle and inputs.sync_lasso and lasso_op + + if inputs.userpref_mode == 'EASY' or box_circle_lasso: + row = layout.row(align=True, heading="Cycle") + elif box_circle: + row = layout.row(align=True, heading="Cycle Box & Circle") + elif box_lasso: + row = layout.row(align=True, heading="Cycle Box & Lasso") + else: + row = layout.row(align=True, heading="Cycle Box") + row.enabled = box_op + row.prop(inputs, "object_cycle_touch", toggle=True) + row.prop(inputs, "object_cycle_enclose", toggle=True) + row.prop(inputs, "object_cycle_origin", toggle=True) + + if inputs.userpref_mode == 'SPLIT' and not box_circle_lasso: + if not box_circle and not circle_lasso: + row = layout.row(align=True, heading="Cycle Circle") + row.prop(inputs, "object_cycle_touch_circle", toggle=True) + row.prop(inputs, "object_cycle_enclose_circle", toggle=True) + row.prop(inputs, "object_cycle_origin_circle", toggle=True) + + if not box_lasso: + if circle_lasso: + row = layout.row(align=True, heading="Cycle Lasso & Circle") + else: + row = layout.row(align=True, heading="Cycle Lasso") + row.enabled = lasso_op + row.prop(inputs, "object_cycle_touch_lasso", toggle=True) + row.prop(inputs, "object_cycle_enclose_lasso", toggle=True) + row.prop(inputs, "object_cycle_origin_lasso", toggle=True) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "face_operator", text="Face Operator", emboss=False) + if inputs.face_operator: + row.prop(inputs, "face_operator", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "face_operator", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "face_header", toggle=True) + box_op = inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_face) + lasso_op = inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_face) + box_circle_lasso = inputs.sync_box and box_op and inputs.sync_circle and inputs.sync_lasso and lasso_op + box_circle = inputs.sync_box and box_op and inputs.sync_circle + box_lasso = inputs.sync_box and box_op and inputs.sync_lasso and lasso_op + circle_lasso = inputs.sync_circle and inputs.sync_lasso and lasso_op + + if inputs.userpref_mode == 'EASY' or box_circle_lasso: + row = layout.row(align=True, heading="Cycle") + elif box_circle: + row = layout.row(align=True, heading="Cycle Box & Circle") + elif box_lasso: + row = layout.row(align=True, heading="Cycle Box & Lasso") + else: + row = layout.row(align=True, heading="Cycle Box") + row.enabled = box_op + row.prop(inputs, "face_cycle_default", toggle=True) + row.prop(inputs, "face_cycle_touch", toggle=True) + row.prop(inputs, "face_cycle_enclose", toggle=True) + row.prop(inputs, "face_cycle_center", toggle=True) + + if inputs.userpref_mode == 'SPLIT' and not box_circle_lasso: + if not box_circle and not circle_lasso: + row = layout.row(align=True, heading="Cycle Circle") + row.prop(inputs, "face_cycle_default_circle", toggle=True) + row.prop(inputs, "face_cycle_touch_circle", toggle=True) + row.prop(inputs, "face_cycle_enclose_circle", toggle=True) + row.prop(inputs, "face_cycle_center_circle", toggle=True) + + if not box_lasso: + if circle_lasso: + row = layout.row(align=True, heading="Cycle Lasso & Circle") + else: + row = layout.row(align=True, heading="Cycle Lasso") + row.enabled = lasso_op + row.prop(inputs, "face_cycle_default_lasso", toggle=True) + row.prop(inputs, "face_cycle_touch_lasso", toggle=True) + row.prop(inputs, "face_cycle_enclose_lasso", toggle=True) + row.prop(inputs, "face_cycle_center_lasso", toggle=True) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "edge_operator", text="Edge Operator", emboss=False) + if inputs.edge_operator: + row.prop(inputs, "edge_operator", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "edge_operator", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "edge_header", toggle=True) + box_op = inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_edge) + lasso_op = inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_edge) + box_circle_lasso = inputs.sync_box and box_op and inputs.sync_circle and inputs.sync_lasso and lasso_op + box_circle = inputs.sync_box and box_op and inputs.sync_circle + box_lasso = inputs.sync_box and box_op and inputs.sync_lasso and lasso_op + circle_lasso = inputs.sync_circle and inputs.sync_lasso and lasso_op + + if inputs.userpref_mode == 'EASY' or box_circle_lasso: + row = layout.row(align=True, heading="Cycle") + elif box_circle: + row = layout.row(align=True, heading="Cycle Box & Circle") + elif box_lasso: + row = layout.row(align=True, heading="Cycle Box & Lasso") + else: + row = layout.row(align=True, heading="Cycle Box") + row.enabled = box_op + row.prop(inputs, "edge_cycle_default", toggle=True) + row.prop(inputs, "edge_cycle_touch", toggle=True) + row.prop(inputs, "edge_cycle_enclose", toggle=True) + + if inputs.userpref_mode == 'SPLIT' and not box_circle_lasso: + if not box_circle and not circle_lasso: + row = layout.row(align=True, heading="Cycle Circle") + row.prop(inputs, "edge_cycle_touch_circle", toggle=True) + row.prop(inputs, "edge_cycle_enclose_circle", toggle=True) + + if not box_lasso: + if circle_lasso: + row = layout.row(align=True, heading="Cycle Lasso & Circle") + else: + row = layout.row(align=True, heading="Cycle Lasso") + row.enabled = lasso_op + row.prop(inputs, "edge_cycle_default_lasso", toggle=True) + row.prop(inputs, "edge_cycle_touch_lasso", toggle=True) + row.prop(inputs, "edge_cycle_enclose_lasso", toggle=True) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "backface_operator", text="Backface Operator", emboss=False) + if inputs.backface_operator: + row.prop(inputs, "backface_operator", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "backface_operator", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "backface_header", toggle=True) + row = layout.row(align=True) + box_op = inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_backface) + lasso_op = inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_backface) + box_circle_lasso = inputs.sync_box and box_op and inputs.sync_circle and inputs.sync_lasso and lasso_op + box_circle = inputs.sync_box and box_op and inputs.sync_circle + box_lasso = inputs.sync_box and box_op and inputs.sync_lasso and lasso_op + circle_lasso = inputs.sync_circle and inputs.sync_lasso and lasso_op + + row = layout.row(align=True) + if inputs.userpref_mode == 'EASY' or box_circle_lasso: + proptext="Toggle" + elif box_circle: + proptext="Toggle Box & Circle" + elif box_lasso: + proptext="Toggle Box & Lasso" + else: + proptext="Toggle Box" + row.enabled = box_op + row.prop(inputs, "backface_toggle", text=proptext) + + if inputs.userpref_mode == 'SPLIT' and not box_circle_lasso: + if not box_circle and not circle_lasso: + row = layout.row(align=True) + row.prop(inputs, "backface_toggle_circle", text="Toggle Circle") + + if not box_lasso: + if circle_lasso: + row = layout.row(align=True) + proptext="Toggle Lasso & Circle" + else: + row = layout.row(align=True) + proptext="Toggle Lasso" + row.enabled = lasso_op + row.prop(inputs, "backface_toggle_lasso", text=proptext) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "auto_xray_operator", text="Auto X-Ray Operator", emboss=False) + if inputs.auto_xray_operator: + row.prop(inputs, "auto_xray_operator", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "auto_xray_operator", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "auto_xray_header", toggle=True) + row = layout.row(align=True) + box_op = inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_auto_xray) + lasso_op = inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_auto_xray) + box_circle_lasso = inputs.sync_box and box_op and inputs.sync_circle and inputs.sync_lasso and lasso_op + box_circle = inputs.sync_box and box_op and inputs.sync_circle + box_lasso = inputs.sync_box and box_op and inputs.sync_lasso and lasso_op + circle_lasso = inputs.sync_circle and inputs.sync_lasso and lasso_op + + row = layout.row(align=True) + if inputs.userpref_mode == 'EASY' or box_circle_lasso: + proptext="Toggle" + elif box_circle: + proptext="Toggle Box & Circle" + elif box_lasso: + proptext="Toggle Box & Lasso" + else: + proptext="Toggle Box" + row.enabled = box_op + row.prop(inputs, "auto_xray_toggle", text=proptext) + + if inputs.userpref_mode == 'SPLIT' and not box_circle_lasso: + if not box_circle and not circle_lasso: + row = layout.row(align=True) + row.prop(inputs, "auto_xray_toggle_circle", text="Toggle Circle") + + if not box_lasso: + if circle_lasso: + row = layout.row(align=True) + proptext="Toggle Lasso & Circle" + else: + row = layout.row(align=True) + proptext="Toggle Lasso" + row.enabled = lasso_op + row.prop(inputs, "auto_xray_toggle_lasso", text=proptext) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "select_through_operator", text="Select Through Operator", emboss=False) + if inputs.select_through_operator: + row.prop(inputs, "select_through_operator", icon='RIGHTARROW', emboss=False) + else: + row.prop(inputs, "select_through_operator", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "select_through_header", toggle=True) + row = layout.row(align=True) + box_op = inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_select_through) + lasso_op = inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and not inputs.direction_select_through) + box_circle_lasso = inputs.sync_box and box_op and inputs.sync_circle and inputs.sync_lasso and lasso_op + box_circle = inputs.sync_box and box_op and inputs.sync_circle + box_lasso = inputs.sync_box and box_op and inputs.sync_lasso and lasso_op + circle_lasso = inputs.sync_circle and inputs.sync_lasso and lasso_op + + row = layout.row(align=True) + if inputs.userpref_mode == 'EASY' or box_circle_lasso: + proptext="Toggle" + elif box_circle: + proptext="Toggle Box & Circle" + elif box_lasso: + proptext="Toggle Box & Lasso" + else: + proptext="Toggle Box" + row.enabled = box_op + row.prop(inputs, "select_through_toggle", text=proptext) + + if inputs.userpref_mode == 'SPLIT' and not box_circle_lasso: + if not box_circle and not circle_lasso: + row = layout.row(align=True) + row.prop(inputs, "select_through_toggle_circle", text="Toggle Circle") + + if not box_lasso: + if circle_lasso: + row = layout.row(align=True) + proptext="Toggle Lasso & Circle" + else: + row = layout.row(align=True) + proptext="Toggle Lasso" + row.enabled = lasso_op + row.prop(inputs, "select_through_toggle_lasso", text=proptext) + else: + row.prop(inputs, "operator_controls", icon='RIGHTARROW', emboss=False) + + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "pie_menu_controls", text="Pie Menu Controls", emboss=False) + if inputs.pie_menu_controls: + row.prop(inputs, "pie_menu_controls", icon='DOWNARROW_HLT', emboss=False) + row = layout.row(align=True) + row.prop(inputs, "object_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "face_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "edge_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "backface_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "auto_xray_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "select_through_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "box_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "circle_pie", expand=True) + row = layout.row(align=True) + row.prop(inputs, "lasso_pie", expand=True) + else: + row.prop(inputs, "pie_menu_controls", icon='RIGHTARROW', emboss=False) + + +class USERPREF_PT_custom_mouse(CustomPanel, CenterAlignMixIn, Panel): + bl_label = "Mouse" + + def draw_centered(self, context, layout): + inputs = context.preferences.inputs + + row = layout.row(align=True) + row.prop(inputs, "adjustable_click_select") + if inputs.adjustable_click_select == 'CUSTOM': + row.prop(inputs, "selection_radius") + row = layout.row(align=True) + row.prop(inputs, "select_unbiased", toggle=True) + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "custom_cursor") + if inputs.custom_cursor == 'CUSTOM': + row.operator("preferences.reset_cursors") + row = layout.row(align=True) + row.prop(inputs, "system_cursor") + row = layout.row(align=True) + row.prop(inputs, "edit_cursor") + row = layout.row(align=True) + row.prop(inputs, "paint_cursor") + row = layout.row(align=True) + row.prop(inputs, "dot_cursor") + row = layout.row(align=True) + row.prop(inputs, "knife_cursor") + row = layout.row(align=True) + row.prop(inputs, "pencil_cursor") + row = layout.row(align=True) + row.prop(inputs, "eraser_cursor") + row = layout.row(align=True) + row.prop(inputs, "eyedropper_cursor") + + +class USERPREF_PT_custom_visual(CustomPanel, CenterAlignMixIn, Panel): + bl_label = "Visual" + + def draw_centered(self, context, layout): + inputs = context.preferences.inputs + + row = layout.row(align=True) + row.prop(inputs, "facedot_mode", expand=True) + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "shading_button_mode") + if inputs.shading_button_mode == 'CYCLE': + row.prop(inputs, "shading_cycle_wire", icon='SHADING_WIRE', toggle=True) + row.prop(inputs, "shading_cycle_solid", icon='SHADING_SOLID', toggle=True) + row.prop(inputs, "shading_cycle_material", icon='SHADING_TEXTURE', toggle=True) + row.prop(inputs, "shading_cycle_render", icon='SHADING_RENDERED', toggle=True) + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.enabled = inputs.shading_button_mode != 'XRAY_COMBINE' + row.prop(inputs, "xray_button", expand=True) + row = layout.row(align=True) + row.separator(factor=1.5) + row = layout.row(align=True) + row.prop(inputs, "custom_highlight") + if inputs.custom_highlight == 'CUSTOM': + row.prop(inputs, "header_highlight") + + + + # ----------------------------------------------------------------------------- # Interface Panels @@ -2728,6 +3309,10 @@ classes = ( USERPREF_MT_view, USERPREF_MT_save_load, + USERPREF_PT_custom_drag_select, + USERPREF_PT_custom_mouse, + USERPREF_PT_custom_visual, + USERPREF_PT_interface_display, USERPREF_PT_interface_editors, USERPREF_PT_interface_temporary_windows, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index afb2d50be89..511dd8b0953 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -751,6 +751,7 @@ class VIEW3D_HT_header(Header): layout = self.layout tool_settings = context.tool_settings + inputs = context.preferences.inputs view = context.space_data shading = view.shading @@ -783,8 +784,221 @@ class VIEW3D_HT_header(Header): ) del act_mode_item - layout.template_header_3D_mode() + row = layout.row(align=True) + row.template_header_3D_mode() + if inputs.drag_control_mode == 'USERPREF': + if (object_mode == 'OBJECT' and (inputs.object_header or inputs.auto_xray_header or inputs.select_through_header)) or (object_mode == 'EDIT' and (inputs.face_header or inputs.edge_header or inputs.backface_header or inputs.select_through_header or inputs.auto_xray_header)): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + _cls = ToolSelectPanelHelper._tool_class_from_space_type('VIEW_3D') + + if tool_settings.workspace_tool_type == 'FALLBACK': + tool = _cls._tool_get_by_id_active(context, _cls.tool_fallback_id)[0].idname + else: + tool = ToolSelectPanelHelper.tool_active_from_context(context).idname + + enable_auto_xray_button = inputs.auto_xray_header + depress_auto_xray = False + enable_select_through_button = inputs.select_through_header + depress_select_through = False + enable_object_button = inputs.object_header + objicon = 'OBJECT_TOUCH' + enable_face_button = inputs.face_header + facicon = 'FACE_DEFAULT' + enable_edge_button = inputs.edge_header + edgicon = 'EDGE_DEFAULT' + enable_backface_button = inputs.backface_header + depress_bface = False + + if inputs.userpref_mode == 'SPLIT' and tool == "builtin.select_circle": + if object_mode == 'OBJECT': + if enable_object_button: + if inputs.object_select_circle == 'OBJECT_ENCLOSE': + objicon = 'OBJECT_ENCLOSE' + elif inputs.object_select_circle == 'OBJECT_ORIGIN': + objicon = 'LIGHTPROBE_SPHERE' + + if enable_auto_xray_button: + depress_auto_xray = inputs.auto_xray_circle == 'AUTO_XRAY_OBJECT' or inputs.auto_xray_circle == 'AUTO_XRAY_BOTH' + + if enable_select_through_button: + depress_select_through = inputs.select_through_circle == 'SELECT_THROUGH_OBJECT' or inputs.select_through_circle == 'SELECT_THROUGH_BOTH' + else: + if enable_face_button: + if inputs.face_select_circle == 'FACE_TOUCH': + facicon = 'FACE_TOUCH' + elif inputs.face_select_circle == 'FACE_ENCLOSE': + facicon = 'FACE_ENCLOSE' + elif inputs.face_select_circle == 'FACE_CENTER': + facicon = 'LIGHTPROBE_PLANE' + + if enable_edge_button: + if inputs.edge_select_circle == 'EDGE_ENCLOSE': + edgicon = 'EDGE_ENCLOSE' + else: + edgicon = 'EDGE_TOUCH' + + if enable_auto_xray_button: + depress_auto_xray = inputs.auto_xray_circle == 'AUTO_XRAY_EDIT' or inputs.auto_xray_circle == 'AUTO_XRAY_BOTH' + + if enable_select_through_button: + depress_select_through = inputs.select_through_circle == 'SELECT_THROUGH_EDIT' or inputs.select_through_circle == 'SELECT_THROUGH_BOTH' + + if enable_backface_button: + depress_bface = inputs.backface_circle != 'DEFAULT' + + elif inputs.userpref_mode == 'SPLIT' and tool == "builtin.select_lasso": + if object_mode == 'OBJECT': + if enable_object_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_object == False: + if inputs.object_select_lasso == 'OBJECT_ENCLOSE': + objicon = 'OBJECT_ENCLOSE' + elif inputs.object_select_lasso == 'OBJECT_ORIGIN': + objicon = 'LIGHTPROBE_SPHERE' + else: + enable_object_button = False + + if enable_auto_xray_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_lasso == 'AUTO_XRAY_OBJECT' or inputs.auto_xray_lasso == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_lasso == 'SELECT_THROUGH_OBJECT' or inputs.select_through_lasso == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + else: + if enable_face_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_face == False: + if inputs.face_select_lasso == 'FACE_TOUCH': + facicon = 'FACE_TOUCH' + elif inputs.face_select_lasso == 'FACE_ENCLOSE': + facicon = 'FACE_ENCLOSE' + elif inputs.face_select_lasso == 'FACE_CENTER': + facicon = 'LIGHTPROBE_PLANE' + else: + enable_face_button = False + + if enable_edge_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_edge == False: + if inputs.edge_select_lasso == 'EDGE_TOUCH': + edgicon = 'EDGE_TOUCH' + elif inputs.edge_select_lasso == 'EDGE_ENCLOSE': + edgicon = 'EDGE_ENCLOSE' + else: + enable_edge_button = False + + if enable_backface_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_backface == False: + depress_bface = inputs.backface_lasso != 'DEFAULT' + else: + enable_backface_button = False + + if enable_auto_xray_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_lasso == 'AUTO_XRAY_EDIT' or inputs.auto_xray_lasso == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_lasso == 'SELECT_THROUGH_EDIT' or inputs.select_through_lasso == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + + elif (inputs.userpref_mode == 'SPLIT' and tool == "builtin.select_box") or (inputs.userpref_mode == 'EASY' and (tool == "builtin.select_box" or tool == "builtin.select_lasso" or tool == "builtin.select_circle")): + if object_mode == 'OBJECT': + if enable_object_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_object == False): + if inputs.object_select_box == 'OBJECT_ENCLOSE': + objicon = 'OBJECT_ENCLOSE' + elif inputs.object_select_box == 'OBJECT_ORIGIN': + objicon = 'LIGHTPROBE_SPHERE' + else: + enable_object_button = False + + if enable_auto_xray_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_box == 'AUTO_XRAY_OBJECT' or inputs.auto_xray_box == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_box == 'SELECT_THROUGH_OBJECT' or inputs.select_through_box == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + else: + if enable_face_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_face == False): + if inputs.face_select_box == 'FACE_TOUCH': + facicon = 'FACE_TOUCH' + elif inputs.face_select_box == 'FACE_ENCLOSE': + facicon = 'FACE_ENCLOSE' + elif inputs.face_select_box == 'FACE_CENTER': + facicon = 'LIGHTPROBE_PLANE' + else: + enable_face_button = False + + if enable_edge_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_edge == False): + if inputs.edge_select_box == 'EDGE_TOUCH': + edgicon = 'EDGE_TOUCH' + elif inputs.edge_select_box == 'EDGE_ENCLOSE': + edgicon = 'EDGE_ENCLOSE' + else: + enable_edge_button = False + + if enable_backface_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_backface == False): + depress_bface = inputs.backface_box != 'DEFAULT' + else: + enable_backface_button = False + + if enable_auto_xray_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_box == 'AUTO_XRAY_EDIT' or inputs.auto_xray_box == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_box == 'SELECT_THROUGH_EDIT' or inputs.select_through_box == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + else: + enable_auto_xray_button = False + enable_select_through_button = False + enable_object_button = False + enable_face_button = False + enable_edge_button = False + enable_backface_button = False + + if object_mode == 'OBJECT': + if inputs.object_header: + sub = row.row(align=True) + sub.enabled = enable_object_button + sub.operator("view3d.cycle_object", text="", icon=objicon) + else: + if inputs.face_header: + sub = row.row(align=True) + sub.enabled = enable_face_button + sub.operator("view3d.cycle_face", text="", icon=facicon) + + if inputs.edge_header: + sub = row.row(align=True) + sub.enabled = enable_edge_button + sub.operator("view3d.cycle_edge", text="", icon=edgicon) + + if inputs.backface_header: + sub = row.row(align=True) + sub.enabled = enable_backface_button + sub.operator("view3d.toggle_backface", text="", icon='NORMALS_FACE', depress=depress_bface) + + del tool, enable_object_button, objicon, enable_face_button, facicon, enable_edge_button, edgicon, enable_backface_button, depress_bface + # Contains buttons like Mode, Pivot, Layer, Mesh Select Mode... if obj: # Particle edit @@ -1057,8 +1271,20 @@ class VIEW3D_HT_header(Header): ): sub.popover(panel="VIEW3D_PT_overlay_bones", text="", icon='POSE_HLT') - row = layout.row() - row.active = (object_mode == 'EDIT') or (shading.type in {'WIREFRAME', 'SOLID'}) + row = layout.row(align=True) + + if (object_mode == 'EDIT' or object_mode == 'OBJECT') and inputs.drag_control_mode == 'USERPREF' and (inputs.auto_xray_header or inputs.select_through_header): + if inputs.auto_xray_header: + sub = row.row(align=True) + sub.enabled = enable_auto_xray_button + sub.operator("view3d.toggle_auto_xray", text="", icon='AUTO_XRAY', depress=depress_auto_xray) + + if inputs.select_through_header: + sub = row.row(align=True) + sub.enabled = enable_select_through_button + sub.operator("view3d.toggle_select_through", text="", icon='SELECT_THROUGH', depress=depress_select_through) + + del enable_auto_xray_button, depress_auto_xray, enable_select_through_button, depress_select_through # While exposing `shading.show_xray(_wireframe)` is correct. # this hides the key shortcut from users: #70433. @@ -1068,15 +1294,28 @@ class VIEW3D_HT_header(Header): draw_depressed = shading.show_xray_wireframe else: draw_depressed = shading.show_xray - row.operator( - "view3d.toggle_xray", - text="", - icon='XRAY', - depress=draw_depressed, - ) + if inputs.xray_button != 'HIDE' and inputs.shading_button_mode != 'XRAY_COMBINE': + row.active = (object_mode == 'EDIT') or (shading.type in {'WIREFRAME', 'SOLID'}) + row.operator("view3d.toggle_xray", text="", icon='XRAY', depress=draw_depressed) - row = layout.row(align=True) - row.prop(shading, "type", text="", expand=True) + if not ((inputs.shading_button_mode == 'XRAY_COMBINE' or inputs.xray_button == 'HIDE') and not inputs.select_through_header and not inputs.auto_xray_header): + row = layout.row(align=True) + + if inputs.shading_button_mode != 'DEFAULT': + if shading.type == 'SOLID': + shadicon = 'SHADING_SOLID' + elif shading.type == 'MATERIAL': + shadicon = 'SHADING_TEXTURE' + elif shading.type == 'RENDERED': + shadicon = 'SHADING_RENDERED' + else: + shadicon = 'SHADING_WIRE' + if inputs.shading_button_mode == 'XRAY_COMBINE': + row.operator("view3d.toggle_xray", text="", icon=shadicon, depress=draw_depressed) + else: + row.operator("view3d.cycle_shading", text="", icon=shadicon) + else: + row.prop(shading, "type", text="", expand=True) sub = row.row(align=True) # TODO, currently render shading type ignores mesh two-side, until it's supported # show the shading popover which shows double-sided option. @@ -1233,6 +1472,322 @@ class ShowHideMenu: layout.operator("%s.hide" % self._operator_name, text="Hide Unselected").unselected = True +# Custom Operators +class VIEW3D_box_lasso(bpy.types.Operator): + bl_idname = "view3d.box_lasso" + bl_label = "Box-Lasso" + + def invoke(self, context, event): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + tool_settings = context.tool_settings + inputs = context.preferences.inputs + + if tool_settings.workspace_tool_type == 'FALLBACK': + tool = _cls._tool_get_by_id_active(context, _cls.tool_fallback_id)[0].idname + else: + tool = ToolSelectPanelHelper.tool_active_from_context(context).idname + + if tool.idname == "builtin.select_box": + for i in range(2): + bpy.ops.wm.tool_set_by_id(name="builtin.select_lasso") + else: + for i in range(2): + bpy.ops.wm.tool_set_by_id(name="builtin.select_box") + + return {'FINISHED'} + +class VIEW3D_box_select(bpy.types.Operator): + bl_idname = "view3d.box_select" + bl_label = "Box" + bl_description = "Use Box Select" + + def invoke(self, context, event): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + tool_settings = context.tool_settings + + for i in range(2): + if tool.idname == "builtin.select_circle" or tool.idname == "builtin.select_lasso" or tool.idname == "builtin.select": + bpy.ops.wm.tool_set_by_id(name="builtin.select_box", space_type='VIEW_3D') + elif tool_settings.workspace_tool_type == 'FALLBACK': + bpy.ops.wm.tool_set_by_id(name="builtin.select_box", as_fallback=True, space_type='VIEW_3D') + else: + bpy.ops.wm.tool_set_by_id(name="builtin.select_box", space_type='VIEW_3D') + + return {'FINISHED'} + +class VIEW3D_circle_select(bpy.types.Operator): + bl_idname = "view3d.circle_select" + bl_label = "Circle" + bl_description = "Use Circle Select" + + def invoke(self, context, event): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + tool_settings = context.tool_settings + + for i in range(2): + if tool.idname == "builtin.select_box" or tool.idname == "builtin.select_lasso" or tool.idname == "builtin.select": + bpy.ops.wm.tool_set_by_id(name="builtin.select_circle", space_type='VIEW_3D') + elif tool_settings.workspace_tool_type == 'FALLBACK': + bpy.ops.wm.tool_set_by_id(name="builtin.select_circle", as_fallback=True, space_type='VIEW_3D') + else: + bpy.ops.wm.tool_set_by_id(name="builtin.select_circle", space_type='VIEW_3D') + + return {'FINISHED'} + +class VIEW3D_lasso_select(bpy.types.Operator): + bl_idname = "view3d.lasso_select" + bl_label = "Lasso" + bl_description = "Use Lasso Select" + + def invoke(self, context, event): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + tool_settings = context.tool_settings + + for i in range(2): + if tool.idname == "builtin.select_circle" or tool.idname == "builtin.select_box" or tool.idname == "builtin.select": + bpy.ops.wm.tool_set_by_id(name="builtin.select_lasso") + elif tool_settings.workspace_tool_type == 'FALLBACK': + bpy.ops.wm.tool_set_by_id(name="builtin.select_lasso", as_fallback=True) + else: + bpy.ops.wm.tool_set_by_id(name="builtin.select_lasso") + + return {'FINISHED'} + +class VIEW3D_MT_PIE_Drag_Select(Menu): + bl_label = "Drag Select" + + def draw(self, context): + layout = self.layout + tool_settings = context.tool_settings + inputs = context.preferences.inputs + obj = context.active_object + object_mode = 'OBJECT' if obj is None else obj.mode + + if inputs.drag_control_mode == 'USERPREF' and (object_mode == 'OBJECT' or object_mode == 'EDIT'): + enable_auto_xray_button = inputs.auto_xray_pie == 'SHOW' + depress_auto_xray = False + enable_select_through_button = inputs.select_through_pie == 'SHOW' + depress_select_through = False + enable_backface_button = inputs.backface_pie == 'SHOW' + depress_bface = False + enable_object_button = inputs.object_pie == 'SHOW' + objicon = 'OBJECT_TOUCH' + enable_face_button = inputs.face_pie == 'SHOW' + facicon = 'FACE_DEFAULT' + enable_edge_button = inputs.edge_pie == 'SHOW' + edgicon = 'EDGE_DEFAULT' + + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + _cls = ToolSelectPanelHelper._tool_class_from_space_type('VIEW_3D') + + if tool_settings.workspace_tool_type == 'FALLBACK': + tool = _cls._tool_get_by_id_active(context, _cls.tool_fallback_id)[0].idname + else: + tool = ToolSelectPanelHelper.tool_active_from_context(context).idname + + if inputs.userpref_mode == 'SPLIT' and tool == "builtin.select_circle": + if object_mode == 'OBJECT': + if enable_object_button: + if inputs.object_select_circle == 'OBJECT_ENCLOSE': + objicon = 'OBJECT_ENCLOSE' + elif inputs.object_select_circle == 'OBJECT_ORIGIN': + objicon = 'LIGHTPROBE_SPHERE' + + if enable_auto_xray_button: + depress_auto_xray = inputs.auto_xray_circle == 'AUTO_XRAY_OBJECT' or inputs.auto_xray_circle == 'AUTO_XRAY_BOTH' + + if enable_select_through_button: + depress_select_through = inputs.select_through_circle == 'SELECT_THROUGH_OBJECT' or inputs.select_through_circle == 'SELECT_THROUGH_BOTH' + else: + if enable_face_button: + if inputs.face_select_circle == 'FACE_TOUCH': + facicon = 'FACE_TOUCH' + elif inputs.face_select_circle == 'FACE_ENCLOSE': + facicon = 'FACE_ENCLOSE' + elif inputs.face_select_circle == 'FACE_CENTER': + facicon = 'LIGHTPROBE_PLANE' + + if enable_edge_button: + if inputs.edge_select_circle == 'EDGE_ENCLOSE': + edgicon = 'EDGE_ENCLOSE' + else: + edgicon = 'EDGE_TOUCH' + + if enable_backface_button: + depress_bface = inputs.backface_circle != 'DEFAULT' + + if enable_auto_xray_button: + depress_auto_xray = inputs.auto_xray_circle == 'AUTO_XRAY_EDIT' or inputs.auto_xray_circle == 'AUTO_XRAY_BOTH' + + if enable_select_through_button: + depress_select_through = inputs.select_through_circle == 'SELECT_THROUGH_EDIT' or inputs.select_through_circle == 'SELECT_THROUGH_BOTH' + + elif inputs.userpref_mode == 'SPLIT' and tool == "builtin.select_lasso": + if object_mode == 'OBJECT': + if enable_object_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_object == False: + if inputs.object_select_lasso == 'OBJECT_ENCLOSE': + objicon = 'OBJECT_ENCLOSE' + elif inputs.object_select_lasso == 'OBJECT_ORIGIN': + objicon = 'LIGHTPROBE_SPHERE' + else: + enable_object_button = False + + if enable_auto_xray_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_lasso == 'AUTO_XRAY_OBJECT' or inputs.auto_xray_lasso == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_lasso == 'SELECT_THROUGH_OBJECT' or inputs.select_through_lasso == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + else: + if enable_face_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_face == False: + if inputs.face_select_lasso == 'FACE_TOUCH': + facicon = 'FACE_TOUCH' + elif inputs.face_select_lasso == 'FACE_ENCLOSE': + facicon = 'FACE_ENCLOSE' + elif inputs.face_select_lasso == 'FACE_CENTER': + facicon = 'LIGHTPROBE_PLANE' + else: + enable_face_button = False + + if enable_edge_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_edge == False: + if inputs.edge_select_lasso == 'EDGE_TOUCH': + edgicon = 'EDGE_TOUCH' + elif inputs.edge_select_lasso == 'EDGE_ENCLOSE': + edgicon = 'EDGE_ENCLOSE' + else: + enable_edge_button = False + + if enable_backface_button: + if inputs.drag_direction_lasso == 'ANY' or inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_backface == False: + depress_bface = inputs.backface_lasso != 'DEFAULT' + else: + enable_backface_button = False + + if enable_auto_xray_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_lasso == 'AUTO_XRAY_EDIT' or inputs.auto_xray_lasso == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.drag_direction_lasso == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_lasso == 'SELECT_THROUGH_EDIT' or inputs.select_through_lasso == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + + elif (inputs.userpref_mode == 'SPLIT' and tool == "builtin.select_box") or (inputs.userpref_mode == 'EASY' and (tool == "builtin.select_box" or tool == "builtin.select_lasso" or tool == "builtin.select_circle")): + if object_mode == 'OBJECT': + if enable_object_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_object == False): + if inputs.object_select_box == 'OBJECT_ENCLOSE': + objicon = 'OBJECT_ENCLOSE' + elif inputs.object_select_box == 'OBJECT_ORIGIN': + objicon = 'LIGHTPROBE_SPHERE' + else: + enable_object_button = False + + if enable_auto_xray_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_box == 'AUTO_XRAY_OBJECT' or inputs.auto_xray_box == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_box == 'SELECT_THROUGH_OBJECT' or inputs.select_through_box == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + else: + if enable_face_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_face == False): + if inputs.face_select_box == 'FACE_TOUCH': + facicon = 'FACE_TOUCH' + elif inputs.face_select_box == 'FACE_ENCLOSE': + facicon = 'FACE_ENCLOSE' + elif inputs.face_select_box == 'FACE_CENTER': + facicon = 'LIGHTPROBE_PLANE' + else: + enable_face_button = False + + if enable_edge_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_edge == False): + if inputs.edge_select_box == 'EDGE_TOUCH': + edgicon = 'EDGE_TOUCH' + elif inputs.edge_select_box == 'EDGE_ENCLOSE': + edgicon = 'EDGE_ENCLOSE' + else: + enable_edge_button = False + + if enable_backface_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_backface == False): + depress_bface = inputs.backface_box != 'DEFAULT' + else: + enable_backface_button = False + + if enable_auto_xray_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_auto_xray == False): + depress_auto_xray = inputs.auto_xray_box == 'AUTO_XRAY_EDIT' or inputs.auto_xray_box == 'AUTO_XRAY_BOTH' + else: + enable_auto_xray_button = False + + if enable_select_through_button: + if inputs.userpref_mode == 'EASY' or inputs.drag_direction_box == 'ANY' or (inputs.drag_direction_mode == 'SELECTIVE' and inputs.direction_select_through == False): + depress_select_through = inputs.select_through_box == 'SELECT_THROUGH_EDIT' or inputs.select_through_box == 'SELECT_THROUGH_BOTH' + else: + enable_select_through_button = False + else: + enable_auto_xray_button = False + enable_select_through_button = False + enable_object_button = False + enable_face_button = False + enable_edge_button = False + enable_backface_button = False + + pie = layout.menu_pie() + + if enable_auto_xray_button: + pie.operator("view3d.toggle_auto_xray", icon='AUTO_XRAY', depress=depress_auto_xray) + if enable_select_through_button: + pie.operator("view3d.toggle_select_through", icon='SELECT_THROUGH', depress=depress_select_through) + if inputs.box_pie == 'SHOW' or tool != "builtin.select_box" and inputs.box_pie == 'CONTEXT': + pie.operator("view3d.box_select", icon='SELECT_SET') + if inputs.circle_pie == 'SHOW' or tool != "builtin.select_circle" and inputs.circle_pie == 'CONTEXT': + pie.operator("view3d.circle_select", icon='MESH_CIRCLE') + if inputs.lasso_pie == 'SHOW' or tool != "builtin.select_lasso" and inputs.lasso_pie == 'CONTEXT': + pie.operator("view3d.lasso_select", icon='MOD_DASH') + + if object_mode == 'OBJECT': + if enable_object_button: + pie.operator("view3d.cycle_object", text="Object", icon=objicon) + else: + if enable_backface_button: + pie.operator("view3d.toggle_backface", text="Backface", icon='NORMALS_FACE', depress=depress_bface) + if enable_face_button: + pie.operator("view3d.cycle_face", text="Face", icon=facicon) + if enable_edge_button: + pie.operator("view3d.cycle_edge", text="Edge", icon=edgicon) + +class VIEW3D_pie_drag_select(bpy.types.Operator): + bl_idname = "view3d.pie_drag_select" + bl_label = "Drag Select" + + def invoke(self, context, event): + bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_Drag_Select") + return {'FINISHED'} + + # Standard transforms which apply to all cases (mix-in class, not used directly). class VIEW3D_MT_transform_base: bl_label = "Transform" @@ -6738,6 +7293,61 @@ class VIEW3D_PT_shading_options(Panel): row = col.row() row.active = not xray_active row.prop(shading, "use_dof", text="Depth of Field") + else: + if shading.type == 'WIREFRAME': + row.prop(shading, "show_xray_wireframe", text="") + sub = row.row() + sub.active = shading.show_xray_wireframe + sub.prop(shading, "xray_alpha_wireframe", text="X-Ray") + elif shading.type == 'SOLID': + row.prop(shading, "show_xray", text="") + sub = row.row() + sub.active = shading.show_xray + sub.prop(shading, "xray_alpha", text="X-Ray") + # X-ray mode is off when alpha is 1.0 + xray_active = shading.show_xray and shading.xray_alpha != 1 + + row = col.row(align=True) + row.prop(shading, "show_shadows", text="") + row.active = not xray_active + sub = row.row(align=True) + sub.active = shading.show_shadows + sub.prop(shading, "shadow_intensity", text="Shadow") + sub.popover( + panel="VIEW3D_PT_shading_options_shadow", + icon='PREFERENCES', + text="", + ) + + col = layout.column() + + row = col.row() + row.active = not xray_active + row.prop(shading, "show_cavity") + + if shading.show_cavity and not xray_active: + row.prop(shading, "cavity_type", text="Type") + + if shading.cavity_type in {'WORLD', 'BOTH'}: + col.label(text="World Space") + sub = col.row(align=True) + sub.prop(shading, "cavity_ridge_factor", text="Ridge") + sub.prop(shading, "cavity_valley_factor", text="Valley") + sub.popover( + panel="VIEW3D_PT_shading_options_ssao", + icon='PREFERENCES', + text="", + ) + + if shading.cavity_type in {'SCREEN', 'BOTH'}: + col.label(text="Screen Space") + sub = col.row(align=True) + sub.prop(shading, "curvature_ridge_factor", text="Ridge") + sub.prop(shading, "curvature_valley_factor", text="Valley") + + row = col.row() + row.active = not xray_active + row.prop(shading, "use_dof", text="Depth of Field") if shading.type in {'WIREFRAME', 'SOLID'}: row = layout.split() @@ -7131,6 +7741,21 @@ class VIEW3D_PT_overlay_edit_mesh(Panel): is_any_solid_shading = not (shading.show_xray or (shading.type == 'WIREFRAME')) + if shading.type == 'WIREFRAME': + xray = shading.show_xray_wireframe and shading.xray_alpha_wireframe < 1.0 + elif shading.type == 'SOLID': + xray = shading.show_xray and shading.xray_alpha < 1.0 + else: + xray = False + + if xray: + fdot_draw_depressed = overlay.show_face_center_xray + else: + fdot_draw_depressed = overlay.show_face_center + + col = layout.column() + col.active = display_all + col = layout.column() col.active = display_all @@ -7139,8 +7764,7 @@ class VIEW3D_PT_overlay_edit_mesh(Panel): sub = split.column() sub.prop(overlay, "show_faces", text="Faces") sub = split.column() - sub.active = is_any_solid_shading - sub.prop(overlay, "show_face_center", text="Center") + sub.operator("view3d.toggle_facedots", text="Facedots", depress = fdot_draw_depressed) row = col.row(align=True) row.prop(overlay, "show_edge_crease", text="Creases", toggle=True) @@ -9018,6 +9642,12 @@ classes = ( VIEW3D_MT_sculpt_gpencil_automasking_pie, VIEW3D_MT_wpaint_vgroup_lock_pie, VIEW3D_MT_sculpt_face_sets_edit_pie, + VIEW3D_box_lasso, + VIEW3D_box_select, + VIEW3D_circle_select, + VIEW3D_lasso_select, + VIEW3D_MT_PIE_Drag_Select, + VIEW3D_pie_drag_select, VIEW3D_MT_sculpt_curves, VIEW3D_PT_active_tool, VIEW3D_PT_active_tool_duplicate, diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index e25bb7d60cb..c06ef91bba6 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -161,7 +161,7 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): bl_context = ".mesh_edit" # dot on purpose (access from topbar) bl_label = "Options" bl_options = {'DEFAULT_CLOSED'} - bl_ui_units_x = 12 + bl_ui_units_x = 13 def draw(self, _context): # layout = self.layout diff --git a/source/blender/blenlib/BLI_lasso_2d.h b/source/blender/blenlib/BLI_lasso_2d.h index 4a15eba51dd..1bfa77acca3 100644 --- a/source/blender/blenlib/BLI_lasso_2d.h +++ b/source/blender/blenlib/BLI_lasso_2d.h @@ -26,7 +26,8 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2], int y0, int x1, int y1, - int error_value); + int error_value, + bool fully_inside); #ifdef __cplusplus } diff --git a/source/blender/blenlib/intern/lasso_2d.c b/source/blender/blenlib/intern/lasso_2d.c index 8a20dbe2c25..8fea42f679f 100644 --- a/source/blender/blenlib/intern/lasso_2d.c +++ b/source/blender/blenlib/intern/lasso_2d.c @@ -57,7 +57,8 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2], int y0, int x1, int y1, - const int error_value) + const int error_value, + bool fully_inside) { if (x0 == error_value || x1 == error_value || mcoords_len == 0) { @@ -66,23 +67,32 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2], const int v1[2] = {x0, y0}, v2[2] = {x1, y1}; - /* check points in lasso */ - if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) { - return true; - } - if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) { + /* enclosed faces only checks for both points inside selection area */ + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value) && + BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) + { return true; } - /* no points in lasso, so we have to intersect with lasso edge */ - - if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) { - return true; - } - for (uint a = 0; a < mcoords_len - 1; a++) { - if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) { + if (!fully_inside) { + /* check points in lasso */ + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) { return true; } + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) { + return true; + } + + /* no points in lasso, so we have to intersect with lasso edge */ + + if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) { + return true; + } + for (unsigned int a = 0; a < mcoords_len - 1; a++) { + if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) { + return true; + } + } } return false; diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index cb25dfeffc0..3c77e3796d1 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -203,6 +203,8 @@ static void blo_update_defaults_screen(bScreen *screen, /* Disable Curve Normals. */ v3d->overlay.edit_flag &= ~V3D_OVERLAY_EDIT_CU_NORMALS; v3d->overlay.normals_constant_screen_size = 7.0f; + /* Show xray facedots */ + v3d->overlay.edit_flag |= V3D_OVERLAY_EDIT_FACE_DOT_XRAY; } else if (area->spacetype == SPACE_CLIP) { SpaceClip *sclip = static_cast(area->spacedata.first); diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index 855bb4b52a4..65913bab522 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -921,6 +921,9 @@ void blo_do_versions_userdef(UserDef *userdef) STRNCPY(km->idname, "NLA Tracks"); } } + + userdef->header_highlight = 5; + userdef->selection_radius = 75.0f; } /** diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.cc b/source/blender/draw/engines/overlay/overlay_edit_mesh.cc index 3060c66ea77..9a89cb2c12d 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.cc +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.cc @@ -61,8 +61,10 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) bool select_face = pd->edit_mesh.select_face = (tsettings->selectmode & SCE_SELECT_FACE) != 0; bool select_edge = pd->edit_mesh.select_edge = (tsettings->selectmode & SCE_SELECT_EDGE) != 0; - bool show_face_dots = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) != 0 || - pd->edit_mesh.do_zbufclip; + bool show_face_dots = !XRAY_FLAG_ENABLED(draw_ctx->v3d) && + v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT || + XRAY_FLAG_ENABLED(draw_ctx->v3d) && + v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT_XRAY; bool show_retopology = RETOPOLOGY_ENABLED(v3d); float retopology_offset = RETOPOLOGY_OFFSET(v3d); diff --git a/source/blender/draw/engines/select/select_draw_utils.cc b/source/blender/draw/engines/select/select_draw_utils.cc index b79310ccbe9..d5d82444177 100644 --- a/source/blender/draw/engines/select/select_draw_utils.cc +++ b/source/blender/draw/engines/select/select_draw_utils.cc @@ -59,7 +59,9 @@ static bool check_ob_drawface_dot(short select_mode, const View3D *v3d, eDrawTyp { if (select_mode & SCE_SELECT_FACE) { if ((dt < OB_SOLID) || XRAY_FLAG_ENABLED(v3d)) { - return true; + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT_XRAY) { + return true; + } } if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) { return true; diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index 8844b0bc30f..252461864b6 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -832,7 +832,8 @@ bool select_lasso(const ViewContext &vc, int(pos1_proj.y), int(pos2_proj.x), int(pos2_proj.y), - IS_CLIPPED)) + IS_CLIPPED, + false)) { apply_selection_operation_at_index(selection.span, curve_i, sel_op); changed = true; diff --git a/source/blender/editors/gpencil_legacy/gpencil_select.cc b/source/blender/editors/gpencil_legacy/gpencil_select.cc index 2f13a9a0859..46764de9d7f 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_select.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_select.cc @@ -1490,7 +1490,8 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd, const float diff_mat[4][4], const int selectmode, const float scale, - const bool is_curve_edit) + const bool is_curve_edit, + const bool square) { bGPDspoint *pt = nullptr; int x0 = 0, y0 = 0; @@ -1510,7 +1511,9 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd, /* do boundbox check first */ if (!ELEM(V2D_IS_CLIPPED, x0, y0) && BLI_rcti_isect_pt(rect, x0, y0)) { /* only check if point is inside */ - if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) { + if (square ? BLI_rcti_isect_pt(rect, x0, y0) : + ((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) + { hit = true; /* change selection */ @@ -1581,7 +1584,8 @@ static bool gpencil_do_curve_circle_sel(bContext *C, const bool select, rcti *rect, const float diff_mat[4][4], - const int selectmode) + const int selectmode, + const bool square) { ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); @@ -1629,7 +1633,7 @@ static bool gpencil_do_curve_circle_sel(bContext *C, int dist_x = screen_co[0] - mx; int dist_y = screen_co[1] - my; int dist = dist_x * dist_x + dist_y * dist_y; - if (dist <= radius * radius) { + if (square ? BLI_rcti_isect_pt(rect, screen_co[0], screen_co[1]) : dist <= radius * radius) { hit = true; /* change selection */ if (select) { @@ -1674,6 +1678,7 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); const bool is_curve_edit = bool(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)); + const bool square = U.square_select; int selectmode; if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL_LEGACY) { @@ -1729,7 +1734,7 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc) { changed |= gpencil_do_curve_circle_sel( - C, gps, gpc, mx, my, radius, select, &rect, gps_iter.diff_mat, selectmode); + C, gps, gpc, mx, my, radius, select, &rect, gps_iter.diff_mat, selectmode, square); } GP_EDITABLE_CURVES_END(gps_iter); } @@ -1758,7 +1763,8 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) gpstroke_iter.diff_mat, selectmode, scale, - is_curve_edit); + is_curve_edit, + square); } GP_EVALUATED_STROKES_END(gpstroke_iter); } diff --git a/source/blender/editors/include/UI_icons.hh b/source/blender/editors/include/UI_icons.hh index 49a7e34acef..89552fe111d 100644 --- a/source/blender/editors/include/UI_icons.hh +++ b/source/blender/editors/include/UI_icons.hh @@ -430,8 +430,8 @@ DEF_ICON(IMAGE_REFERENCE) /* EMPTY */ DEF_ICON_BLANK(445) -DEF_ICON_BLANK(446) -DEF_ICON_BLANK(447) +DEF_ICON(OBJECT_ENCLOSE) +DEF_ICON(FACE_TOUCH) DEF_ICON(NODE_INSERT_ON) DEF_ICON(NODE_INSERT_OFF) DEF_ICON(NODE_TOP) @@ -442,14 +442,14 @@ DEF_ICON(ANCHOR_BOTTOM) DEF_ICON(ANCHOR_LEFT) DEF_ICON(ANCHOR_RIGHT) DEF_ICON(ANCHOR_CENTER) -DEF_ICON_BLANK(703) -DEF_ICON_BLANK(704) -DEF_ICON_BLANK(705) -DEF_ICON_BLANK(706) -DEF_ICON_BLANK(707) -DEF_ICON_BLANK(708) -DEF_ICON_BLANK(709) -DEF_ICON_BLANK(710) +DEF_ICON(EDGE_DEFAULT) +DEF_ICON(EDGE_ENCLOSE) +DEF_ICON(EDGE_TOUCH) +DEF_ICON(FACE_DEFAULT) +DEF_ICON(FACE_ENCLOSE) +DEF_ICON(OBJECT_TOUCH) +DEF_ICON(AUTO_XRAY) +DEF_ICON(SELECT_THROUGH) DEF_ICON(SELECT_SET) DEF_ICON(SELECT_EXTEND) DEF_ICON(SELECT_SUBTRACT) diff --git a/source/blender/editors/interface/resources.cc b/source/blender/editors/interface/resources.cc index 66eb3b8a3dd..b51f3bef494 100644 --- a/source/blender/editors/interface/resources.cc +++ b/source/blender/editors/interface/resources.cc @@ -248,7 +248,7 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_HEADER_ACTIVE: { cp = ts->header; - const int factor = 5; + const int factor = U.custom_highlight ? U.header_highlight : 5; /* Lighten the header color when editor is active. */ header_active[0] = cp[0] > 245 ? cp[0] - factor : cp[0] + factor; header_active[1] = cp[1] > 245 ? cp[1] - factor : cp[1] + factor; diff --git a/source/blender/editors/mesh/editmesh_select.cc b/source/blender/editors/mesh/editmesh_select.cc index 5b84b92b22f..7cc0714c008 100644 --- a/source/blender/editors/mesh/editmesh_select.cc +++ b/source/blender/editors/mesh/editmesh_select.cc @@ -246,7 +246,9 @@ static void findnearestvert__doClosest(void *user_data, dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (!(U.adjustable_click_select && U.select_unbiased) && data->use_select_bias && + BM_elem_flag_test(eve, BM_ELEM_SELECT)) + { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -455,7 +457,9 @@ static void find_nearest_edge__doClosest(void *user_data, dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + if (!(U.adjustable_click_select && U.select_unbiased) && data->use_select_bias && + BM_elem_flag_test(eed, BM_ELEM_SELECT)) + { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -688,7 +692,9 @@ static void findnearestface__doClosest(void *user_data, dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + if (!(U.adjustable_click_select && U.select_unbiased) && data->use_select_bias && + BM_elem_flag_test(efa, BM_ELEM_SELECT)) + { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -724,27 +730,27 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, uint base_index = 0; if (!XRAY_FLAG_ENABLED(vc->v3d)) { - float dist_test; + float dist_test = 0.0f; uint index; BMFace *efa; + uint dist_px_manhattan_test = 0; { - uint dist_px_manhattan_test = 0; - if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) { - dist_px_manhattan_test = uint( - ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p)); + if (U.adjustable_click_select || + *dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) + { + dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, + *dist_px_manhattan_p); } DRW_select_buffer_context_create(vc->depsgraph, bases, SCE_SELECT_FACE); - if (dist_px_manhattan_test == 0) { + if (!(U.adjustable_click_select) && dist_px_manhattan_test == 0) { index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval); - dist_test = 0.0f; } else { index = DRW_select_buffer_find_nearest_to_point( vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test); - dist_test = dist_px_manhattan_test; } if (index) { @@ -778,7 +784,14 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, /* end exception */ if (efa) { - if (dist_test < *dist_px_manhattan_p) { + if (U.adjustable_click_select && dist_px_manhattan_test < *dist_px_manhattan_p) { + if (r_base_index) { + *r_base_index = base_index; + } + *dist_px_manhattan_p = dist_px_manhattan_test; + return efa; + } + else if (!U.adjustable_click_select && dist_test < *dist_px_manhattan_p) { if (r_base_index) { *r_base_index = base_index; } diff --git a/source/blender/editors/screen/screen_ops.cc b/source/blender/editors/screen/screen_ops.cc index 332da569b0a..8badfc931e2 100644 --- a/source/blender/editors/screen/screen_ops.cc +++ b/source/blender/editors/screen/screen_ops.cc @@ -3982,6 +3982,62 @@ static void SCREEN_OT_repeat_history(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Invoke Last Operator + * \{ */ + +static int invoke_last_exec(bContext *C, wmOperator * /*op*/) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmOperator *lastop = static_cast(wm->operators.last); + + /* Seek last registered operator */ + while (lastop) { + if (lastop->type->flag & OPTYPE_REGISTER) { + if (STREQ(lastop->idname, "MESH_OT_delete") || STREQ(lastop->idname, "MESH_OT_select_all") || + STREQ(lastop->idname, "MESH_OT_select_mode") || + STREQ(lastop->idname, "OBJECT_OT_editmode_toggle") || + STREQ(lastop->idname, "OBJECT_OT_select_all") || + STREQ(lastop->idname, "TRANSFORM_OT_resize") || + STREQ(lastop->idname, "TRANSFORM_OT_rotate") || + STREQ(lastop->idname, "TRANSFORM_OT_trackball") || + STREQ(lastop->idname, "TRANSFORM_OT_translate")) + { + lastop = lastop->prev; + } + else { + // printf(lastop->idname, "\n"); + break; + } + } + else { + lastop = lastop->prev; + } + } + + if (lastop) { + WM_operator_free_all_after(wm, lastop); + WM_operator_invoke_last(C, lastop); + } + + return OPERATOR_CANCELLED; +} + +static void SCREEN_OT_invoke_last(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Invoke Last"; + ot->description = "Invoke last operation"; + ot->idname = "SCREEN_OT_invoke_last"; + + /* api callbacks */ + ot->exec = invoke_last_exec; + + ot->poll = ED_operator_screenactive; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Redo Operator * \{ */ @@ -5863,6 +5919,7 @@ void ED_operatortypes_screen() WM_operatortype_append(SCREEN_OT_actionzone); WM_operatortype_append(SCREEN_OT_repeat_last); WM_operatortype_append(SCREEN_OT_repeat_history); + WM_operatortype_append(SCREEN_OT_invoke_last); WM_operatortype_append(SCREEN_OT_redo_last); /* Screen tools. */ diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index a4243711d9f..86aed7fee5c 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -875,9 +875,11 @@ static int node_circleselect_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); ARegion *region = CTX_wm_region(C); bNodeTree &node_tree = *snode->edittree; + const bool square = U.square_select; int x, y, radius; float2 offset; + rctf rectf; float zoom = float(BLI_rcti_size_x(®ion->winrct)) / float(BLI_rctf_size_x(®ion->v2d.cur)); @@ -896,6 +898,11 @@ static int node_circleselect_exec(bContext *C, wmOperator *op) UI_view2d_region_to_view(®ion->v2d, x, y, &offset.x, &offset.y); + if (square) { + BLI_rctf_init(&rectf, x - radius, x + radius, y - radius, y + radius); + UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); + } + for (bNode *node : node_tree.all_nodes()) { switch (node->type) { case NODE_FRAME: { @@ -904,16 +911,32 @@ static int node_circleselect_exec(bContext *C, wmOperator *op) rctf frame_inside = node_frame_rect_inside(*snode, *node); const float radius_adjusted = float(radius) / zoom; BLI_rctf_pad(&frame_inside, -2.0f * radius_adjusted, -2.0f * radius_adjusted); - if (BLI_rctf_isect_circle(&node->runtime->totr, offset, radius_adjusted) && - !BLI_rctf_isect_circle(&frame_inside, offset, radius_adjusted)) - { - nodeSetSelected(node, select); + if (square) { + if (BLI_rctf_isect(&rectf, &node->runtime->totr, nullptr) && + !BLI_rctf_inside_rctf(&frame_inside, &rectf)) + { + nodeSetSelected(node, select); + } + } + else { + if (BLI_rctf_isect_circle(&node->runtime->totr, offset, radius_adjusted) && + !BLI_rctf_isect_circle(&frame_inside, offset, radius_adjusted)) + { + nodeSetSelected(node, select); + } } break; } default: { - if (BLI_rctf_isect_circle(&node->runtime->totr, offset, radius / zoom)) { - nodeSetSelected(node, select); + if (square) { + if (BLI_rctf_isect(&rectf, &node->runtime->totr, nullptr)) { + nodeSetSelected(node, select); + } + } + else { + if (BLI_rctf_isect_circle(&node->runtime->totr, offset, radius / zoom)) { + nodeSetSelected(node, select); + } } break; } diff --git a/source/blender/editors/space_view3d/view3d_edit.cc b/source/blender/editors/space_view3d/view3d_edit.cc index 161e0a97b21..e3f68205b07 100644 --- a/source/blender/editors/space_view3d/view3d_edit.cc +++ b/source/blender/editors/space_view3d/view3d_edit.cc @@ -1243,3 +1243,2654 @@ void VIEW3D_OT_toggle_xray(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Automatic XRay + * \{ */ + +static int toggle_auto_xray_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + Object *obedit = CTX_data_edit_object(C); + bToolRef *tref = area->runtime.tool; + + const bool box_op = U.drag_direction_box == 0 || + U.drag_direction_mode == 1 && !U.direction_auto_xray; + const bool lasso_op = U.drag_direction_lasso == 0 || + U.drag_direction_mode == 1 && !U.direction_auto_xray; + const bool box_circle_lasso = U.sync_box && box_op && U.sync_circle && U.sync_lasso && lasso_op; + const bool box = STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box"); + const bool lasso = STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso"); + const bool circle = STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle"); + const bool box_lasso = (box || lasso) && lasso_op && U.sync_lasso && box_op && U.sync_box; + const bool box_circle = (box || circle) && U.sync_circle && box_op && U.sync_box; + const bool circle_lasso = (lasso || circle) && U.sync_circle && lasso_op && U.sync_lasso; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1) { + if (box && box_op || box_circle_lasso || box_lasso || box_circle) { + if (box) { + /* Off in the active mode and On in the other mode = Both */ + if (obedit && U.auto_xray_box == 1 || !obedit && U.auto_xray_box == 2) { + U.auto_xray_box = 3; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_lasso = 3; + } + + if (box_circle_lasso || box_circle) { + U.auto_xray_circle = 3; + } + } + /* if toggle mode == Current, Off Edit = On Edit, Both Object = Edit only */ + else if (U.auto_xray_toggle == 1 && + (obedit && U.auto_xray_box == 0 || !obedit && U.auto_xray_box == 3)) + { + U.auto_xray_box = 2; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.auto_xray_circle = 2; + } + } + /* if toggle mode == Current, Off Object = On Object, Both Edit = Object only */ + else if (U.auto_xray_toggle == 1 && + (!obedit && U.auto_xray_box == 0 || obedit && U.auto_xray_box == 3)) + { + U.auto_xray_box = 1; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.auto_xray_circle = 1; + } + } + /* otherwise On = Off */ + else if (U.auto_xray_box != 0) { + U.auto_xray_box = 0; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.auto_xray_circle = 0; + } + } + /* and Off = Both */ + else { + U.auto_xray_box = 3; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_lasso = 3; + } + + if (box_circle_lasso || box_circle) { + U.auto_xray_circle = 3; + } + } + } + else if (lasso) { + if (obedit && U.auto_xray_lasso == 1 || !obedit && U.auto_xray_lasso == 2) { + U.auto_xray_lasso = 3; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_circle = 3; + } + } + else if (U.auto_xray_toggle == 1 && + (obedit && U.auto_xray_lasso == 0 || !obedit && U.auto_xray_lasso == 3)) + { + U.auto_xray_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_circle = 2; + } + } + else if (U.auto_xray_toggle == 1 && + (!obedit && U.auto_xray_lasso == 0 || obedit && U.auto_xray_lasso == 3)) + { + U.auto_xray_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_circle = 1; + } + } + else if (U.auto_xray_lasso != 0) { + U.auto_xray_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_circle = 0; + } + } + else { + U.auto_xray_lasso = 3; + + if (box_circle_lasso || box_lasso) { + U.auto_xray_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_circle = 3; + } + } + } + else { + if (obedit && U.auto_xray_circle == 1 || !obedit && U.auto_xray_circle == 2) { + U.auto_xray_circle = 3; + + if (box_circle_lasso || box_circle) { + U.auto_xray_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_lasso = 3; + } + } + else if (U.auto_xray_toggle == 1 && + (obedit && U.auto_xray_circle == 0 || !obedit && U.auto_xray_circle == 3)) + { + U.auto_xray_circle = 2; + + if (box_circle_lasso || box_circle) { + U.auto_xray_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_lasso = 2; + } + } + else if (U.auto_xray_toggle == 1 && + (!obedit && U.auto_xray_circle == 0 || obedit && U.auto_xray_circle == 3)) + { + U.auto_xray_circle = 1; + + if (box_circle_lasso || box_circle) { + U.auto_xray_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_lasso = 1; + } + } + else if (U.auto_xray_circle != 0) { + U.auto_xray_circle = 0; + + if (box_circle_lasso || box_circle) { + U.auto_xray_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_lasso = 0; + } + } + else { + U.auto_xray_circle = 3; + + if (box_circle_lasso || box_circle) { + U.auto_xray_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.auto_xray_lasso = 3; + } + } + } + } + else if (lasso && lasso_op || circle_lasso) { + if (lasso) { + if (obedit && U.auto_xray_lasso == 1 || !obedit && U.auto_xray_lasso == 2) { + U.auto_xray_lasso = 3; + + if (circle_lasso) { + U.auto_xray_circle = 3; + } + } + else if (U.auto_xray_toggle_lasso == 1 && + (obedit && U.auto_xray_lasso == 0 || !obedit && U.auto_xray_lasso == 3)) + { + U.auto_xray_lasso = 2; + + if (circle_lasso) { + U.auto_xray_circle = 2; + } + } + else if (U.auto_xray_toggle_lasso == 1 && + (!obedit && U.auto_xray_lasso == 0 || obedit && U.auto_xray_lasso == 3)) + { + U.auto_xray_lasso = 1; + + if (circle_lasso) { + U.auto_xray_circle = 1; + } + } + else if (U.auto_xray_lasso != 0) { + U.auto_xray_lasso = 0; + + if (circle_lasso) { + U.auto_xray_circle = 0; + } + } + else { + U.auto_xray_lasso = 3; + + if (circle_lasso) { + U.auto_xray_circle = 3; + } + } + } + else { + if (obedit && U.auto_xray_circle == 1 || !obedit && U.auto_xray_circle == 2) { + U.auto_xray_circle = 3; + + if (circle_lasso) { + U.auto_xray_lasso = 3; + } + } + else if (U.auto_xray_toggle_lasso == 1 && + (obedit && U.auto_xray_circle == 0 || !obedit && U.auto_xray_circle == 3)) + { + U.auto_xray_circle = 2; + + if (circle_lasso) { + U.auto_xray_lasso = 2; + } + } + else if (U.auto_xray_toggle_lasso == 1 && + (!obedit && U.auto_xray_circle == 0 || obedit && U.auto_xray_circle == 3)) + { + U.auto_xray_circle = 1; + + if (circle_lasso) { + U.auto_xray_lasso = 1; + } + } + else if (U.auto_xray_circle != 0) { + U.auto_xray_circle = 0; + + if (circle_lasso) { + U.auto_xray_lasso = 0; + } + } + else { + U.auto_xray_circle = 3; + + if (circle_lasso) { + U.auto_xray_lasso = 3; + } + } + } + } + else if (circle) { + if (obedit && U.auto_xray_circle == 1 || !obedit && U.auto_xray_circle == 2) { + U.auto_xray_circle = 3; + } + else if (U.auto_xray_toggle_circle == 1 && + (obedit && U.auto_xray_circle == 0 || !obedit && U.auto_xray_circle == 3)) + { + U.auto_xray_circle = 2; + } + else if (U.auto_xray_toggle_circle == 1 && + (!obedit && U.auto_xray_circle == 0 || obedit && U.auto_xray_circle == 3)) + { + U.auto_xray_circle = 1; + } + else if (U.auto_xray_circle != 0) { + U.auto_xray_circle = 0; + } + else { + U.auto_xray_circle = 3; + } + } + } + else { + if (obedit && U.auto_xray_box == 1 || !obedit && U.auto_xray_box == 2) { + U.auto_xray_box = 3; + } + else if (U.auto_xray_toggle == 1 && + (obedit && U.auto_xray_box == 0 || !obedit && U.auto_xray_box == 3)) + { + U.auto_xray_box = 2; + } + else if (U.auto_xray_toggle == 1 && + (!obedit && U.auto_xray_box == 0 || obedit && U.auto_xray_box == 3)) + { + U.auto_xray_box = 1; + } + else if (U.auto_xray_box != 0) { + U.auto_xray_box = 0; + } + else { + U.auto_xray_box = 3; + } + } + ED_area_tag_redraw(area); + } + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_auto_xray(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Auto X-Ray"; + ot->idname = "VIEW3D_OT_toggle_auto_xray"; + ot->description = "Transparent scene display during box, lasso, and circle select"; + + /* api callbacks */ + ot->exec = toggle_auto_xray_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Select Through + * \{ */ + +static int toggle_select_through_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + Object *obedit = CTX_data_edit_object(C); + bToolRef *tref = area->runtime.tool; + + const bool box_op = U.drag_direction_box == 0 || + U.drag_direction_mode == 1 && !U.direction_select_through; + const bool lasso_op = U.drag_direction_lasso == 0 || + U.drag_direction_mode == 1 && !U.direction_select_through; + const bool box_circle_lasso = U.sync_box && box_op && U.sync_circle && U.sync_lasso && lasso_op; + const bool box = STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box"); + const bool lasso = STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso"); + const bool circle = STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle"); + const bool box_lasso = (box || lasso) && lasso_op && U.sync_lasso && box_op && U.sync_box; + const bool box_circle = (box || circle) && U.sync_circle && box_op && U.sync_box; + const bool circle_lasso = (lasso || circle) && U.sync_circle && lasso_op && U.sync_lasso; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1) { + if (box && box_op || box_circle_lasso || box_lasso || box_circle) { + if (box) { + /* Off in the active mode and On in the other mode = Both */ + if (obedit && U.select_through_box == 1 || !obedit && U.select_through_box == 2) { + U.select_through_box = 3; + + if (box_circle_lasso || box_lasso) { + U.select_through_lasso = 3; + } + + if (box_circle_lasso || box_circle) { + U.select_through_circle = 3; + } + } + /* if toggle mode == Current, Off Edit = On Edit, Both Object = Edit only */ + else if (U.select_through_toggle == 1 && + (obedit && U.select_through_box == 0 || !obedit && U.select_through_box == 3)) + { + U.select_through_box = 2; + + if (box_circle_lasso || box_lasso) { + U.select_through_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.select_through_circle = 2; + } + } + /* if toggle mode == Current, Off Object = On Object, Both Edit = Object only */ + else if (U.select_through_toggle == 1 && + (!obedit && U.select_through_box == 0 || obedit && U.select_through_box == 3)) + { + U.select_through_box = 1; + + if (box_circle_lasso || box_lasso) { + U.select_through_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.select_through_circle = 1; + } + } + /* otherwise On = Off */ + else if (U.select_through_box != 0) { + U.select_through_box = 0; + + if (box_circle_lasso || box_lasso) { + U.select_through_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.select_through_circle = 0; + } + } + /* and Off = Both */ + else { + U.select_through_box = 3; + + if (box_circle_lasso || box_lasso) { + U.select_through_lasso = 3; + } + + if (box_circle_lasso || box_circle) { + U.select_through_circle = 3; + } + } + } + else if (lasso) { + if (obedit && U.select_through_lasso == 1 || !obedit && U.select_through_lasso == 2) { + U.select_through_lasso = 3; + + if (box_circle_lasso || box_lasso) { + U.select_through_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_circle = 3; + } + } + else if (U.select_through_toggle == 1 && (obedit && U.select_through_lasso == 0 || + !obedit && U.select_through_lasso == 3)) + { + U.select_through_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.select_through_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_circle = 2; + } + } + else if (U.select_through_toggle == 1 && (!obedit && U.select_through_lasso == 0 || + obedit && U.select_through_lasso == 3)) + { + U.select_through_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.select_through_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_circle = 1; + } + } + else if (U.select_through_lasso != 0) { + U.select_through_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.select_through_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_circle = 0; + } + } + else { + U.select_through_lasso = 3; + + if (box_circle_lasso || box_lasso) { + U.select_through_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_circle = 3; + } + } + } + else { + if (obedit && U.select_through_circle == 1 || !obedit && U.select_through_circle == 2) { + U.select_through_circle = 3; + + if (box_circle_lasso || box_circle) { + U.select_through_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_lasso = 3; + } + } + else if (U.select_through_toggle == 1 && (obedit && U.select_through_circle == 0 || + !obedit && U.select_through_circle == 3)) + { + U.select_through_circle = 2; + + if (box_circle_lasso || box_circle) { + U.select_through_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_lasso = 2; + } + } + else if (U.select_through_toggle == 1 && (!obedit && U.select_through_circle == 0 || + obedit && U.select_through_circle == 3)) + { + U.select_through_circle = 1; + + if (box_circle_lasso || box_circle) { + U.select_through_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_lasso = 1; + } + } + else if (U.select_through_circle != 0) { + U.select_through_circle = 0; + + if (box_circle_lasso || box_circle) { + U.select_through_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_lasso = 0; + } + } + else { + U.select_through_circle = 3; + + if (box_circle_lasso || box_circle) { + U.select_through_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.select_through_lasso = 3; + } + } + } + } + else if (lasso && lasso_op || circle_lasso) { + if (lasso) { + if (obedit && U.select_through_lasso == 1 || !obedit && U.select_through_lasso == 2) { + U.select_through_lasso = 3; + + if (circle_lasso) { + U.select_through_circle = 3; + } + } + else if (U.select_through_toggle_lasso == 1 && (obedit && U.select_through_lasso == 0 || + !obedit && U.select_through_lasso == 3)) + { + U.select_through_lasso = 2; + + if (circle_lasso) { + U.select_through_circle = 2; + } + } + else if (U.select_through_toggle_lasso == 1 && (!obedit && U.select_through_lasso == 0 || + obedit && U.select_through_lasso == 3)) + { + U.select_through_lasso = 1; + + if (circle_lasso) { + U.select_through_circle = 1; + } + } + else if (U.select_through_lasso != 0) { + U.select_through_lasso = 0; + + if (circle_lasso) { + U.select_through_circle = 0; + } + } + else { + U.select_through_lasso = 3; + + if (circle_lasso) { + U.select_through_circle = 3; + } + } + } + else { + if (obedit && U.select_through_circle == 1 || !obedit && U.select_through_circle == 2) { + U.select_through_circle = 3; + + if (circle_lasso) { + U.select_through_lasso = 3; + } + } + else if (U.select_through_toggle_lasso == 1 && (obedit && U.select_through_circle == 0 || + !obedit && U.select_through_circle == 3)) + { + U.select_through_circle = 2; + + if (circle_lasso) { + U.select_through_lasso = 2; + } + } + else if (U.select_through_toggle_lasso == 1 && + (!obedit && U.select_through_circle == 0 || + obedit && U.select_through_circle == 3)) + { + U.select_through_circle = 1; + + if (circle_lasso) { + U.select_through_lasso = 1; + } + } + else if (U.select_through_circle != 0) { + U.select_through_circle = 0; + + if (circle_lasso) { + U.select_through_lasso = 0; + } + } + else { + U.select_through_circle = 3; + + if (circle_lasso) { + U.select_through_lasso = 3; + } + } + } + } + else if (circle) { + if (obedit && U.select_through_circle == 1 || !obedit && U.select_through_circle == 2) { + U.select_through_circle = 3; + } + else if (U.select_through_toggle_circle == 1 && (obedit && U.select_through_circle == 0 || + !obedit && U.select_through_circle == 3)) + { + U.select_through_circle = 2; + } + else if (U.select_through_toggle_circle == 1 && (!obedit && U.select_through_circle == 0 || + obedit && U.select_through_circle == 3)) + { + U.select_through_circle = 1; + } + else if (U.select_through_circle != 0) { + U.select_through_circle = 0; + } + else { + U.select_through_circle = 3; + } + } + } + else { + if (obedit && U.select_through_box == 1 || !obedit && U.select_through_box == 2) { + U.select_through_box = 3; + } + else if (U.select_through_toggle == 1 && + (obedit && U.select_through_box == 0 || !obedit && U.select_through_box == 3)) + { + U.select_through_box = 2; + } + else if (U.select_through_toggle == 1 && + (!obedit && U.select_through_box == 0 || obedit && U.select_through_box == 3)) + { + U.select_through_box = 1; + } + else if (U.select_through_box != 0) { + U.select_through_box = 0; + } + else { + U.select_through_box = 3; + } + } + ED_area_tag_redraw(area); + } + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_select_through(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Through"; + ot->idname = "VIEW3D_OT_toggle_select_through"; + ot->description = "Select occluded objects and mesh with box, lasso, and circle select"; + + /* api callbacks */ + ot->exec = toggle_select_through_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Backface Filter + * \{ */ + +static int toggle_backface_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + Object *obedit = CTX_data_edit_object(C); + bToolRef *tref = area->runtime.tool; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1) { + const bool box_op = U.drag_direction_box == 0 || + U.drag_direction_mode == 1 && !U.direction_backface; + const bool lasso_op = U.drag_direction_lasso == 0 || + U.drag_direction_mode == 1 && !U.direction_backface; + const bool box = STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box"); + const bool lasso = STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso"); + const bool circle = STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle"); + const bool box_circle_lasso = (box || circle || lasso) && U.sync_box && box_op && + U.sync_circle && U.sync_lasso && lasso_op; + const bool box_lasso = (box || lasso) && lasso_op && U.sync_lasso && box_op && U.sync_box; + const bool box_circle = (box || circle) && U.sync_circle && box_op && U.sync_box; + const bool circle_lasso = (lasso || circle) && U.sync_circle && lasso_op && U.sync_lasso; + + if (box && box_op || box_circle_lasso || box_lasso || box_circle) { + if (box) { + if (U.backface_box != 0) { + U.backface_box = 0; + + if (box_circle_lasso || box_lasso) { + U.backface_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.backface_circle = 0; + } + } + else if (U.backface_toggle == 1) { + U.backface_box = 2; + + if (box_circle_lasso || box_lasso) { + U.backface_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.backface_circle = 2; + } + } + else { + U.backface_box = 1; + + if (box_circle_lasso || box_lasso) { + U.backface_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.backface_circle = 1; + } + } + } + else if (lasso) { + if (U.backface_lasso != 0) { + U.backface_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.backface_box = 0; + } + + if (box_circle_lasso || box_circle) { + U.backface_circle = 0; + } + } + else if (U.backface_toggle == 1) { + U.backface_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.backface_box = 2; + } + + if (box_circle_lasso || box_circle) { + U.backface_circle = 2; + } + } + else { + U.backface_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.backface_box = 1; + } + + if (box_circle_lasso || box_circle) { + U.backface_circle = 1; + } + } + } + else { + if (U.backface_circle != 0) { + U.backface_circle = 0; + + if (box_circle_lasso || box_circle) { + U.backface_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.backface_lasso = 0; + } + } + else if (U.backface_toggle == 1) { + U.backface_circle = 2; + + if (box_circle_lasso || box_circle) { + U.backface_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.backface_lasso = 2; + } + } + else { + U.backface_circle = 1; + + if (box_circle_lasso || box_circle) { + U.backface_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.backface_lasso = 1; + } + } + } + } + else if (lasso && lasso_op || circle_lasso) { + if (lasso) { + if (U.backface_lasso != 0) { + U.backface_lasso = 0; + + if (circle_lasso) { + U.backface_circle = 0; + } + } + else if (U.backface_toggle_lasso == 1) { + U.backface_lasso = 2; + + if (circle_lasso) { + U.backface_circle = 2; + } + } + else { + U.backface_lasso = 1; + + if (circle_lasso) { + U.backface_circle = 1; + } + } + } + else { + if (U.backface_circle != 0) { + U.backface_circle = 0; + + if (circle_lasso) { + U.backface_lasso = 0; + } + } + else if (U.backface_toggle_lasso == 1) { + U.backface_circle = 2; + + if (circle_lasso) { + U.backface_lasso = 2; + } + } + else { + U.backface_circle = 1; + + if (circle_lasso) { + U.backface_lasso = 1; + } + } + } + } + else if (circle) { + if (U.backface_circle != 0) { + U.backface_circle = 0; + } + else if (U.backface_toggle_circle == 1) { + U.backface_circle = 2; + } + else { + U.backface_circle = 1; + } + } + } + else { + if (U.backface_box != 0) { + U.backface_box = 0; + } + else if (U.backface_toggle == 1) { + U.backface_box = 2; + } + else { + U.backface_box = 1; + } + } + ED_area_tag_redraw(area); + } + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_backface(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Backface Filter"; + ot->idname = "VIEW3D_OT_toggle_backface"; + ot->description = + "Select mesh based on the direciton of their normals with box, lasso, and circle select"; + + /* api callbacks */ + ot->exec = toggle_backface_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Facedots + * \{ */ + +static int toggle_facedots_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + View3D *v3d = CTX_wm_view3d(C); + const bool solid = v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT; + const bool xray = v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT_XRAY; + + if (U.facedot_mode == 1 && xray == solid) { + v3d->overlay.edit_flag ^= V3D_OVERLAY_EDIT_FACE_DOT; + v3d->overlay.edit_flag ^= V3D_OVERLAY_EDIT_FACE_DOT_XRAY; + } + else if (XRAY_FLAG_ENABLED(v3d)) { + v3d->overlay.edit_flag ^= V3D_OVERLAY_EDIT_FACE_DOT_XRAY; + } + else { + v3d->overlay.edit_flag ^= V3D_OVERLAY_EDIT_FACE_DOT; + } + + ED_area_tag_redraw(area); + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_facedots(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Facedots"; + ot->idname = "VIEW3D_OT_toggle_facedots"; + ot->description = "Show face center dots in the current shading mode"; + /* api callbacks */ + ot->exec = toggle_facedots_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cycle Shading Mode + * \{ */ + +static int cycle_shading_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + View3D *v3d = CTX_wm_view3d(C); + + if (v3d->shading.type == OB_WIRE) { + if (U.shading_cycle_solid) { + v3d->shading.type = OB_SOLID; + } + else if (U.shading_cycle_material) { + v3d->shading.type = OB_MATERIAL; + } + else if (U.shading_cycle_render) { + v3d->shading.type = OB_RENDER; + } + } + else if (v3d->shading.type == OB_SOLID) { + if (U.shading_cycle_material) { + v3d->shading.type = OB_MATERIAL; + } + else if (U.shading_cycle_render) { + v3d->shading.type = OB_RENDER; + } + else if (U.shading_cycle_wire) { + v3d->shading.type = OB_WIRE; + } + } + else if (v3d->shading.type == OB_MATERIAL) { + if (U.shading_cycle_render) { + v3d->shading.type = OB_RENDER; + } + else if (U.shading_cycle_wire) { + v3d->shading.type = OB_WIRE; + } + else if (U.shading_cycle_solid) { + v3d->shading.type = OB_SOLID; + } + } + else { + if (U.shading_cycle_wire) { + v3d->shading.type = OB_WIRE; + } + else if (U.shading_cycle_solid) { + v3d->shading.type = OB_SOLID; + } + else if (U.shading_cycle_material) { + v3d->shading.type = OB_MATERIAL; + } + } + + ED_area_tag_redraw(area); + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_cycle_shading(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cycle Shading Mode"; + ot->idname = "VIEW3D_OT_cycle_shading"; + ot->description = "Switch to the next shading mode in the cycle"; + /* api callbacks */ + ot->exec = cycle_shading_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cycle Object Mode + * \{ */ + +static int cycle_object_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + bToolRef *tref = area->runtime.tool; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1) { + const bool box_op = U.drag_direction_box == 0 || + U.drag_direction_mode == 1 && !U.direction_object; + const bool lasso_op = U.drag_direction_lasso == 0 || + U.drag_direction_mode == 1 && !U.direction_object; + const bool box = STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box"); + const bool lasso = STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso"); + const bool circle = STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle"); + const bool box_circle_lasso = (box || circle || lasso) && U.sync_box && box_op && + U.sync_circle && U.sync_lasso && lasso_op; + const bool box_lasso = (box || lasso) && lasso_op && U.sync_lasso && box_op && U.sync_box; + const bool box_circle = (box || circle) && U.sync_circle && box_op && U.sync_box; + const bool circle_lasso = (lasso || circle) && U.sync_circle && lasso_op && U.sync_lasso; + + if (box && box_op || box_circle_lasso || box_lasso || box_circle) { + if (box) { + if (U.object_select_box == 1) { + if (U.object_cycle_origin) { + U.object_select_box = 2; + + if (box_circle_lasso || box_lasso) { + U.object_select_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.object_select_circle = 2; + } + } + else if (U.object_cycle_touch) { + U.object_select_box = 0; + + if (box_circle_lasso || box_lasso) { + U.object_select_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.object_select_circle = 0; + } + } + } + else if (U.object_select_box == 2) { + if (U.object_cycle_touch) { + U.object_select_box = 0; + + if (box_circle_lasso || box_lasso) { + U.object_select_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.object_select_circle = 0; + } + } + else if (U.object_cycle_enclose) { + U.object_select_box = 1; + + if (box_circle_lasso || box_lasso) { + U.object_select_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.object_select_circle = 1; + } + } + } + else { + if (U.object_cycle_enclose) { + U.object_select_box = 1; + + if (box_circle_lasso || box_lasso) { + U.object_select_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.object_select_circle = 1; + } + } + else if (U.object_cycle_origin) { + U.object_select_box = 2; + + if (box_circle_lasso || box_lasso) { + U.object_select_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.object_select_circle = 2; + } + } + } + } + else if (lasso) { + if (U.object_select_lasso == 1) { + if (U.object_cycle_origin) { + U.object_select_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.object_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_circle = 2; + } + } + else if (U.object_cycle_touch) { + U.object_select_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.object_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_circle = 0; + } + } + } + else if (U.object_select_lasso == 2) { + if (U.object_cycle_touch) { + U.object_select_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.object_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_circle = 0; + } + } + else if (U.object_cycle_enclose) { + U.object_select_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.object_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_circle = 1; + } + } + } + else { + if (U.object_cycle_enclose) { + U.object_select_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.object_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_circle = 1; + } + } + else if (U.object_cycle_origin) { + U.object_select_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.object_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_circle = 2; + } + } + } + } + else { + if (U.object_select_circle == 1) { + if (U.object_cycle_origin) { + U.object_select_circle = 2; + + if (box_circle_lasso || box_circle) { + U.object_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_lasso = 2; + } + } + else if (U.object_cycle_touch) { + U.object_select_circle = 0; + + if (box_circle_lasso || box_circle) { + U.object_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_lasso = 0; + } + } + } + else if (U.object_select_circle == 2) { + if (U.object_cycle_touch) { + U.object_select_circle = 0; + + if (box_circle_lasso || box_circle) { + U.object_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_lasso = 0; + } + } + else if (U.object_cycle_enclose) { + U.object_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.object_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_lasso = 1; + } + } + } + else { + if (U.object_cycle_enclose) { + U.object_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.object_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_lasso = 1; + } + } + else if (U.object_cycle_origin) { + U.object_select_circle = 2; + + if (box_circle_lasso || box_circle) { + U.object_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.object_select_lasso = 2; + } + } + } + } + } + else if (lasso && lasso_op || circle_lasso) { + if (lasso) { + if (U.object_select_lasso == 1) { + if (U.object_cycle_origin_lasso) { + U.object_select_lasso = 2; + + if (circle_lasso) { + U.object_select_circle = 2; + } + } + else if (U.object_cycle_touch_lasso) { + U.object_select_lasso = 0; + + if (circle_lasso) { + U.object_select_circle = 0; + } + } + } + else if (U.object_select_lasso == 2) { + if (U.object_cycle_touch_lasso) { + U.object_select_lasso = 0; + + if (circle_lasso) { + U.object_select_circle = 0; + } + } + else if (U.object_cycle_enclose_lasso) { + U.object_select_lasso = 1; + + if (circle_lasso) { + U.object_select_circle = 1; + } + } + } + else { + if (U.object_cycle_enclose_lasso) { + U.object_select_lasso = 1; + + if (circle_lasso) { + U.object_select_circle = 1; + } + } + else if (U.object_cycle_origin_lasso) { + U.object_select_lasso = 2; + + if (circle_lasso) { + U.object_select_circle = 2; + } + } + } + } + else { + if (U.object_select_circle == 1) { + if (U.object_cycle_origin_lasso) { + U.object_select_circle = 2; + + if (circle_lasso) { + U.object_select_lasso = 2; + } + } + else if (U.object_cycle_touch_lasso) { + U.object_select_circle = 0; + + if (circle_lasso) { + U.object_select_lasso = 0; + } + } + } + else if (U.object_select_circle == 2) { + if (U.object_cycle_touch_lasso) { + U.object_select_circle = 0; + + if (circle_lasso) { + U.object_select_lasso = 0; + } + } + else if (U.object_cycle_enclose_lasso) { + U.object_select_circle = 1; + + if (circle_lasso) { + U.object_select_lasso = 1; + } + } + } + else { + if (U.object_cycle_enclose_lasso) { + U.object_select_circle = 1; + + if (circle_lasso) { + U.object_select_lasso = 1; + } + } + else if (U.object_cycle_origin_lasso) { + U.object_select_circle = 2; + + if (circle_lasso) { + U.object_select_lasso = 2; + } + } + } + } + } + else if (circle) { + if (U.object_select_circle == 1) { + if (U.object_cycle_origin_circle) { + U.object_select_circle = 2; + } + else if (U.object_cycle_touch_circle) { + U.object_select_circle = 0; + } + } + else if (U.object_select_circle == 2) { + if (U.object_cycle_touch_circle) { + U.object_select_circle = 0; + } + else if (U.object_cycle_enclose_circle) { + U.object_select_circle = 1; + } + } + else { + if (U.object_cycle_enclose_circle) { + U.object_select_circle = 1; + } + else if (U.object_cycle_origin_circle) { + U.object_select_circle = 2; + } + } + } + } + else { + if (U.object_select_box == 1) { + if (U.object_cycle_origin) { + U.object_select_box = 2; + } + else if (U.object_cycle_touch) { + U.object_select_box = 0; + } + } + else if (U.object_select_box == 2) { + if (U.object_cycle_touch) { + U.object_select_box = 0; + } + else if (U.object_cycle_enclose) { + U.object_select_box = 1; + } + } + else { + if (U.object_cycle_enclose) { + U.object_select_box = 1; + } + else if (U.object_cycle_origin) { + U.object_select_box = 2; + } + } + } + ED_area_tag_redraw(area); + } + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_cycle_object(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cycle Object"; + ot->idname = "VIEW3D_OT_cycle_object"; + ot->description = "Change to the next object selection mode with box, lasso, and circle select"; + /* api callbacks */ + ot->exec = cycle_object_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cycle Face Mode + * \{ */ + +static int cycle_face_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + bToolRef *tref = area->runtime.tool; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1) { + const bool box_op = U.drag_direction_box == 0 || + U.drag_direction_mode == 1 && !U.direction_face; + const bool lasso_op = U.drag_direction_lasso == 0 || + U.drag_direction_mode == 1 && !U.direction_face; + const bool box = STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box"); + const bool lasso = STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso"); + const bool circle = STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle"); + const bool box_circle_lasso = (box || circle || lasso) && U.sync_box && box_op && + U.sync_circle && U.sync_lasso && lasso_op; + const bool box_lasso = (box || lasso) && lasso_op && U.sync_lasso && box_op && U.sync_box; + const bool box_circle = (box || circle) && U.sync_circle && box_op && U.sync_box; + const bool circle_lasso = (lasso || circle) && U.sync_circle && lasso_op && U.sync_lasso; + + if (box && box_op || box_circle_lasso || box_lasso || box_circle) { + if (box) { + if (U.face_select_box == 1) { + if (U.face_cycle_enclose) { + U.face_select_box = 2; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 2; + } + } + else if (U.face_cycle_center) { + U.face_select_box = 3; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 3; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 3; + } + } + else if (U.face_cycle_default) { + U.face_select_box = 0; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 0; + } + } + } + else if (U.face_select_box == 2) { + if (U.face_cycle_center) { + U.face_select_box = 3; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 3; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 3; + } + } + else if (U.face_cycle_default) { + U.face_select_box = 0; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 0; + } + } + else if (U.face_cycle_touch) { + U.face_select_box = 1; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 1; + } + } + } + else if (U.face_select_box == 3) { + if (U.face_cycle_default) { + U.face_select_box = 0; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 0; + } + } + else if (U.face_cycle_touch) { + U.face_select_box = 1; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 1; + } + } + else if (U.face_cycle_enclose) { + U.face_select_box = 2; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 2; + } + } + } + else { + if (U.face_cycle_touch) { + U.face_select_box = 1; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 1; + } + } + else if (U.face_cycle_enclose) { + U.face_select_box = 2; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 2; + } + } + else if (U.face_cycle_center) { + U.face_select_box = 3; + + if (box_circle_lasso || box_lasso) { + U.face_select_lasso = 3; + } + + if (box_circle_lasso || box_circle) { + U.face_select_circle = 3; + } + } + } + } + else if (lasso) { + if (U.face_select_lasso == 1) { + if (U.face_cycle_enclose) { + U.face_select_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 2; + } + } + else if (U.face_cycle_center) { + U.face_select_lasso = 3; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 3; + } + } + else if (U.face_cycle_default) { + U.face_select_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 0; + } + } + } + else if (U.face_select_lasso == 2) { + if (U.face_cycle_center) { + U.face_select_lasso = 3; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 3; + } + } + else if (U.face_cycle_default) { + U.face_select_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 0; + } + } + else if (U.face_cycle_touch) { + U.face_select_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 1; + } + } + } + else if (U.face_select_lasso == 3) { + if (U.face_cycle_default) { + U.face_select_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 0; + } + } + else if (U.face_cycle_touch) { + U.face_select_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 1; + } + } + else if (U.face_cycle_enclose) { + U.face_select_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 2; + } + } + } + else { + if (U.face_cycle_touch) { + U.face_select_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 1; + } + } + else if (U.face_cycle_enclose) { + U.face_select_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 2; + } + } + else if (U.face_cycle_center) { + U.face_select_lasso = 3; + + if (box_circle_lasso || box_lasso) { + U.face_select_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_circle = 3; + } + } + } + } + else { + if (U.face_select_circle == 1) { + if (U.face_cycle_enclose) { + U.face_select_circle = 2; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 2; + } + } + else if (U.face_cycle_center) { + U.face_select_circle = 3; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 3; + } + } + else if (U.face_cycle_default) { + U.face_select_circle = 0; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 0; + } + } + } + else if (U.face_select_circle == 2) { + if (U.face_cycle_center) { + U.face_select_circle = 3; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 3; + } + } + else if (U.face_cycle_default) { + U.face_select_circle = 0; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 0; + } + } + else if (U.face_cycle_touch) { + U.face_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 1; + } + } + } + else if (U.face_select_circle == 3) { + if (U.face_cycle_default) { + U.face_select_circle = 0; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 0; + } + } + else if (U.face_cycle_touch) { + U.face_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 1; + } + } + else if (U.face_cycle_enclose) { + U.face_select_circle = 2; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 2; + } + } + } + else { + if (U.face_cycle_touch) { + U.face_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 1; + } + } + else if (U.face_cycle_enclose) { + U.face_select_circle = 2; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 2; + } + } + else if (U.face_cycle_center) { + U.face_select_circle = 3; + + if (box_circle_lasso || box_circle) { + U.face_select_box = 3; + } + + if (box_circle_lasso || circle_lasso) { + U.face_select_lasso = 3; + } + } + } + } + } + else if (lasso && lasso_op || circle_lasso) { + if (lasso) { + if (U.face_select_lasso == 1) { + if (U.face_cycle_enclose_lasso) { + U.face_select_lasso = 2; + + if (circle_lasso) { + U.face_select_circle = 2; + } + } + else if (U.face_cycle_center_lasso) { + U.face_select_lasso = 3; + + if (circle_lasso) { + U.face_select_circle = 3; + } + } + else if (U.face_cycle_default_lasso) { + U.face_select_lasso = 0; + + if (circle_lasso) { + U.face_select_circle = 0; + } + } + } + else if (U.face_select_lasso == 2) { + if (U.face_cycle_center_lasso) { + U.face_select_lasso = 3; + + if (circle_lasso) { + U.face_select_circle = 3; + } + } + else if (U.face_cycle_default_lasso) { + U.face_select_lasso = 0; + + if (circle_lasso) { + U.face_select_circle = 0; + } + } + else if (U.face_cycle_touch_lasso) { + U.face_select_lasso = 1; + + if (circle_lasso) { + U.face_select_circle = 1; + } + } + } + else if (U.face_select_lasso == 3) { + if (U.face_cycle_default_lasso) { + U.face_select_lasso = 0; + + if (circle_lasso) { + U.face_select_circle = 0; + } + } + else if (U.face_cycle_touch_lasso) { + U.face_select_lasso = 1; + + if (circle_lasso) { + U.face_select_circle = 1; + } + } + else if (U.face_cycle_enclose_lasso) { + U.face_select_lasso = 2; + + if (circle_lasso) { + U.face_select_circle = 2; + } + } + } + else { + if (U.face_cycle_touch_lasso) { + U.face_select_lasso = 1; + + if (circle_lasso) { + U.face_select_circle = 1; + } + } + else if (U.face_cycle_enclose_lasso) { + U.face_select_lasso = 2; + + if (circle_lasso) { + U.face_select_circle = 2; + } + } + else if (U.face_cycle_center_lasso) { + U.face_select_lasso = 3; + + if (circle_lasso) { + U.face_select_circle = 3; + } + } + } + } + else { + if (U.face_select_circle == 1) { + if (U.face_cycle_enclose_lasso) { + U.face_select_circle = 2; + + if (circle_lasso) { + U.face_select_lasso = 2; + } + } + else if (U.face_cycle_center_lasso) { + U.face_select_circle = 3; + + if (circle_lasso) { + U.face_select_lasso = 3; + } + } + else if (U.face_cycle_default_lasso) { + U.face_select_circle = 0; + + if (circle_lasso) { + U.face_select_lasso = 0; + } + } + } + else if (U.face_select_circle == 2) { + if (U.face_cycle_center_lasso) { + U.face_select_circle = 3; + + if (circle_lasso) { + U.face_select_lasso = 3; + } + } + else if (U.face_cycle_default_lasso) { + U.face_select_circle = 0; + + if (circle_lasso) { + U.face_select_lasso = 0; + } + } + else if (U.face_cycle_touch_lasso) { + U.face_select_circle = 1; + + if (circle_lasso) { + U.face_select_lasso = 1; + } + } + } + else if (U.face_select_circle == 3) { + if (U.face_cycle_default_lasso) { + U.face_select_circle = 0; + + if (circle_lasso) { + U.face_select_lasso = 0; + } + } + else if (U.face_cycle_touch_lasso) { + U.face_select_circle = 1; + + if (circle_lasso) { + U.face_select_lasso = 1; + } + } + else if (U.face_cycle_enclose_lasso) { + U.face_select_circle = 2; + + if (circle_lasso) { + U.face_select_lasso = 2; + } + } + } + else { + if (U.face_cycle_touch_lasso) { + U.face_select_circle = 1; + + if (circle_lasso) { + U.face_select_lasso = 1; + } + } + else if (U.face_cycle_enclose_lasso) { + U.face_select_circle = 2; + + if (circle_lasso) { + U.face_select_lasso = 2; + } + } + else if (U.face_cycle_center_lasso) { + U.face_select_circle = 3; + + if (circle_lasso) { + U.face_select_lasso = 3; + } + } + } + } + } + else if (circle) { + if (U.face_select_circle == 1) { + if (U.face_cycle_enclose_circle) { + U.face_select_circle = 2; + } + else if (U.face_cycle_center_circle) { + U.face_select_circle = 3; + } + else if (U.face_cycle_default_circle) { + U.face_select_circle = 0; + } + } + else if (U.face_select_circle == 2) { + if (U.face_cycle_center_circle) { + U.face_select_circle = 3; + } + else if (U.face_cycle_default_circle) { + U.face_select_circle = 0; + } + else if (U.face_cycle_touch_circle) { + U.face_select_circle = 1; + } + } + else if (U.face_select_circle == 3) { + if (U.face_cycle_default_circle) { + U.face_select_circle = 0; + } + else if (U.face_cycle_touch_circle) { + U.face_select_circle = 1; + } + else if (U.face_cycle_enclose_circle) { + U.face_select_circle = 2; + } + } + else { + if (U.face_cycle_touch_circle) { + U.face_select_circle = 1; + } + else if (U.face_cycle_enclose_circle) { + U.face_select_circle = 2; + } + else if (U.face_cycle_center_circle) { + U.face_select_circle = 3; + } + } + } + } + else { + if (U.face_select_box == 1) { + if (U.face_cycle_enclose) { + U.face_select_box = 2; + } + else if (U.face_cycle_center) { + U.face_select_box = 3; + } + else if (U.face_cycle_default) { + U.face_select_box = 0; + } + } + else if (U.face_select_box == 2) { + if (U.face_cycle_center) { + U.face_select_box = 3; + } + else if (U.face_cycle_default) { + U.face_select_box = 0; + } + else if (U.face_cycle_touch) { + U.face_select_box = 1; + } + } + else if (U.face_select_box == 3) { + if (U.face_cycle_default) { + U.face_select_box = 0; + } + else if (U.face_cycle_touch) { + U.face_select_box = 1; + } + else if (U.face_cycle_enclose) { + U.face_select_box = 2; + } + } + else { + if (U.face_cycle_touch) { + U.face_select_box = 1; + } + else if (U.face_cycle_enclose) { + U.face_select_box = 2; + } + else if (U.face_cycle_center) { + U.face_select_box = 3; + } + } + } + ED_area_tag_redraw(area); + } + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_cycle_face(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cycle Face"; + ot->idname = "VIEW3D_OT_cycle_face"; + ot->description = "Change to the next face selection mode with box, lasso, and circle select"; + /* api callbacks */ + ot->exec = cycle_face_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cycle Edge Mode + * \{ */ + +static int cycle_edge_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + bToolRef *tref = area->runtime.tool; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1) { + const bool box_op = U.drag_direction_box == 0 || + U.drag_direction_mode == 1 && !U.direction_edge; + const bool lasso_op = U.drag_direction_lasso == 0 || + U.drag_direction_mode == 1 && !U.direction_edge; + const bool box = STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box"); + const bool lasso = STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso"); + const bool circle = STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle"); + const bool box_circle_lasso = (box || circle || lasso) && U.sync_box && box_op && + U.sync_circle && U.sync_lasso && lasso_op; + const bool box_lasso = (box || lasso) && lasso_op && U.sync_lasso && box_op && U.sync_box; + const bool box_circle = (box || circle) && U.sync_circle && box_op && U.sync_box; + const bool circle_lasso = (lasso || circle) && U.sync_circle && lasso_op && U.sync_lasso; + + if (box && box_op || box_circle_lasso || box_lasso || box_circle) { + if (box) { + if (U.edge_select_box == 1) { + if (U.edge_cycle_enclose) { + U.edge_select_box = 2; + + if (box_circle_lasso || box_lasso) { + U.edge_select_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.edge_select_circle = 2; + } + } + else if (U.edge_cycle_default) { + U.edge_select_box = 0; + + if (box_circle_lasso || box_lasso) { + U.edge_select_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.edge_select_circle = 1; + } + } + } + else if (U.edge_select_box == 2) { + if (U.edge_cycle_default) { + U.edge_select_box = 0; + + if (box_circle_lasso || box_lasso) { + U.edge_select_lasso = 0; + } + + if (box_circle_lasso || box_circle) { + U.edge_select_circle = 1; + } + } + else if (U.edge_cycle_touch) { + U.edge_select_box = 1; + + if (box_circle_lasso || box_lasso) { + U.edge_select_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.edge_select_circle = 1; + } + } + } + else { + if (U.edge_cycle_touch) { + U.edge_select_box = 1; + + if (box_circle_lasso || box_lasso) { + U.edge_select_lasso = 1; + } + + if (box_circle_lasso || box_circle) { + U.edge_select_circle = 1; + } + } + else if (U.edge_cycle_enclose) { + U.edge_select_box = 2; + + if (box_circle_lasso || box_lasso) { + U.edge_select_lasso = 2; + } + + if (box_circle_lasso || box_circle) { + U.edge_select_circle = 2; + } + } + } + } + else if (lasso) { + if (U.edge_select_lasso == 1) { + if (U.edge_cycle_enclose) { + U.edge_select_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.edge_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_circle = 2; + } + } + else if (U.edge_cycle_default) { + U.edge_select_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.edge_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_circle = 1; + } + } + } + else if (U.edge_select_lasso == 2) { + if (U.edge_cycle_default) { + U.edge_select_lasso = 0; + + if (box_circle_lasso || box_lasso) { + U.edge_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_circle = 1; + } + } + else if (U.edge_cycle_touch) { + U.edge_select_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.edge_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_circle = 1; + } + } + } + else { + if (U.edge_cycle_touch) { + U.edge_select_lasso = 1; + + if (box_circle_lasso || box_lasso) { + U.edge_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_circle = 1; + } + } + else if (U.edge_cycle_enclose) { + U.edge_select_lasso = 2; + + if (box_circle_lasso || box_lasso) { + U.edge_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_circle = 2; + } + } + } + } + else { + if (U.edge_select_circle == 1) { + if (U.edge_cycle_enclose) { + U.edge_select_circle = 2; + + if (box_circle_lasso || box_circle) { + U.edge_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_lasso = 2; + } + } + else if (U.edge_cycle_default) { + U.edge_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.edge_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_lasso = 0; + } + } + } + else if (U.edge_select_circle == 2) { + if (U.edge_cycle_default) { + U.edge_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.edge_select_box = 0; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_lasso = 0; + } + } + else if (U.edge_cycle_touch) { + U.edge_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.edge_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_lasso = 1; + } + } + } + else { + if (U.edge_cycle_touch) { + U.edge_select_circle = 1; + + if (box_circle_lasso || box_circle) { + U.edge_select_box = 1; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_lasso = 1; + } + } + else if (U.edge_cycle_enclose) { + U.edge_select_circle = 2; + + if (box_circle_lasso || box_circle) { + U.edge_select_box = 2; + } + + if (box_circle_lasso || circle_lasso) { + U.edge_select_lasso = 2; + } + } + } + } + } + else if (lasso && lasso_op || circle_lasso) { + if (lasso) { + if (U.edge_select_lasso == 1) { + if (U.edge_cycle_enclose_lasso) { + U.edge_select_lasso = 2; + + if (circle_lasso) { + U.edge_select_circle = 2; + } + } + else if (U.edge_cycle_default_lasso) { + U.edge_select_lasso = 0; + + if (circle_lasso) { + U.edge_select_circle = 1; + } + } + } + else if (U.edge_select_lasso == 2) { + if (U.edge_cycle_default_lasso) { + U.edge_select_lasso = 0; + + if (circle_lasso) { + U.edge_select_circle = 1; + } + } + else if (U.edge_cycle_touch_lasso) { + U.edge_select_lasso = 1; + + if (circle_lasso) { + U.edge_select_circle = 1; + } + } + } + else { + if (U.edge_cycle_touch_lasso) { + U.edge_select_lasso = 1; + + if (circle_lasso) { + U.edge_select_circle = 1; + } + } + else if (U.edge_cycle_enclose_lasso) { + U.edge_select_lasso = 2; + + if (circle_lasso) { + U.edge_select_circle = 2; + } + } + } + } + else { + if (U.edge_select_circle == 1) { + if (U.edge_cycle_enclose_lasso) { + U.edge_select_circle = 2; + + if (circle_lasso) { + U.edge_select_lasso = 2; + } + } + else if (U.edge_cycle_default_lasso) { + U.edge_select_circle = 1; + + if (circle_lasso) { + U.edge_select_lasso = 0; + } + } + } + else if (U.edge_select_circle == 2) { + if (U.edge_cycle_default_lasso) { + U.edge_select_circle = 1; + + if (circle_lasso) { + U.edge_select_lasso = 0; + } + } + else if (U.edge_cycle_touch_lasso) { + U.edge_select_circle = 1; + + if (circle_lasso) { + U.edge_select_lasso = 1; + } + } + } + else { + if (U.edge_cycle_touch_lasso) { + U.edge_select_circle = 1; + + if (circle_lasso) { + U.edge_select_lasso = 1; + } + } + else if (U.edge_cycle_enclose_lasso) { + U.edge_select_circle = 2; + + if (circle_lasso) { + U.edge_select_lasso = 2; + } + } + } + } + } + else if (circle) { + if (U.edge_select_circle == 1) { + if (U.edge_cycle_enclose_circle) { + U.edge_select_circle = 2; + } + } + else if (U.edge_cycle_touch_circle) { + U.edge_select_circle = 1; + } + } + } + else { + if (U.edge_select_box == 1) { + if (U.edge_cycle_enclose) { + U.edge_select_box = 2; + } + else if (U.edge_cycle_default) { + U.edge_select_box = 0; + } + } + else if (U.edge_select_box == 2) { + if (U.edge_cycle_default) { + U.edge_select_box = 0; + } + else if (U.edge_cycle_touch) { + U.edge_select_box = 1; + } + } + else { + if (U.edge_cycle_touch) { + U.edge_select_box = 1; + } + else if (U.edge_cycle_enclose) { + U.edge_select_box = 2; + } + } + } + ED_area_tag_redraw(area); + } + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_cycle_edge(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cycle Edge"; + ot->idname = "VIEW3D_OT_cycle_edge"; + ot->description = "Change to the next edge selection mode with box, lasso, and circle select"; + /* api callbacks */ + ot->exec = cycle_edge_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 695025cf726..59210e490ea 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -61,6 +61,14 @@ void VIEW3D_OT_render_border(struct wmOperatorType *ot); void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot); void VIEW3D_OT_toggle_shading(struct wmOperatorType *ot); void VIEW3D_OT_toggle_xray(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_auto_xray(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_select_through(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_backface(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_facedots(struct wmOperatorType *ot); +void VIEW3D_OT_cycle_shading(struct wmOperatorType *ot); +void VIEW3D_OT_cycle_object(struct wmOperatorType *ot); +void VIEW3D_OT_cycle_face(struct wmOperatorType *ot); +void VIEW3D_OT_cycle_edge(struct wmOperatorType *ot); /* `view3d_draw.cc` */ diff --git a/source/blender/editors/space_view3d/view3d_navigate.cc b/source/blender/editors/space_view3d/view3d_navigate.cc index dca57939fc0..6e9c740174f 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.cc +++ b/source/blender/editors/space_view3d/view3d_navigate.cc @@ -663,6 +663,25 @@ void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPro if (flag & V3D_OP_PROP_USE_MOUSE_INIT) { WM_operator_properties_use_cursor_init(ot); } + + if (flag & V3D_OP_PROP_FRAME_SELECTED_DISTANCE) { + PropertyRNA *prop; + prop = RNA_def_int(ot->srna, + "frame_selected_distance", + 0, + 0, + 1000, + "Camera Offset", + "Distance to put camera from selected", + 0, + 1000); + } + + if (flag & V3D_OP_PROP_ZOOM_SPEED) { + PropertyRNA *prop; + prop = RNA_def_float( + ot->srna, "zoom_speed", 1.2f, 1.001, 10, "Zoom Speed", "Speed to zoom camera", 1.001f, 10); + } } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate.hh b/source/blender/editors/space_view3d/view3d_navigate.hh index 2318dd546cd..ea5ea8b3f7f 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.hh +++ b/source/blender/editors/space_view3d/view3d_navigate.hh @@ -41,6 +41,8 @@ enum eV3D_OpPropFlag { V3D_OP_PROP_DELTA = (1 << 1), V3D_OP_PROP_USE_ALL_REGIONS = (1 << 2), V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3), + V3D_OP_PROP_FRAME_SELECTED_DISTANCE = (1 << 4), + V3D_OP_PROP_ZOOM_SPEED = (1 << 5), }; ENUM_OPERATORS(eV3D_OpPropFlag, V3D_OP_PROP_USE_MOUSE_INIT); diff --git a/source/blender/editors/space_view3d/view3d_navigate_view_all.cc b/source/blender/editors/space_view3d/view3d_navigate_view_all.cc index e357332d4d3..73baba9cb55 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_view_all.cc +++ b/source/blender/editors/space_view3d/view3d_navigate_view_all.cc @@ -91,7 +91,8 @@ static void view3d_from_minmax(bContext *C, const float min[3], const float max[3], bool ok_dist, - const int smooth_viewtx) + const int smooth_viewtx, + const int view_dist) { RegionView3D *rv3d = static_cast(region->regiondata); float afm[3]; @@ -134,7 +135,7 @@ static void view3d_from_minmax(bContext *C, v3d, region, depsgraph, persp, true, (size / 2) * VIEW3D_MARGIN); if (rv3d->is_persp) { /* don't zoom closer than the near clipping plane */ - new_dist = max_ff(new_dist, v3d->clip_start * 1.5f); + new_dist = max_ff(new_dist, v3d->clip_start * 1.5f + view_dist); } } } @@ -166,7 +167,8 @@ static void view3d_from_minmax_multi(bContext *C, const float min[3], const float max[3], const bool ok_dist, - const int smooth_viewtx) + const int smooth_viewtx, + const int view_dist) { ScrArea *area = CTX_wm_area(C); LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { @@ -175,7 +177,7 @@ static void view3d_from_minmax_multi(bContext *C, /* when using all regions, don't jump out of camera view, * but _do_ allow locked cameras to be moved */ if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx, view_dist); } } } @@ -263,10 +265,10 @@ static int view3d_all_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); + view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx, 0); } else { - view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx, 0); } ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); @@ -328,6 +330,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) /* any one of the regions may be locked */ (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA)); const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + const int view_dist = RNA_int_get(op->ptr, "frame_selected_distance"); INIT_MINMAX(min, max); if (is_face_map) { @@ -454,10 +457,10 @@ static int viewselected_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx, view_dist); } else { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx, view_dist); } ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); @@ -481,6 +484,7 @@ void VIEW3D_OT_view_selected(wmOperatorType *ot) /* properties */ view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS); + view3d_operator_properties_common(ot, V3D_OP_PROP_FRAME_SELECTED_DISTANCE); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_view_zoom.cc b/source/blender/editors/space_view3d/view3d_navigate_view_zoom.cc index ff4feb5da89..207f9666cf0 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_view_zoom.cc +++ b/source/blender/editors/space_view3d/view3d_navigate_view_zoom.cc @@ -389,7 +389,8 @@ static void view_zoom_apply_step(bContext *C, ScrArea *area, ARegion *region, const int delta, - const int zoom_xy[2]) + const int zoom_xy[2], + const float zoom_speed) { View3D *v3d = static_cast(area->spacedata.first); RegionView3D *rv3d = static_cast(region->regiondata); @@ -402,7 +403,8 @@ static void view_zoom_apply_step(bContext *C, ED_view3d_dist_range_get(v3d, dist_range); if (delta < 0) { - const float step = 1.2f; + const float step = zoom_speed; + if (use_cam_zoom) { view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); } @@ -413,7 +415,7 @@ static void view_zoom_apply_step(bContext *C, } } else { - const float step = 1.0f / 1.2f; + const float step = 1.0f / zoom_speed; if (use_cam_zoom) { view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); } @@ -446,6 +448,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) RegionView3D *rv3d = static_cast(region->regiondata); const int delta = RNA_int_get(op->ptr, "delta"); + const float zoom_speed = RNA_float_get(op->ptr, "zoom_speed"); const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); int zoom_xy_buf[2]; @@ -459,7 +462,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) zoom_xy = zoom_xy_buf; } - view_zoom_apply_step(C, depsgraph, scene, area, region, delta, zoom_xy); + view_zoom_apply_step(C, depsgraph, scene, area, region, delta, zoom_xy, zoom_speed); ED_view3d_camera_lock_undo_grouped_push(op->type->name, v3d, rv3d, C); return OPERATOR_FINISHED; @@ -481,6 +484,7 @@ static int viewzoom_invoke_impl(bContext *C, prop = RNA_struct_find_property(ptr, "delta"); const int delta = RNA_property_is_set(ptr, prop) ? RNA_property_int_get(ptr, prop) : 0; + const float zoom_speed = RNA_float_get(ptr, "zoom_speed"); if (delta) { const bool do_zoom_to_mouse_pos = (vod->viewops_flag & VIEWOPS_FLAG_ZOOM_TO_MOUSE) != 0; @@ -490,7 +494,8 @@ static int viewzoom_invoke_impl(bContext *C, vod->area, vod->region, delta, - do_zoom_to_mouse_pos ? xy : nullptr); + do_zoom_to_mouse_pos ? xy : nullptr, + zoom_speed); return OPERATOR_FINISHED; } @@ -545,8 +550,9 @@ void VIEW3D_OT_zoom(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; /* properties */ - view3d_operator_properties_common( - ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT); + view3d_operator_properties_common(ot, + V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | + V3D_OP_PROP_USE_MOUSE_INIT | V3D_OP_PROP_ZOOM_SPEED); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_ops.cc b/source/blender/editors/space_view3d/view3d_ops.cc index 1bc6e5c0aee..698f1d37532 100644 --- a/source/blender/editors/space_view3d/view3d_ops.cc +++ b/source/blender/editors/space_view3d/view3d_ops.cc @@ -223,6 +223,14 @@ void view3d_operatortypes() WM_operatortype_append(VIEW3D_OT_toggle_shading); WM_operatortype_append(VIEW3D_OT_toggle_xray); WM_operatortype_append(VIEW3D_OT_toggle_matcap_flip); + WM_operatortype_append(VIEW3D_OT_toggle_auto_xray); + WM_operatortype_append(VIEW3D_OT_toggle_select_through); + WM_operatortype_append(VIEW3D_OT_toggle_backface); + WM_operatortype_append(VIEW3D_OT_toggle_facedots); + WM_operatortype_append(VIEW3D_OT_cycle_shading); + WM_operatortype_append(VIEW3D_OT_cycle_object); + WM_operatortype_append(VIEW3D_OT_cycle_face); + WM_operatortype_append(VIEW3D_OT_cycle_edge); WM_operatortype_append(VIEW3D_OT_ruler_add); WM_operatortype_append(VIEW3D_OT_ruler_remove); diff --git a/source/blender/editors/space_view3d/view3d_select.cc b/source/blender/editors/space_view3d/view3d_select.cc index 2ab147bb96e..d0392e2d81a 100644 --- a/source/blender/editors/space_view3d/view3d_select.cc +++ b/source/blender/editors/space_view3d/view3d_select.cc @@ -30,6 +30,7 @@ #include "BLI_listbase.h" #include "BLI_math_bits.h" #include "BLI_math_geom.h" +#include "BLI_math_matrix.h" #include "BLI_rect.h" #include "BLI_span.hh" #include "BLI_string.h" @@ -107,6 +108,7 @@ // #include "BLI_time_utildefines.h" using blender::Vector; +using namespace blender; /* -------------------------------------------------------------------- */ /** \name Public Utilities @@ -114,7 +116,14 @@ using blender::Vector; float ED_view3d_select_dist_px() { - return 75.0f * U.pixelsize; + const float radius = U.selection_radius == 0 ? 0.1f : (float)U.selection_radius; + + if (U.adjustable_click_select == 1) { + return radius * U.pixelsize; + } + else { + return 75.0f * U.pixelsize; + } } ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph) @@ -154,6 +163,347 @@ void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact) /** \name Internal Object Utilities * \{ */ +/* helper also for box_select and lasso */ +static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2]) +{ + return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2); +} + +static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2]) +{ + int d1, d2, d3, d4; + + /* check points in rect */ + if (edge_fully_inside_rect(rect, v1, v2)) { + return true; + } + + /* check points completely out rect */ + if (v1[0] < rect->xmin && v2[0] < rect->xmin) { + return false; + } + if (v1[0] > rect->xmax && v2[0] > rect->xmax) { + return false; + } + if (v1[1] < rect->ymin && v2[1] < rect->ymin) { + return false; + } + if (v1[1] > rect->ymax && v2[1] > rect->ymax) { + return false; + } + + /* simple check lines intersecting. */ + d1 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymin); + d2 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymax); + d3 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymax); + d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin); + + if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) { + return false; + } + if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) { + return false; + } + + return true; +} + +const bool object_filter(ViewContext *vc, + Base *base, + const int style, + const int mcoords[][2], + const int mcoords_len, + rctf *rectf, + const int circle_data[3], + const bool square_select) +{ + Mesh *me = BKE_object_to_mesh(nullptr, base->object, false); + ED_view3d_init_mats_rv3d(base->object, vc->rv3d); + float radius_squared, point[2], v1co[2], v2co[2]; + const bool lasso = mcoords != nullptr; + const bool box = rectf != nullptr && !square_select; + const bool square = rectf != nullptr && square_select; + const bool circle = circle_data != nullptr; + bool in_bounds = false; + bool touch_bounds = false; + bool inside = false; + + if (lasso) { + point[0] = static_cast(mcoords[0][0]); + point[1] = static_cast(mcoords[0][1]); + } + else if (box || square) { + point[0] = rectf->xmin; + point[1] = rectf->ymin; + } + else if (circle) { + radius_squared = float(circle_data[2]) * float(circle_data[2]); + point[0] = float(circle_data[0]); + point[1] = float(circle_data[1]); + } + + /* bounds check for touch & non-circle enclose */ + if (style == 1 || box || lasso) { + const std::optional> bounds = me->bounds_min_max(); + float boundsv2[4][2] = {}; + float boundsv3[8][3] = {bounds->min[0], bounds->min[1], bounds->min[2], bounds->min[0], + bounds->min[1], bounds->max[2], bounds->min[0], bounds->max[1], + bounds->min[2], bounds->min[0], bounds->max[1], bounds->max[2], + bounds->max[0], bounds->max[1], bounds->min[2], bounds->max[0], + bounds->max[1], bounds->max[2], bounds->max[0], bounds->min[1], + bounds->min[2], bounds->max[0], bounds->min[1], bounds->max[2]}; + ED_view3d_project_float_object( + vc->region, boundsv3[0], v1co, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + boundsv2[0][0] = boundsv2[1][0] = boundsv2[2][0] = boundsv2[3][0] = v1co[0]; + boundsv2[0][1] = boundsv2[1][1] = boundsv2[2][1] = boundsv2[3][1] = v1co[1]; + + for (int i = 1; i < 8; i++) { + ED_view3d_project_float_object( + vc->region, boundsv3[i], v1co, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + if (v1co[0] < boundsv2[0][0]) { + boundsv2[0][0] = boundsv2[1][0] = v1co[0]; + } + else if (v1co[0] > boundsv2[2][0]) { + boundsv2[2][0] = boundsv2[3][0] = v1co[0]; + } + if (v1co[1] < boundsv2[0][1]) { + boundsv2[0][1] = boundsv2[3][1] = v1co[1]; + } + else if (v1co[1] > boundsv2[1][1]) { + boundsv2[1][1] = boundsv2[2][1] = v1co[1]; + } + } + + if (style == 1) { + in_bounds = isect_point_poly_v2(point, boundsv2, 4); + + for (int i = 0; i < 4; i++) { + if (lasso) { + if (i == 3) { + touch_bounds = BLI_lasso_is_edge_inside(mcoords, + mcoords_len, + (int)boundsv2[3][0], + (int)boundsv2[3][1], + (int)boundsv2[0][0], + (int)boundsv2[0][1], + IS_CLIPPED, + false); + } + else { + touch_bounds = BLI_lasso_is_edge_inside(mcoords, + mcoords_len, + (int)boundsv2[i][0], + (int)boundsv2[i][1], + (int)boundsv2[i + 1][0], + (int)boundsv2[i + 1][1], + IS_CLIPPED, + false); + } + } + else if (box || square) { + if (i == 3) { + touch_bounds = edge_inside_rect(rectf, boundsv2[3], boundsv2[0]); + } + else { + touch_bounds = edge_inside_rect(rectf, boundsv2[i], boundsv2[i + 1]); + } + } + else if (circle) { + if (i == 3) { + touch_bounds = edge_inside_circle(point, circle_data[2], boundsv2[3], boundsv2[0]); + } + else { + touch_bounds = edge_inside_circle(point, circle_data[2], boundsv2[i], boundsv2[i + 1]); + } + } + if (touch_bounds) { + break; + } + } + + if (!in_bounds && !touch_bounds) { + return false; + } + } + else { + for (int i = 0; i < 4; i++) { + if (lasso) { + if (i == 3) { + inside = BLI_lasso_is_edge_inside(mcoords, + mcoords_len, + (int)boundsv2[3][0], + (int)boundsv2[3][1], + (int)boundsv2[0][0], + (int)boundsv2[0][1], + IS_CLIPPED, + true); + } + else { + inside = BLI_lasso_is_edge_inside(mcoords, + mcoords_len, + (int)boundsv2[i][0], + (int)boundsv2[i][1], + (int)boundsv2[i + 1][0], + (int)boundsv2[i + 1][1], + IS_CLIPPED, + true); + } + } + else if (box) { + if (i == 3) { + inside = edge_fully_inside_rect(rectf, boundsv2[3], boundsv2[0]); + } + else { + inside = edge_fully_inside_rect(rectf, boundsv2[i], boundsv2[i + 1]); + } + } + if (!inside) { + break; + } + } + if (inside) { + return true; + } + } + } + + /* touch */ + const Span positions = me->vert_positions(); + if (style == 1) { + for (int i = 0; i < me->verts_num; i++) { + ED_view3d_project_float_object( + vc->region, positions[i], v1co, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + + if (lasso) { + inside = BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(v1co[0]), + int(v1co[1]), + /* Dummy value. */ + INT_MAX); + } + else if (box || square) { + inside = BLI_rctf_isect_pt_v(rectf, v1co); + } + else if (circle) { + inside = len_squared_v2v2(point, v1co) <= radius_squared; + } + if (inside) { + return true; + } + } + + const blender::Span edges = me->edges(); + for (int i = 0; i < me->edges_num; i++) { + ED_view3d_project_float_object(vc->region, + positions[edges[i][0]], + v1co, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + ED_view3d_project_float_object(vc->region, + positions[edges[i][1]], + v2co, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + if (lasso) { + inside = BLI_lasso_is_edge_inside(mcoords, + mcoords_len, + (int)v1co[0], + (int)v1co[1], + (int)v2co[0], + (int)v2co[1], + IS_CLIPPED, + false); + } + else if (box || square) { + inside = edge_inside_rect(rectf, v1co, v2co); + } + else if (circle) { + inside = edge_inside_circle(point, circle_data[2], v1co, v2co); + } + if (inside) { + break; + } + } + /* skip checks that can't succeed after vert & edge failure */ + if (!inside && !touch_bounds && in_bounds) { + const blender::OffsetIndices faces = me->faces(); + const Span corner_verts = me->corner_verts(); + + for (const int i : faces.index_range()) { + const Span polyverts = corner_verts.slice(faces[i]); + const int size = polyverts.size(); + + if (size == 3) { + float tri[3][2] = {}; + for (int i = 0; i < 3; i++) { + ED_view3d_project_float_object(vc->region, + positions[polyverts[i]], + v1co, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + tri[i][0] = v1co[0]; + tri[i][1] = v1co[1]; + } + inside = isect_point_poly_v2(point, tri, 3); + } + else if (size == 4) { + float quad[4][2] = {}; + for (int i = 0; i < 4; i++) { + ED_view3d_project_float_object(vc->region, + positions[polyverts[i]], + v1co, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + quad[i][0] = v1co[0]; + quad[i][1] = v1co[1]; + } + inside = isect_point_poly_v2(point, quad, 4); + } + else { + float(*ngon)[2] = static_cast( + MEM_mallocN(sizeof(float) * 2 * size, __func__)); + for (int i = 0; i < size; i++) { + ED_view3d_project_float_object(vc->region, + positions[polyverts[i]], + v1co, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + ngon[i][0] = v1co[0]; + ngon[i][1] = v1co[1]; + } + inside = isect_point_poly_v2(point, ngon, size); + MEM_freeN(ngon); + } + if (inside) { + break; + } + } + } + } + /* enclose */ + else { + for (int i = 0; i < me->verts_num; i++) { + ED_view3d_project_float_object( + vc->region, positions[i], v1co, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + + if (lasso) { + inside = BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(v1co[0]), + int(v1co[1]), + /* Dummy value. */ + INT_MAX); + } + else if (box || square) { + inside = BLI_rctf_isect_pt_v(rectf, v1co); + } + else if (circle) { + inside = len_squared_v2v2(point, v1co) <= radius_squared; + } + if (!inside) { + break; + } + } + } + return inside; +} + static bool object_deselect_all_visible(const Scene *scene, ViewLayer *view_layer, View3D *v3d) { bool changed = false; @@ -248,16 +598,828 @@ static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *w /** \name Internal Edit-Mesh Utilities * \{ */ -static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, - Depsgraph *depsgraph, - Object *ob, - BMEditMesh *em, - const eSelectOp sel_op) +struct BoxSelectUserData { + ViewContext *vc; + const rcti *rect; + const rctf *rect_fl; + rctf _rect_fl; + eSelectOp sel_op; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + blender::BitVector<> visited_face; + blender::BitVector<> front_face; + + /* runtime */ + bool is_done; + bool is_changed; +}; + +struct LassoSelectUserData { + ViewContext *vc; + const rcti *rect; + const rctf *rect_fl; + rctf _rect_fl; + const int (*mcoords)[2]; + int mcoords_len; + eSelectOp sel_op; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + blender::BitVector<> visited_face; + blender::BitVector<> front_face; + + /* runtime */ + int pass; + bool is_done; + bool is_changed; +}; + +struct CircleSelectUserData { + ViewContext *vc; + bool select; + int mval[2]; + float mval_fl[2]; + float radius; + float radius_squared; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + blender::BitVector<> visited_face; + blender::BitVector<> front_face; + + /* runtime */ + bool is_changed; +}; + +void edbm_vert_orientation(const BMVert *eve, float normal[3], float plane[3], float meshmat[3][3]) +{ + float vec[3] = {0.0, 0.0, 0.0}; + float tangent[3] = {0.0f, 0.0f, 1.0f}; + copy_v3_v3(normal, eve->no); + + if (eve->e) { + float vert1v3[3] = {eve->e->v1->co[0], eve->e->v1->co[1], eve->e->v1->co[2]}; + float vert2v3[3] = {eve->e->v2->co[0], eve->e->v2->co[1], eve->e->v2->co[2]}; + sub_v3_v3v3(plane, vert2v3, vert1v3); + } + else { + if (eve->no[0] < 0.5f) { + vec[0] = 1.0f; + } + else if (eve->no[1] < 0.5f) { + vec[1] = 1.0f; + } + else { + vec[2] = 1.0f; + } + cross_v3_v3v3(plane, eve->no, vec); + } + normalize_v3(plane); + copy_v3_v3(meshmat[2], normal); + cross_v3_v3v3(meshmat[0], meshmat[2], tangent); + + if (is_zero_v3(meshmat[0])) { + tangent[0] = 1.0f; + tangent[1] = tangent[2] = 0.0f; + cross_v3_v3v3(meshmat[0], tangent, meshmat[2]); + } + cross_v3_v3v3(meshmat[1], meshmat[2], meshmat[0]); +} + +void edbm_edge_orientation(const BMEdge *eed, float normal[3], float plane[3], float meshmat[3][3]) +{ + float eed_plane[3], vec[3]; + add_v3_v3v3(normal, eed->v1->no, eed->v2->no); + sub_v3_v3v3(eed_plane, eed->v2->co, eed->v1->co); + cross_v3_v3v3(vec, normal, eed_plane); + cross_v3_v3v3(normal, eed_plane, vec); + normalize_v3(normal); + + if (plane != nullptr && meshmat != nullptr) { + if (BM_edge_is_boundary(eed)) { + sub_v3_v3v3(plane, eed->l->v->co, eed->l->next->v->co); + } + else { + if (eed->v2->co[1] > eed->v1->co[1]) { + sub_v3_v3v3(plane, eed->v2->co, eed->v1->co); + } + else { + sub_v3_v3v3(plane, eed->v1->co, eed->v2->co); + } + } + normalize_v3(plane); + } +} + +bool edbm_mesh_facing_viewport(ViewContext *vc, + struct BMFace *efa, + struct BMEdge *eed, + struct BMVert *eve) +{ + ToolSettings *ts = vc->scene->toolsettings; + const bool face = efa != nullptr; + const bool edge = eed != nullptr; + const bool vert = eve != nullptr; + const bool persp = vc->rv3d->is_persp; + float view[3], mesh_co[3], mesh_no[3]; + bool mesh_facing = false; + + if (persp) { + if (face) { + BMIter iter; + BMVert *v; + int vcount = 0; + mesh_no[0] = efa->no[0]; + mesh_no[1] = efa->no[1]; + mesh_no[2] = efa->no[2]; + + if (efa->len == 3) { + float tri[3][3]; + BM_ITER_ELEM (v, &iter, efa, BM_VERTS_OF_FACE) { + tri[vcount][0] = v->co[0]; + tri[vcount][1] = v->co[1]; + tri[vcount][2] = v->co[2]; + vcount++; + } + mid_v3_v3v3v3(mesh_co, tri[0], tri[1], tri[2]); + } + else if (efa->len == 4) { + float quad[4][3]; + BM_ITER_ELEM (v, &iter, efa, BM_VERTS_OF_FACE) { + quad[vcount][0] = v->co[0]; + quad[vcount][1] = v->co[1]; + quad[vcount][2] = v->co[2]; + vcount++; + } + mid_v3_v3v3v3v3(mesh_co, quad[0], quad[1], quad[2], quad[3]); + } + else { + float(*ngon)[3] = static_cast( + MEM_mallocN(sizeof(float) * 3 * efa->len, __func__)); + BM_ITER_ELEM (v, &iter, efa, BM_VERTS_OF_FACE) { + ngon[vcount][0] = v->co[0]; + ngon[vcount][1] = v->co[1]; + ngon[vcount][2] = v->co[2]; + vcount++; + } + mid_v3_v3_array(mesh_co, ngon, efa->len); + MEM_freeN(ngon); + } + } + else if (edge) { + edbm_edge_orientation(eed, mesh_no, nullptr, nullptr); + mid_v3_v3v3(mesh_co, eed->v1->co, eed->v2->co); + } + else if (vert) { + for (int i = 0; i < 3; i++) { + mesh_no[i] = eve->no[i]; + mesh_co[i] = eve->co[i]; + } + } + } + else { + float plane[3], meshmat[3][3]; + if (vert) { + edbm_vert_orientation(eve, mesh_no, plane, meshmat); + } + else { + if (edge) { + edbm_edge_orientation(eed, mesh_no, plane, meshmat); + } + else if (face) { + copy_v3_v3(mesh_no, efa->no); + BM_face_calc_tangent_auto(efa, plane); + } + normalize_v3_v3(meshmat[2], mesh_no); + negate_v3_v3(meshmat[1], plane); + + if (is_zero_v3(meshmat[1])) { + meshmat[1][2] = 1.0f; + } + + cross_v3_v3v3(meshmat[0], meshmat[2], meshmat[1]); + cross_v3_v3v3(meshmat[1], meshmat[2], meshmat[0]); + normalize_v3(meshmat[1]); + } + + normalize_m3(meshmat); + invert_m3(meshmat); + mesh_no[0] = meshmat[0][2]; + mesh_no[1] = meshmat[1][2]; + mesh_no[2] = meshmat[2][2]; + } + + for (int i = 0; i < 3; i++) { + if (persp) { + mesh_co[i] += vc->obact->object_to_world[3][i]; + view[i] = vc->rv3d->viewinv[3][i] - mesh_co[i]; + + if (i == 2) { + normalize_v3(view); + } + } + else { + view[i] = vc->rv3d->viewmat[i][2]; + } + } + + mesh_facing = dot_v3v3(mesh_no, view) > 0.0f; + + return mesh_facing; +} + +bool edbm_check_mesh_facing( + wmOperator *op, const bool zbuf, const bool lasso, const bool circle, const bool zbuf_vert) +{ + bool check_facing = false; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1 && lasso) { + if (zbuf) { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_backface)) { + check_facing = zbuf_vert ? + U.backface_downright_lasso == 1 : + U.backface_downright_lasso == 1 || U.backface_downright_lasso == 2; + } + else { + check_facing = zbuf_vert ? U.backface_lasso == 1 : + U.backface_lasso == 1 || U.backface_lasso == 2; + } + } + else { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_backface)) { + check_facing = U.backface_downright_lasso == 1; + } + else { + check_facing = U.backface_lasso == 1; + } + } + } + else if (U.userpref_mode == 1 && circle) { + if (zbuf) { + check_facing = zbuf_vert ? U.backface_circle == 1 : + U.backface_circle == 1 || U.backface_circle == 2; + } + else { + check_facing = U.backface_circle == 1; + } + } + else { + if (zbuf) { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_backface)) + { + check_facing = zbuf_vert ? + U.backface_downright_box == 1 : + U.backface_downright_box == 1 || U.backface_downright_box == 2; + } + else { + check_facing = zbuf_vert ? U.backface_box == 1 : + U.backface_box == 1 || U.backface_box == 2; + } + } + else { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_backface)) + { + check_facing = U.backface_downright_box == 1; + } + else { + check_facing = U.backface_box == 1; + } + } + } + } + else { + const int mode = RNA_enum_get(op->ptr, "backface_filter"); + + if (zbuf) { + check_facing = zbuf_vert ? mode == 2 : mode == 2 || mode == 4; + } + else { + check_facing = mode == 2; + } + } + + return check_facing; +} + +int object_style(wmOperator *op, const bool lasso, const bool circle) +{ + int type = 0; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1 && lasso) { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_object)) { + type = U.object_select_downright_lasso * 2; + } + else { + type = U.object_select_lasso * 2; + } + } + else if (U.userpref_mode == 1 && circle) { + type = U.object_select_circle * 2; + } + else { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_object)) + { + type = U.object_select_downright_box * 2; + } + else { + type = U.object_select_box * 2; + } + } + } + else { + type = RNA_enum_get(op->ptr, "object_type"); + } + + if (type == 0) { + type = 1; + } + + return type; +} + +int face_style(wmOperator *op, const bool lasso, const bool circle) +{ + int type = 0; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1 && lasso) { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_face)) { + type = U.face_select_downright_lasso * 2; + } + else { + type = U.face_select_lasso * 2; + } + } + else if (U.userpref_mode == 1 && circle) { + type = U.face_select_circle * 2; + } + else { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_face)) + { + type = U.face_select_downright_box * 2; + } + else { + type = U.face_select_box * 2; + } + } + + if (type == 6) { + type = 8; + } + } + else { + type = RNA_enum_get(op->ptr, "face_type"); + } + + if (type == 0) { + type = 1; + } + + return type; +} + +int edge_style(wmOperator *op, const bool lasso, const bool circle) +{ + int type = 0; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1 && lasso) { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_edge)) { + type = U.edge_select_downright_lasso * 2; + } + else { + type = U.edge_select_lasso * 2; + } + } + else if (U.userpref_mode == 1 && circle) { + type = U.edge_select_circle * 2; + } + else { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_edge)) + { + type = U.edge_select_downright_box * 2; + } + else { + type = U.edge_select_box * 2; + } + } + } + else { + type = RNA_enum_get(op->ptr, "edge_type"); + } + + if (type == 0) { + type = 1; + } + + return type; +} + +bool drag_select_through( + View3D *v3d, wmOperator *op, const bool mesh, const bool lasso, const bool circle) +{ + if (XRAY_FLAG_ENABLED(v3d)) { + return true; + } + + bool select_through = false; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1 && circle) { + if (mesh) { + select_through = U.select_through_circle == 2 || U.select_through_circle == 3; + } + else { + select_through = U.select_through_circle == 1 || U.select_through_circle == 3; + } + } + else if (U.userpref_mode == 1 && lasso) { + if (mesh) { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_auto_xray)) { + select_through = U.select_through_downright_lasso == 2 || + U.select_through_downright_lasso == 3; + } + else { + select_through = U.select_through_lasso == 2 || U.select_through_lasso == 3; + } + } + else { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_auto_xray)) { + select_through = U.select_through_downright_lasso == 1 || + U.select_through_downright_lasso == 3; + } + else { + select_through = U.select_through_lasso == 1 || U.select_through_lasso == 3; + } + } + } + else { + if (mesh) { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_auto_xray)) + { + select_through = U.select_through_downright_box == 2 || + U.select_through_downright_box == 3; + } + else { + select_through = U.select_through_box == 2 || U.select_through_box == 3; + } + } + else { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_auto_xray)) + { + select_through = U.select_through_downright_box == 1 || + U.select_through_downright_box == 3; + } + else { + select_through = U.select_through_box == 1 || U.select_through_box == 3; + } + } + } + } + else { + const int mode = RNA_enum_get(op->ptr, "select_through"); + select_through = mesh ? mode == 4 || mode == 8 : mode == 2 || mode == 8; + } + + return select_through; +} + +void face_filter( + ViewContext *vc, void *box_data, void *lasso_data, void *circle_data, eSelectOp sel_op) +{ + BMVert *eve; + BoxSelectUserData *data = static_cast(box_data); + LassoSelectUserData *ldata = static_cast(lasso_data); + CircleSelectUserData *cdata = static_cast(circle_data); + const BMesh *bm = vc->em->bm; + const bool box = box_data != nullptr; + const bool lasso = lasso_data != nullptr; + const bool circle = circle_data != nullptr; + const bool enclose = box ? data->face_style == 4 : + lasso ? ldata->face_style == 4 : + cdata->face_style == 4; + const bool check_mesh_facing = box ? data->check_mesh_direction : + lasso ? ldata->check_mesh_direction : + cdata->check_mesh_direction; + const int totvert = bm->totvert; + float v1co[2], v2co[2]; + float(*vco)[2] = static_cast(MEM_mallocN(sizeof(float) * 2 * totvert, __func__)); + + /* store 2d vert co */ + for (int i = 0; i < totvert; i++) { + eve = BM_vert_at_index(vc->em->bm, i); + if (ED_view3d_project_float_object( + vc->region, eve->co, v1co, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB) == + V3D_PROJ_RET_OK) + { + vco[i][0] = v1co[0]; + vco[i][1] = v1co[1]; + } + /* invalid vco */ + else { + vco[i][0] = 0.0f; + vco[i][1] = 0.0f; + } + } + + for (int i = 0; i < bm->totface; i++) { + BMFace *efa = BM_face_at_index(vc->em->bm, i); + bool inside = false; + + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + BMLoop *l_first, *l_iter; + rctf facetmp; + rctf *face = &facetmp; + BLI_rctf_init_minmax(face); + bool skip = false; + + /* skip invalid */ + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const int index = BM_elem_index_get(l_iter->v); + + if (index > totvert || index < 0 || (vco[index][0] == 0.0f && vco[index][1] == 0.0f)) { + skip = true; + break; + } + + BLI_rctf_do_minmax_v(face, vco[index]); + } while ((l_iter = l_iter->next) != l_first); + + /* skip non-intersect, not enough on its own, selects too many things */ + if (!skip) { + if (box && !BLI_rctf_isect(face, data->rect_fl, nullptr) || + lasso && !BLI_rctf_isect(face, ldata->rect_fl, nullptr) || + circle && !BLI_rctf_isect_circle(face, cdata->mval_fl, cdata->radius)) + { + skip = true; + } + } + + if (!skip) { + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + if (circle && enclose) { + do { + const int index = BM_elem_index_get(l_iter->v); + v1co[0] = vco[index][0]; + v1co[1] = vco[index][1]; + inside = len_squared_v2v2(cdata->mval_fl, v1co) <= cdata->radius_squared; + + if (!inside) { + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + do { + const int index1 = BM_elem_index_get(l_iter->e->v1); + const int index2 = BM_elem_index_get(l_iter->e->v2); + v1co[0] = vco[index1][0]; + v1co[1] = vco[index1][1]; + v2co[0] = vco[index2][0]; + v2co[1] = vco[index2][1]; + + if (box) { + inside = enclose ? edge_fully_inside_rect(data->rect_fl, v1co, v2co) : + edge_inside_rect(data->rect_fl, v1co, v2co); + } + else if (lasso) { + inside = BLI_lasso_is_edge_inside(ldata->mcoords, + ldata->mcoords_len, + int(v1co[0]), + int(v1co[1]), + int(v2co[0]), + int(v2co[1]), + IS_CLIPPED, + enclose); + } + else if (circle) { + inside = edge_inside_circle(cdata->mval_fl, cdata->radius, v1co, v2co); + } + + if (!enclose && inside) { + break; + } + else if (enclose && !inside) { + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + + /* touch face interior */ + if (!enclose && !inside) { + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + int vcount = 0; + float point[2]; + + if (box) { + point[0] = data->rect_fl->xmin; + point[1] = data->rect_fl->ymin; + } + else if (lasso) { + point[0] = static_cast(ldata->mcoords[0][0]); + point[1] = static_cast(ldata->mcoords[0][1]); + } + else if (circle) { + point[0] = cdata->mval_fl[0]; + point[1] = cdata->mval_fl[1]; + } + + if (efa->len == 3) { + float tri[3][2]; + do { + const int index = BM_elem_index_get(l_iter->v); + tri[vcount][0] = vco[index][0]; + tri[vcount][1] = vco[index][1]; + vcount++; + } while ((l_iter = l_iter->next) != l_first); + + inside = isect_point_tri_v2(point, tri[0], tri[1], tri[2]); + } + else if (efa->len == 4) { + float quad[4][2]; + do { + const int index = BM_elem_index_get(l_iter->v); + quad[vcount][0] = vco[index][0]; + quad[vcount][1] = vco[index][1]; + vcount++; + } while ((l_iter = l_iter->next) != l_first); + + inside = isect_point_quad_v2(point, quad[0], quad[1], quad[2], quad[3]); + } + else { + float(*ngon)[2] = static_cast( + MEM_mallocN(sizeof(float) * 2 * efa->len, __func__)); + do { + const int index = BM_elem_index_get(l_iter->v); + ngon[vcount][0] = vco[index][0]; + ngon[vcount][1] = vco[index][1]; + vcount++; + } while ((l_iter = l_iter->next) != l_first); + + inside = isect_point_poly_v2(point, ngon, efa->len); + MEM_freeN(ngon); + } + } + + if (inside && check_mesh_facing) { + inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + } + } + } + + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, inside); + + if (sel_op_result != -1) { + BM_face_select_set(vc->em->bm, efa, sel_op_result); + if (box) { + data->is_changed = true; + } + else if (lasso) { + ldata->is_changed = true; + } + else if (circle) { + cdata->is_changed = true; + } + } + } + MEM_freeN(vco); +} + +bool edbm_circle_enclose_mesh(BMEdge *eed, BMFace *efa, struct CircleSelectUserData *data) { BMVert *eve; BMIter iter; + bool enclose = false; + + if (eed != nullptr) { + BM_ITER_ELEM (eve, &iter, eed, BM_VERTS_OF_EDGE) { + float vertv3[3] = {eve->co[0], eve->co[1], eve->co[2]}; + float vertv2[2] = {}; + ED_view3d_project_float_object( + data->vc->region, vertv3, vertv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + enclose = len_squared_v2v2(data->mval_fl, vertv2) <= data->radius_squared; + if (!enclose) { + break; + } + } + } + else if (efa != nullptr) { + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + float vertv3[3] = {eve->co[0], eve->co[1], eve->co[2]}; + float vertv2[2] = {}; + ED_view3d_project_float_object( + data->vc->region, vertv3, vertv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + enclose = len_squared_v2v2(data->mval_fl, vertv2) <= data->radius_squared; + if (!enclose) { + break; + } + } + } + return enclose; +} + +bool edbm_center_face(ViewContext *vc, + BMFace *efa, + const rctf *rect, + struct LassoSelectUserData *ldata, + struct CircleSelectUserData *cdata) +{ + BMVert *eve; + BMIter iter; + float centerv3[3], centerv2[2]; + int vcount = 0; + bool center_face = false; + + if (efa->len == 3) { + float tri[3][3]; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + tri[vcount][0] = eve->co[0]; + tri[vcount][1] = eve->co[1]; + tri[vcount][2] = eve->co[2]; + vcount++; + } + mid_v3_v3v3v3(centerv3, tri[0], tri[1], tri[2]); + } + else if (efa->len == 4) { + float quad[4][3]; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + quad[vcount][0] = eve->co[0]; + quad[vcount][1] = eve->co[1]; + quad[vcount][2] = eve->co[2]; + vcount++; + } + mid_v3_v3v3v3v3(centerv3, quad[0], quad[1], quad[2], quad[3]); + } + else { + float(*ngon)[3] = static_cast( + MEM_mallocN(sizeof(float) * 3 * efa->len, __func__)); + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + ngon[vcount][0] = eve->co[0]; + ngon[vcount][1] = eve->co[1]; + ngon[vcount][2] = eve->co[2]; + vcount++; + } + mid_v3_v3_array(centerv3, ngon, efa->len); + MEM_freeN(ngon); + } + + ED_view3d_project_float_object( + vc->region, centerv3, centerv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + + if (ldata != nullptr) { + center_face = BLI_rctf_isect_pt_v(rect, centerv2) && + BLI_lasso_is_point_inside( + ldata->mcoords, ldata->mcoords_len, centerv2[0], centerv2[1], IS_CLIPPED); + } + else if (cdata != nullptr) { + center_face = (len_squared_v2v2(cdata->mval_fl, centerv2) <= cdata->radius_squared); + } + else { + center_face = BLI_rctf_isect_pt_v(rect, centerv2); + } + return center_face; +} + +static bool edbm_backbuf_check_and_select_verts(ViewContext *vc, + wmOperator *op, + EditSelectBuf_Cache *esel, + Depsgraph *depsgraph, + Object *ob, + BMEditMesh *em, + const eSelectOp sel_op, + const bool lasso, + const bool circle) +{ + BMVert *eve; + BMIter iter; + const int totface = em->bm->totface; + const bool check_mesh_facing = edbm_check_mesh_facing(op, true, lasso, circle, true); + blender::BitVector<> visited_face; + blender::BitVector<> front_face; bool changed = false; + if (check_mesh_facing) { + blender::BitVector<> vtmp(totface); + blender::BitVector<> ftmp(totface); + visited_face = vtmp; + front_face = ftmp; + } + const BLI_bitmap *select_bitmap = esel->select_bitmap; uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_VERTEX); if (index == 0) { @@ -265,10 +1427,47 @@ static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, } index -= 1; + BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); - const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); + bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); + + if (is_inside && check_mesh_facing) { + is_inside = false; + + if (eve->e && eve->e->l) { + BMIter fiter; + BMFace *efa; + BM_ITER_ELEM (efa, &fiter, eve, BM_FACES_OF_VERT) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (visited_face[index]) { + if (front_face[index]) { + is_inside = true; + break; + } + } + else { + visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + front_face[index].set(true); + break; + } + } + } + } + } + } + /* use vert normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, nullptr, eve); + } + } + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_vert_select_set(em->bm, eve, sel_op_result); @@ -280,15 +1479,20 @@ static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, return changed; } -static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, +static bool edbm_backbuf_check_and_select_edges(void *userData, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, + wmOperator *op, const eSelectOp sel_op) { + CircleSelectUserData *data = static_cast(userData); + ViewContext *vc = data->vc; BMEdge *eed; BMIter iter; bool changed = false; + const bool check_mesh_facing = edbm_check_mesh_facing(op, true, false, true, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_EDGE); @@ -300,7 +1504,50 @@ static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); + bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); + + /* enclose */ + if (is_inside && data->edge_style == 4) { + is_inside = edbm_circle_enclose_mesh(eed, nullptr, data); + } + + if (is_inside && check_mesh_facing) { + is_inside = false; + + if (eed->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use edge normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, eed, nullptr); + } + } + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_edge_select_set(em->bm, eed, sel_op_result); @@ -312,27 +1559,73 @@ static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, return changed; } -static bool edbm_backbuf_check_and_select_faces(EditSelectBuf_Cache *esel, +static bool edbm_backbuf_check_and_select_faces(ViewContext *vc, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, - const eSelectOp sel_op) + wmOperator *op, + const eSelectOp sel_op, + const rcti *rect, + const int face_style, + void *lasso_data, + void *circle_data) { + // ToolSettings *ts = vc->scene->toolsettings; + BMIter iter, viter; BMFace *efa; - BMIter iter; + BMVert *eve; + rctf rectf; bool changed = false; - + const bool check_mesh_facing = edbm_check_mesh_facing( + op, true, lasso_data != nullptr, circle_data != nullptr, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; + LassoSelectUserData *ldata = static_cast(lasso_data); + CircleSelectUserData *cdata = static_cast(circle_data); uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_FACE); + uint vindex = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_VERTEX); + + if (rect != nullptr) { + BLI_rctf_rcti_copy(&rectf, rect); + } + if (index == 0) { return false; } index -= 1; + vindex -= 1; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); - const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); + bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); + + if (face_style > 2 && is_inside) { + /* enclose */ + if (face_style == 4) { + if (cdata != nullptr) { + is_inside = edbm_circle_enclose_mesh(nullptr, efa, cdata); + } + else { + BM_ITER_ELEM (eve, &viter, efa, BM_VERTS_OF_FACE) { + is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, vindex + BM_elem_index_get(eve)); + if (!is_inside) { + break; + } + } + } + } + /* center */ + else { + is_inside = edbm_center_face(vc, efa, &rectf, ldata, cdata); + } + } + + if (is_inside && check_mesh_facing) { + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + } + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_face_select_set(em->bm, efa, sel_op_result); @@ -412,28 +1705,14 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *mesh, /** \name Lasso Select * \{ */ -struct LassoSelectUserData { - ViewContext *vc; - const rcti *rect; - const rctf *rect_fl; - rctf _rect_fl; - const int (*mcoords)[2]; - int mcoords_len; - eSelectOp sel_op; - eBezTriple_Flag select_flag; - - /* runtime */ - int pass; - bool is_done; - bool is_changed; -}; - static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, ViewContext *vc, const rcti *rect, const int (*mcoords)[2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + const int edge_style, + const int face_style) { r_data->vc = vc; @@ -446,6 +1725,9 @@ static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, r_data->sel_op = sel_op; /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->pass = 0; @@ -484,51 +1766,6 @@ static bool view3d_selectable_data(bContext *C) return true; } -/* helper also for box_select */ -static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2]) -{ - return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2); -} - -static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2]) -{ - int d1, d2, d3, d4; - - /* check points in rect */ - if (edge_fully_inside_rect(rect, v1, v2)) { - return true; - } - - /* check points completely out rect */ - if (v1[0] < rect->xmin && v2[0] < rect->xmin) { - return false; - } - if (v1[0] > rect->xmax && v2[0] > rect->xmax) { - return false; - } - if (v1[1] < rect->ymin && v2[1] < rect->ymin) { - return false; - } - if (v1[1] > rect->ymax && v2[1] > rect->ymax) { - return false; - } - - /* simple check lines intersecting. */ - d1 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymin); - d2 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymax); - d3 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymax); - d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin); - - if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) { - return false; - } - if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) { - return false; - } - - return true; -} - static void do_lasso_select_pose__do_tag(void *user_data, bPoseChannel *pchan, const float screen_co_a[2], @@ -541,8 +1778,12 @@ static void do_lasso_select_pose__do_tag(void *user_data, } if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) && - BLI_lasso_is_edge_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) + BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + INT_MAX, + false)) { pchan->bone->flag |= BONE_DONE; data->is_changed = true; @@ -560,7 +1801,7 @@ static void do_lasso_tag_pose(ViewContext *vc, const int mcoords[][2], const int BLI_lasso_boundbox(&rect, mcoords, mcoords_len); view3d_userdata_lassoselect_init( - &data, vc, &rect, mcoords, mcoords_len, static_cast(0)); + &data, vc, &rect, mcoords, mcoords_len, static_cast(0), 0, 0); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -571,41 +1812,171 @@ static void do_lasso_tag_pose(ViewContext *vc, const int mcoords[][2], const int V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); } +/** + * Compare result of 'GPU_select': 'GPUSelectResult', + * Needed for stable sorting, so cycling through all items near the cursor behaves predictably. + */ +static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p) +{ + GPUSelectResult *a = (GPUSelectResult *)sel_a_p; + GPUSelectResult *b = (GPUSelectResult *)sel_b_p; + + if (a->depth < b->depth) { + return -1; + } + if (a->depth > b->depth) { + return 1; + } + + /* Depths match, sort by id. */ + uint sel_a = a->id; + uint sel_b = b->id; + +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&sel_a); + BLI_endian_switch_uint32(&sel_b); +#endif + + if (sel_a < sel_b) { + return -1; + } + if (sel_a > sel_b) { + return 1; + } + return 0; +} + static bool do_lasso_select_objects(ViewContext *vc, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { View3D *v3d = vc->v3d; - bool changed = false; + float region_co[2]; + const int object_type = object_style(op, true, false); + const bool wireless_touch = U.wireless_touch_object && object_type == 1 && + v3d->shading.type == OB_WIRE && XRAY_FLAG_ENABLED(v3d); + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); + changed |= object_deselect_all_visible(vc->scene, vc->view_layer, v3d); } - BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); - LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { - if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */ - float region_co[2]; - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) == - V3D_PROJ_RET_OK) && - BLI_lasso_is_point_inside(mcoords, - mcoords_len, - int(region_co[0]), - int(region_co[1]), - /* Dummy value. */ - INT_MAX); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - changed = true; + + if (wireless_touch) { + BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + if (BASE_SELECTABLE(v3d, base)) { + const bool is_select = base->flag & BASE_SELECTED; + bool is_inside = false; + if (base->object->type == OB_MESH) { + is_inside = object_filter( + vc, base, object_type, mcoords, mcoords_len, nullptr, nullptr, false); + } + else { + is_inside = (ED_view3d_project_base(vc->region, base, region_co) == -V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(region_co[0]), + int(region_co[1]), + /* Dummy value. */ + INT_MAX); + } + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } } } - } - if (changed) { - DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); - WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + if (changed) { + DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + } + } + else { + int hits = 0; + GPUSelectBuffer buffer; + rcti rect; + blender::Vector bases; + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); + const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, + vc->obact); + + if (drag_select_through(v3d, op, false, true, false)) { + hits = view3d_opengl_select(vc, &buffer, &rect, VIEW3D_SELECT_PICK_ALL, select_filter); + } + else { + hits = view3d_opengl_select(vc, &buffer, &rect, VIEW3D_SELECT_PICK_NEAREST, select_filter); + } + + BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } + ListBase *object_bases = BKE_view_layer_object_bases_get(vc->view_layer); + + if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) { + goto finally; + } + + LISTBASE_FOREACH (Base *, base, object_bases) { + if (BASE_SELECTABLE(v3d, base)) { + if ((base->object->runtime->select_id & 0x0000FFFF) != 0) { + bases.append(base); + } + } + } + + /* The draw order doesn't always match the order we populate the engine, see: T51695. */ + qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp); + for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits; + buf_iter < buf_end; + buf_iter++) + { + bPoseChannel *pchan_dummy; + Base *base = ED_armature_base_and_pchan_from_select_buffer( + bases, buf_iter->id, &pchan_dummy); + + if (base != nullptr) { + base->object->id.tag |= LIB_TAG_DOIT; + } + } + + for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { + if (BASE_SELECTABLE(v3d, base)) { + const bool is_select = base->flag & BASE_SELECTED; + bool is_inside = false; + if (base->object->type == OB_MESH && (object_type == 1 || object_type == 2)) { + is_inside = base->object->id.tag & LIB_TAG_DOIT && + object_filter( + vc, base, object_type, mcoords, mcoords_len, nullptr, nullptr, false); + } + else { + is_inside = base->object->id.tag & LIB_TAG_DOIT && + (ED_view3d_project_base(vc->region, base, region_co) == -V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(region_co[0]), + int(region_co[1]), + /* Dummy value. */ + INT_MAX); + } + + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + + finally: + if (changed) { + DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(vc->C, NC_SCENE | ND_OB_SELECT, vc->scene); + } } return changed; } @@ -734,11 +2105,49 @@ static void do_lasso_select_mesh__doSelectVert(void *user_data, int /*index*/) { LassoSelectUserData *data = static_cast(user_data); + ViewContext *vc = data->vc; const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); - const bool is_inside = - (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); + bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); + + if (is_inside && data->check_mesh_direction) { + is_inside = false; + + if (eve->e && eve->e->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eve, BM_FACES_OF_VERT) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use vert normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, nullptr, eve); + } + } + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); @@ -759,6 +2168,7 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, LassoSelectUserData_ForMeshEdge *data_for_edge = static_cast( user_data); LassoSelectUserData *data = data_for_edge->data; + ViewContext *vc = data->vc; bool is_visible = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; @@ -766,12 +2176,50 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, } const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = - (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && - BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), IS_CLIPPED) && - BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), IS_CLIPPED)); + bool is_inside = (is_visible && + edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), IS_CLIPPED) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), IS_CLIPPED)); + + if (is_inside && data->check_mesh_direction) { + is_inside = false; + + if (eed->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use edge normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, eed, nullptr); + } + } + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); @@ -788,6 +2236,7 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, LassoSelectUserData_ForMeshEdge *data_for_edge = static_cast( user_data); LassoSelectUserData *data = data_for_edge->data; + ViewContext *vc = data->vc; bool is_visible = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; @@ -795,11 +2244,49 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, } const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcoords, - data->mcoords_len, - UNPACK2(screen_co_a), - UNPACK2(screen_co_b), - IS_CLIPPED)); + bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + IS_CLIPPED, + false)); + + if (is_inside && data->check_mesh_direction) { + is_inside = false; + + if (eed->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use edge normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, eed, nullptr); + } + } const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); @@ -813,11 +2300,16 @@ static void do_lasso_select_mesh__doSelectFace(void *user_data, int /*index*/) { LassoSelectUserData *data = static_cast(user_data); + ViewContext *vc = data->vc; const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); - const bool is_inside = - (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); + bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); + + if (is_inside && data->check_mesh_direction) { + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + } + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_face_select_set(data->vc->em->bm, efa, sel_op_result); @@ -829,7 +2321,8 @@ static bool do_lasso_select_mesh(ViewContext *vc, wmGenericUserData *wm_userdata, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { LassoSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; @@ -840,7 +2333,14 @@ static bool do_lasso_select_mesh(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, + vc, + &rect, + mcoords, + mcoords_len, + sel_op, + edge_style(op, true, false), + face_style(op, true, false)); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { @@ -854,22 +2354,43 @@ static bool do_lasso_select_mesh(ViewContext *vc, GPU_matrix_set(vc->rv3d->viewmat); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const bool use_zbuf = !drag_select_through(vc->v3d, op, true, true, false); EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); if (use_zbuf) { if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + /* for near enclose face */ + if (data.face_style == 4 && ts->selectmode & SCE_SELECT_FACE && + !(ts->selectmode & SCE_SELECT_VERTEX)) + { + editselect_buf_cache_init_with_generic_userdata( + wm_userdata, vc, ts->selectmode | SCE_SELECT_VERTEX); + } + else { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + } esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, nullptr); } } + if (ts->selectmode & SCE_SELECT_EDGE || !use_zbuf) { + data.check_mesh_direction = edbm_check_mesh_facing(op, use_zbuf, true, false, false); + + if (data.check_mesh_direction) { + const int totface = vc->em->bm->totface; + blender::BitVector<> visited_face(totface); + blender::BitVector<> front_face(totface); + data.visited_face = visited_face; + data.front_face = front_face; + } + } + if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, op, esel, vc->depsgraph, vc->obedit, vc->em, sel_op, true, false); } else { mesh_foreachScreenVert( @@ -887,12 +2408,14 @@ static bool do_lasso_select_mesh(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB); - /* Fully inside. */ - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag); - if (data.is_done == false) { - /* Fall back to partially inside. - * Clip content to account for edges partially behind the view. */ + + /* Fully inside */ + if (data.edge_style != 2) { + mesh_foreachScreenEdge_clip_bb_segment( + vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag); + } + /* Partially inside */ + if (data.edge_style == 2 || data.edge_style == 1 && data.is_done == false) { mesh_foreachScreenEdge_clip_bb_segment(vc, do_lasso_select_mesh__doSelectEdge_pass1, &data_for_edge, @@ -902,12 +2425,28 @@ static bool do_lasso_select_mesh(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces(vc, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + op, + sel_op, + &rect, + data.face_style, + &data, + nullptr); } else { - mesh_foreachScreenFace( - vc, do_lasso_select_mesh__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + /* center */ + if (data.face_style == 1 || data.face_style == 8) { + mesh_foreachScreenFace( + vc, do_lasso_select_mesh__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* touch & enclose */ + else { + face_filter(vc, nullptr, &data, nullptr, sel_op); + } } } @@ -971,7 +2510,7 @@ static bool do_lasso_select_curve(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -1022,7 +2561,7 @@ static bool do_lasso_select_lattice(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -1050,7 +2589,9 @@ static void do_lasso_select_armature__doSelectBone(void *user_data, if (screen_co_a[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && - BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX), + false) { is_inside_flag |= BONESEL_ROOT; } @@ -1061,7 +2602,9 @@ static void do_lasso_select_armature__doSelectBone(void *user_data, if (screen_co_b[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && - BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX), + false) { is_inside_flag |= BONESEL_TIP; } @@ -1072,8 +2615,12 @@ static void do_lasso_select_armature__doSelectBone(void *user_data, if (is_ignore_flag == 0) { if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) || - BLI_lasso_is_edge_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) + BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + INT_MAX, + false)) { is_inside_flag |= BONESEL_BONE; } @@ -1102,8 +2649,12 @@ static void do_lasso_select_armature__doSelectBone_clip_content(void *user_data, return; } - if (BLI_lasso_is_edge_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) + if (BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + INT_MAX, + false)) { is_inside_flag |= BONESEL_BONE; } @@ -1121,7 +2672,7 @@ static bool do_lasso_select_armature(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit); @@ -1181,7 +2732,7 @@ static bool do_lasso_select_meta(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= BKE_mball_deselect_all(mb); @@ -1313,7 +2864,8 @@ static bool do_lasso_select_paintvert(ViewContext *vc, LassoSelectUserData_ForMeshVert data; data.select_vert = select_vert.span; - view3d_userdata_lassoselect_init(&data.lasso_data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init( + &data.lasso_data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -1378,7 +2930,8 @@ static bool view3d_lasso_select(bContext *C, ViewContext *vc, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { using namespace blender; Object *ob = CTX_data_active_object(C); @@ -1412,7 +2965,7 @@ static bool view3d_lasso_select(bContext *C, /* pass */ } else { - changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op); + changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op, op); if (changed_multi) { ED_outliner_select_sync_from_object_tag(C); } @@ -1426,7 +2979,7 @@ static bool view3d_lasso_select(bContext *C, switch (vc->obedit->type) { case OB_MESH: - changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op); + changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op, op); break; case OB_CURVES_LEGACY: case OB_SURF: @@ -1506,7 +3059,7 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op) ViewContext vc = ED_view3d_viewcontext_init(C, depsgraph); eSelectOp sel_op = static_cast(RNA_enum_get(op->ptr, "mode")); - bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op); + bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op, op); MEM_freeN((void *)mcoords); @@ -2228,40 +3781,6 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, return hits; } -/** - * Compare result of 'GPU_select': 'GPUSelectResult', - * Needed for stable sorting, so cycling through all items near the cursor behaves predictably. - */ -static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p) -{ - GPUSelectResult *a = (GPUSelectResult *)sel_a_p; - GPUSelectResult *b = (GPUSelectResult *)sel_b_p; - - if (a->depth < b->depth) { - return -1; - } - if (a->depth > b->depth) { - return 1; - } - - /* Depths match, sort by id. */ - uint sel_a = a->id; - uint sel_b = b->id; - -#ifdef __BIG_ENDIAN__ - BLI_endian_switch_uint32(&sel_a); - BLI_endian_switch_uint32(&sel_b); -#endif - - if (sel_a < sel_b) { - return -1; - } - if (sel_a > sel_b) { - return 1; - } - return 0; -} - /** * \param has_bones: When true, skip non-bone hits, also allow bases to be used * that are visible but not select-able, @@ -3496,23 +5015,12 @@ void VIEW3D_OT_select(wmOperatorType *ot) /** \name Box Select * \{ */ -struct BoxSelectUserData { - ViewContext *vc; - const rcti *rect; - const rctf *rect_fl; - rctf _rect_fl; - eSelectOp sel_op; - eBezTriple_Flag select_flag; - - /* runtime */ - bool is_done; - bool is_changed; -}; - static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, ViewContext *vc, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + const int edge_style, + const int face_style) { r_data->vc = vc; @@ -3523,6 +5031,9 @@ static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, r_data->sel_op = sel_op; /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->is_done = false; @@ -3560,7 +5071,8 @@ static void do_paintvert_box_select__doSelectVert(void *user_data, static bool do_paintvert_box_select(ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + const bool square) { using namespace blender; const bool use_zbuf = !XRAY_ENABLED(vc->v3d); @@ -3579,15 +5091,31 @@ static bool do_paintvert_box_select(ViewContext *vc, /* pass */ } else if (use_zbuf) { - EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); - if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX); + if (square) { + if (wm_userdata->data == nullptr) { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX); + } + EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( vc->depsgraph, vc->region, vc->v3d, rect, nullptr); + if (esel->select_bitmap != nullptr) { + changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op); + MEM_freeN(esel->select_bitmap); + esel->select_bitmap = nullptr; + } } - if (esel->select_bitmap != nullptr) { - changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op); + else { + EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); + if (wm_userdata->data == nullptr) { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX); + esel = static_cast(wm_userdata->data); + esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( + vc->depsgraph, vc->region, vc->v3d, rect, nullptr); + } + if (esel->select_bitmap != nullptr) { + changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op); + } } } else { @@ -3598,7 +5126,7 @@ static bool do_paintvert_box_select(ViewContext *vc, BoxSelectUserData_ForMeshVert data; data.select_vert = select_vert.span; - view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op, 0, 0); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -3621,7 +5149,8 @@ static bool do_paintvert_box_select(ViewContext *vc, static bool do_paintface_box_select(ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, - eSelectOp sel_op) + eSelectOp sel_op, + const bool square) { Object *ob = vc->obact; Mesh *mesh; @@ -3640,15 +5169,31 @@ static bool do_paintface_box_select(ViewContext *vc, /* pass */ } else { - EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); - if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE); + if (square) { + if (wm_userdata->data == nullptr) { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE); + } + EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( vc->depsgraph, vc->region, vc->v3d, rect, nullptr); + if (esel->select_bitmap != nullptr) { + changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op); + MEM_freeN(esel->select_bitmap); + esel->select_bitmap = nullptr; + } } - if (esel->select_bitmap != nullptr) { - changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op); + else { + EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); + if (wm_userdata->data == nullptr) { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE); + esel = static_cast(wm_userdata->data); + esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( + vc->depsgraph, vc->region, vc->v3d, rect, nullptr); + } + if (esel->select_bitmap != nullptr) { + changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op); + } } } @@ -3704,7 +5249,7 @@ static bool do_nurbs_box_select(ViewContext *vc, const rcti *rect, const eSelect const bool deselect_all = (sel_op == SEL_OP_SET); BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -3743,7 +5288,7 @@ static bool do_lattice_box_select(ViewContext *vc, const rcti *rect, const eSele { BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -3762,8 +5307,47 @@ static void do_mesh_box_select__doSelectVert(void *user_data, int /*index*/) { BoxSelectUserData *data = static_cast(user_data); + ViewContext *vc = data->vc; const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); - const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + + if (is_inside && data->check_mesh_direction) { + is_inside = false; + + if (eve->e && eve->e->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eve, BM_FACES_OF_VERT) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use vert normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, nullptr, eve); + } + } + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); @@ -3787,6 +5371,7 @@ static void do_mesh_box_select__doSelectEdge_pass0(void *user_data, BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast( user_data); BoxSelectUserData *data = data_for_edge->data; + ViewContext *vc = data->vc; bool is_visible = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; @@ -3794,11 +5379,48 @@ static void do_mesh_box_select__doSelectEdge_pass0(void *user_data, } const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = (is_visible && - edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); + bool is_inside = (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); + + if (is_inside && data->check_mesh_direction) { + is_inside = false; + + if (eed->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use edge normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, eed, nullptr); + } + } + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { - BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); + BM_edge_select_set(vc->em->bm, eed, sel_op_result); data->is_done = true; data->is_changed = true; } @@ -3815,6 +5437,7 @@ static void do_mesh_box_select__doSelectEdge_pass1(void *user_data, BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast( user_data); BoxSelectUserData *data = data_for_edge->data; + ViewContext *vc = data->vc; bool is_visible = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; @@ -3822,36 +5445,84 @@ static void do_mesh_box_select__doSelectEdge_pass1(void *user_data, } const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); + bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); + + if (is_inside && data->check_mesh_direction) { + is_inside = false; + + if (eed->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use edge normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, eed, nullptr); + } + } + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { - BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); + BM_edge_select_set(vc->em->bm, eed, sel_op_result); data->is_changed = true; } } + static void do_mesh_box_select__doSelectFace(void *user_data, BMFace *efa, const float screen_co[2], int /*index*/) { BoxSelectUserData *data = static_cast(user_data); + ViewContext *vc = data->vc; const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); - const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + + if (is_inside && data->check_mesh_direction) { + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + } + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { - BM_face_select_set(data->vc->em->bm, efa, sel_op_result); + BM_face_select_set(vc->em->bm, efa, sel_op_result); data->is_changed = true; } } static bool do_mesh_box_select(ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op, + const bool square) { BoxSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init( + &data, vc, rect, sel_op, edge_style(op, false, square), face_style(op, false, square)); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { @@ -3865,22 +5536,46 @@ static bool do_mesh_box_select(ViewContext *vc, GPU_matrix_set(vc->rv3d->viewmat); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const bool use_zbuf = !drag_select_through(vc->v3d, op, true, false, square); + + if (use_zbuf) { + /* for near enclose face */ + if (data.face_style == 4 && ts->selectmode & SCE_SELECT_FACE && + !(ts->selectmode & SCE_SELECT_VERTEX)) + { + editselect_buf_cache_init_with_generic_userdata( + wm_userdata, vc, ts->selectmode | SCE_SELECT_VERTEX); + } + else { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + } + } EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); if (use_zbuf) { - if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + if (esel->select_bitmap == nullptr) { esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( vc->depsgraph, vc->region, vc->v3d, rect, nullptr); } } + if (ts->selectmode & SCE_SELECT_EDGE || !use_zbuf) { + data.check_mesh_direction = edbm_check_mesh_facing(op, use_zbuf, false, false, false); + + if (data.check_mesh_direction) { + const int totface = vc->em->bm->totface; + blender::BitVector<> visited_face(totface); + blender::BitVector<> front_face(totface); + data.visited_face = visited_face; + data.front_face = front_face; + } + } + if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, op, esel, vc->depsgraph, vc->obedit, vc->em, sel_op, false, false); } else { mesh_foreachScreenVert( @@ -3898,12 +5593,14 @@ static bool do_mesh_box_select(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB); - /* Fully inside. */ - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag); - if (data.is_done == false) { - /* Fall back to partially inside. - * Clip content to account for edges partially behind the view. */ + + /* Fully inside */ + if (data.edge_style != 2) { + mesh_foreachScreenEdge_clip_bb_segment( + vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag); + } + /* Partially inside */ + if (data.edge_style == 2 || data.edge_style == 1 && data.is_done == false) { mesh_foreachScreenEdge_clip_bb_segment(vc, do_mesh_box_select__doSelectEdge_pass1, &cb_data, @@ -3913,12 +5610,28 @@ static bool do_mesh_box_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces(vc, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + op, + sel_op, + rect, + data.face_style, + nullptr, + nullptr); } else { - mesh_foreachScreenFace( - vc, do_mesh_box_select__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + /* center */ + if (data.face_style == 1 || data.face_style == 8) { + mesh_foreachScreenFace( + vc, do_mesh_box_select__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* touch & enclose */ + else { + face_filter(vc, &data, nullptr, nullptr, sel_op); + } } } @@ -4077,27 +5790,49 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_ static bool do_object_box_select(bContext *C, ViewContext *vc, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op, + const bool square) { View3D *v3d = vc->v3d; - GPUSelectBuffer buffer; const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, vc->obact); - const int hits = view3d_opengl_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter); - BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); - LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { - base->object->id.tag &= ~LIB_TAG_DOIT; + rctf rectf_data; + rctf *rectf = &rectf_data; + BLI_rctf_rcti_copy(rectf, rect); + int hits = 0; + bool changed = false; + float region_co[2]; + const int object_type = object_style(op, false, square); + const bool wireless_touch = U.wireless_touch_object && object_type == 1 && + v3d->shading.type == OB_WIRE && XRAY_FLAG_ENABLED(v3d); + bool select; + int select_flag; + + if (square) { + select = (sel_op != SEL_OP_SUB); + select_flag = select ? BASE_SELECTED : 0; } - blender::Vector bases; - - bool changed = false; if (SEL_OP_USE_PRE_DESELECT(sel_op)) { changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); } + if (drag_select_through(v3d, op, false, false, square)) { + hits = view3d_opengl_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter); + } + else { + hits = view3d_opengl_select(vc, &buffer, rect, VIEW3D_SELECT_PICK_NEAREST, select_filter); + } + + BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } + blender::Vector bases; ListBase *object_bases = BKE_view_layer_object_bases_get(vc->view_layer); + if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) { goto finally; } @@ -4109,10 +5844,8 @@ static bool do_object_box_select(bContext *C, } } } - /* The draw order doesn't always match the order we populate the engine, see: #51695. */ qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), opengl_bone_select_buffer_cmp); - for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits; buf_iter < buf_end; buf_iter++) @@ -4124,20 +5857,91 @@ static bool do_object_box_select(bContext *C, } } - for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { - if (BASE_SELECTABLE(v3d, base)) { - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = base->object->id.tag & LIB_TAG_DOIT; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - changed = true; + if (wireless_touch) { + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + if (square) { + if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) { + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = base->object->type == OB_MESH ? + object_filter( + vc, base, object_type, nullptr, 0, rectf, nullptr, true) : + base->object->id.tag & LIB_TAG_DOIT; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + else { + if (BASE_SELECTABLE(v3d, base)) { + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = + base->object->type == OB_MESH ? + object_filter(vc, base, object_type, nullptr, 0, rectf, nullptr, false) : + base->object->id.tag & LIB_TAG_DOIT; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + } + } + else { + for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { + if (square) { + if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) { + const bool is_select = base->flag & BASE_SELECTED; + bool is_inside = false; + if (base->object->type != OB_MESH || object_type < 2) { + is_inside = base->object->id.tag & LIB_TAG_DOIT; + } + else { + if (object_type == 2 && base->object->id.tag & LIB_TAG_DOIT) { + is_inside = object_filter(vc, base, object_type, nullptr, 0, rectf, nullptr, true); + } + else if (object_type == 4 && base->object->id.tag & LIB_TAG_DOIT) { + is_inside = (ED_view3d_project_base(vc->region, base, region_co) == + V3D_PROJ_RET_OK) && + BLI_rctf_isect_pt_v(rectf, region_co); + } + } + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + else { + if (BASE_SELECTABLE(v3d, base)) { + const bool is_select = base->flag & BASE_SELECTED; + bool is_inside = false; + if (base->object->type != OB_MESH || object_type < 2) { + is_inside = base->object->id.tag & LIB_TAG_DOIT; + } + else { + if (object_type == 2 && base->object->id.tag & LIB_TAG_DOIT) { + is_inside = object_filter(vc, base, object_type, nullptr, 0, rectf, nullptr, true); + } + else if (object_type == 4 && base->object->id.tag & LIB_TAG_DOIT) { + is_inside = (ED_view3d_project_base(vc->region, base, region_co) == + V3D_PROJ_RET_OK) && + BLI_rctf_isect_pt_v(rectf, region_co); + } + } + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } } } } - finally: - if (changed) { DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); @@ -4290,7 +6094,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) switch (vc.obedit->type) { case OB_MESH: vc.em = BKE_editmesh_from_object(vc.obedit); - changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op); + changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op, op, false); if (changed) { DEG_id_tag_update(static_cast(vc.obedit->data), ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); @@ -4357,10 +6161,10 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } else { /* No edit-mode, unified for bones and objects. */ if (vc.obact && BKE_paint_select_face_test(vc.obact)) { - changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op); + changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op, false); } else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) { - changed_multi = do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op); + changed_multi = do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op, false); } else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) { changed_multi = PE_box_select(C, &rect, sel_op); @@ -4375,7 +6179,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } } else { /* object mode with none active */ - changed_multi = do_object_box_select(C, &vc, &rect, sel_op); + changed_multi = do_object_box_select(C, &vc, &rect, sel_op, op, false); if (changed_multi) { ED_outliner_select_sync_from_object_tag(C); } @@ -4418,24 +6222,13 @@ void VIEW3D_OT_select_box(wmOperatorType *ot) /** \name Circle Select * \{ */ -struct CircleSelectUserData { - ViewContext *vc; - bool select; - int mval[2]; - float mval_fl[2]; - float radius; - float radius_squared; - eBezTriple_Flag select_flag; - - /* runtime */ - bool is_changed; -}; - static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data, ViewContext *vc, const bool select, const int mval[2], - const float rad) + const float rad, + const int edge_style, + const int face_style) { r_data->vc = vc; r_data->select = select; @@ -4448,6 +6241,9 @@ static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data, /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->is_changed = false; @@ -4459,10 +6255,52 @@ static void mesh_circle_doSelectVert(void *user_data, int /*index*/) { CircleSelectUserData *data = static_cast(user_data); + ViewContext *vc = data->vc; if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { - BM_vert_select_set(data->vc->em->bm, eve, data->select); - data->is_changed = true; + bool is_inside = true; + + if (data->check_mesh_direction) { + is_inside = false; + + if (eve->e && eve->e->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eve, BM_FACES_OF_VERT) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use vert normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, nullptr, eve); + } + } + + if (is_inside) { + BM_vert_select_set(vc->em->bm, eve, data->select); + data->is_changed = true; + } } } static void mesh_circle_doSelectEdge(void *user_data, @@ -4472,22 +6310,78 @@ static void mesh_circle_doSelectEdge(void *user_data, int /*index*/) { CircleSelectUserData *data = static_cast(user_data); + ViewContext *vc = data->vc; if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { - BM_edge_select_set(data->vc->em->bm, eed, data->select); - data->is_changed = true; + bool is_inside = true; + + if (data->edge_style == 4) { + is_inside = edbm_circle_enclose_mesh(eed, nullptr, data); + } + + if (is_inside && data->check_mesh_direction) { + is_inside = false; + + if (eed->l) { + const int totface = vc->em->bm->totface; + BMIter iter; + BMFace *efa; + + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + const int index = BM_elem_index_get(efa); + if (!(index < 0 || index > totface)) { + if (data->visited_face[index]) { + if (data->front_face[index]) { + is_inside = true; + break; + } + } + else { + data->visited_face[index].set(true); + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + + if (is_inside) { + data->front_face[index].set(true); + break; + } + } + } + } + } + } + /* use edge normals */ + else { + is_inside = edbm_mesh_facing_viewport(vc, nullptr, eed, nullptr); + } + } + + if (is_inside) { + BM_edge_select_set(vc->em->bm, eed, data->select); + data->is_changed = true; + } } } + static void mesh_circle_doSelectFace(void *user_data, BMFace *efa, const float screen_co[2], int /*index*/) { CircleSelectUserData *data = static_cast(user_data); + ViewContext *vc = data->vc; if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { - BM_face_select_set(data->vc->em->bm, efa, data->select); - data->is_changed = true; + bool is_inside = true; + + if (data->check_mesh_direction) { + is_inside = edbm_mesh_facing_viewport(vc, efa, nullptr, nullptr); + } + + if (is_inside) { + BM_face_select_set(vc->em->bm, efa, data->select); + data->is_changed = true; + } } } @@ -4495,7 +6389,8 @@ static bool mesh_circle_select(ViewContext *vc, wmGenericUserData *wm_userdata, eSelectOp sel_op, const int mval[2], - float rad) + float rad, + wmOperator *op) { ToolSettings *ts = vc->scene->toolsettings; CircleSelectUserData data; @@ -4515,9 +6410,10 @@ static bool mesh_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init( + &data, vc, select, mval, rad, edge_style(op, false, true), face_style(op, false, true)); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const bool use_zbuf = !drag_select_through(vc->v3d, op, true, false, true); if (use_zbuf) { if (wm_userdata->data == nullptr) { @@ -4533,11 +6429,30 @@ static bool mesh_circle_select(ViewContext *vc, } } + if (ts->selectmode & SCE_SELECT_EDGE || !use_zbuf) { + data.check_mesh_direction = edbm_check_mesh_facing(op, use_zbuf, false, true, false); + + if (data.check_mesh_direction) { + const int totface = vc->em->bm->totface; + blender::BitVector<> visited_face(totface); + blender::BitVector<> front_face(totface); + data.visited_face = visited_face; + data.front_face = front_face; + } + } + if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { if (esel->select_bitmap != nullptr) { - changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + changed |= edbm_backbuf_check_and_select_verts(vc, + op, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + select ? SEL_OP_ADD : SEL_OP_SUB, + false, + true); } } else { @@ -4549,7 +6464,7 @@ static bool mesh_circle_select(ViewContext *vc, if (use_zbuf) { if (esel->select_bitmap != nullptr) { changed |= edbm_backbuf_check_and_select_edges( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + &data, esel, vc->depsgraph, vc->obedit, vc->em, op, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { @@ -4564,12 +6479,28 @@ static bool mesh_circle_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { if (esel->select_bitmap != nullptr) { - changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + changed |= edbm_backbuf_check_and_select_faces(vc, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + op, + select ? SEL_OP_ADD : SEL_OP_SUB, + nullptr, + data.face_style, + nullptr, + &data); } } else { - mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + /* center */ + if (data.face_style == 1 || data.face_style == 8) { + mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* touch & enclose */ + else { + face_filter(vc, nullptr, nullptr, &data, sel_op); + } } } @@ -4683,7 +6614,7 @@ static bool paint_vertsel_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */ - view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad, 0, 0); meshobject_foreachScreenVert( vc, paint_vertsel_circle_select_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); changed |= data.circle_data.is_changed; @@ -4737,7 +6668,7 @@ static bool nurbscurve_circle_select(ViewContext *vc, const bool deselect_all = (sel_op == SEL_OP_SET); CircleSelectUserData data; - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -4778,7 +6709,7 @@ static bool lattice_circle_select(ViewContext *vc, CircleSelectUserData data; const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -4870,7 +6801,7 @@ static bool pose_circle_select(ViewContext *vc, CircleSelectUserData data; const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_pose_deselect_all(vc->obact, SEL_DESELECT, false); @@ -5008,7 +6939,7 @@ static bool armature_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit); @@ -5061,7 +6992,7 @@ static bool mball_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= BKE_mball_deselect_all(static_cast(vc->obedit->data)); @@ -5129,30 +7060,60 @@ static bool obedit_circle_select(bContext *C, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], - float rad) + float rad, + wmOperator *op, + const bool square, + rcti *rect) { using namespace blender; bool changed = false; BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB)); switch (vc->obedit->type) { case OB_MESH: - changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad); + if (square) { + vc->em = BKE_editmesh_from_object(vc->obedit); + changed = do_mesh_box_select(vc, wm_userdata, rect, sel_op, op, true); + } + else { + changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad, op); + } break; case OB_CURVES_LEGACY: case OB_SURF: - changed = nurbscurve_circle_select(vc, sel_op, mval, rad); + if (square) { + changed = do_nurbs_box_select(vc, rect, sel_op); + } + else { + changed = nurbscurve_circle_select(vc, sel_op, mval, rad); + } break; case OB_LATTICE: - changed = lattice_circle_select(vc, sel_op, mval, rad); + if (square) { + changed = do_lattice_box_select(vc, rect, sel_op); + } + else { + changed = lattice_circle_select(vc, sel_op, mval, rad); + } break; case OB_ARMATURE: - changed = armature_circle_select(vc, sel_op, mval, rad); + if (square) { + changed = do_armature_box_select(vc, rect, sel_op); + } + else { + changed = armature_circle_select(vc, sel_op, mval, rad); + } + if (changed) { ED_outliner_select_sync_from_edit_bone_tag(C); } break; case OB_MBALL: - changed = mball_circle_select(vc, sel_op, mval, rad); + if (square) { + changed = do_meta_box_select(vc, rect, sel_op); + } + else { + changed = mball_circle_select(vc, sel_op, mval, rad); + } break; case OB_CURVES: { Curves &curves_id = *static_cast(vc->obedit->data); @@ -5161,8 +7122,15 @@ static bool obedit_circle_select(bContext *C, bke::crazyspace::get_evaluated_curves_deformation(*vc->depsgraph, *vc->obedit); const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain); const IndexRange elements(curves.attributes().domain_size(selection_domain)); - changed = ed::curves::select_circle( - *vc, curves, deformation.positions, elements, selection_domain, mval, rad, sel_op); + + if (square) { + changed = ed::curves::select_box( + *vc, curves, deformation.positions, elements, selection_domain, *rect, sel_op); + } + else { + changed = ed::curves::select_circle( + *vc, curves, deformation.positions, elements, selection_domain, mval, rad, sel_op); + } if (changed) { /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a * generic attribute for now. */ @@ -5172,7 +7140,13 @@ static bool obedit_circle_select(bContext *C, break; } case OB_GREASE_PENCIL: - changed = grease_pencil_circle_select(vc, sel_op, mval, rad); + /* this is gpen 3.0 and won't happen in 4.1 release version */ + if (square) { + changed = do_grease_pencil_box_select(vc, rect, sel_op); + } + else { + changed = grease_pencil_circle_select(vc, sel_op, mval, rad); + } break; default: @@ -5187,42 +7161,158 @@ static bool obedit_circle_select(bContext *C, return changed; } -static bool object_circle_select(ViewContext *vc, - const eSelectOp sel_op, - const int mval[2], - float rad) +static bool object_circle_select( + ViewContext *vc, const eSelectOp sel_op, wmOperator *op, const int mval[2], float rad) { BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB)); Scene *scene = vc->scene; ViewLayer *view_layer = vc->view_layer; View3D *v3d = vc->v3d; - - const float radius_squared = rad * rad; - const float mval_fl[2] = {float(mval[0]), float(mval[1])}; - - bool changed = false; - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); - } + float region_co[2]; + const int circle_data[3] = {mval[0], mval[1], int(rad)}; + float mval_fl[2] = {float(mval[0]), float(mval[1])}; + float radius_squared = rad * rad; + const int object_type = object_style(op, false, true); + const bool wireless_touch = U.wireless_touch_object && object_type == 1 && + v3d->shading.type == OB_WIRE && XRAY_FLAG_ENABLED(v3d); const bool select = (sel_op != SEL_OP_SUB); const int select_flag = select ? BASE_SELECTED : 0; + bool changed = false; + + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + changed |= object_deselect_all_visible(scene, view_layer, v3d); + } BKE_view_layer_synced_ensure(scene, view_layer); - LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { - if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) { - float screen_co[2]; - if (ED_view3d_project_float_global(vc->region, - base->object->object_to_world[3], - screen_co, - V3D_PROJ_TEST_CLIP_DEFAULT) == V3D_PROJ_RET_OK) - { - if (len_squared_v2v2(mval_fl, screen_co) <= radius_squared) { - ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); - changed = true; + + if (wireless_touch) { + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { + if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) { + if (base->object->type == OB_MESH) { + if (object_filter(vc, base, object_type, nullptr, 0, nullptr, circle_data, false)) { + ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + else { + if (ED_view3d_project_float_global(vc->region, + base->object->object_to_world[3], + region_co, + V3D_PROJ_TEST_CLIP_DEFAULT) == V3D_PROJ_RET_OK) + + { + if (len_squared_v2v2(mval_fl, region_co) <= radius_squared) { + ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); + changed = true; + } + } } } } } + else { + GPUSelectBuffer buffer; + BKE_object_update_select_id(CTX_data_main(vc->C)); + int hits = 0; + int point[4][2] = {}; + rcti rect; + blender::Vector bases; + for (int i = 0; i < 4; i++) { + float angle = 6.28f * (i + 1) / 4; + point[i][0] = circle_data[0] + circle_data[2] * cosf(angle); + point[i][1] = circle_data[1] + circle_data[2] * sinf(angle); + } + rect.xmin = rect.xmax = point[0][0]; + rect.ymin = rect.ymax = point[0][1]; + for (int i = 1; i < 4; i++) { + if (point[i][0] < rect.xmin) { + rect.xmin = point[i][0]; + } + else if (point[i][0] > rect.xmax) { + rect.xmax = point[i][0]; + } + if (point[i][1] < rect.ymin) { + rect.ymin = point[i][1]; + } + else if (point[i][1] > rect.ymax) { + rect.ymax = point[i][1]; + } + } + const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(scene, + vc->obact); + if (drag_select_through(v3d, op, false, false, true)) { + hits = view3d_opengl_select(vc, &buffer, &rect, VIEW3D_SELECT_ALL, select_filter); + } + else { + hits = view3d_opengl_select(vc, &buffer, &rect, VIEW3D_SELECT_PICK_NEAREST, select_filter); + } + + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } + ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer); + + if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) { + goto finally; + } + + LISTBASE_FOREACH (Base *, base, object_bases) { + if (BASE_SELECTABLE(v3d, base)) { + if ((base->object->runtime->select_id & 0x0000FFFF) != 0) { + bases.append(base); + } + } + } + /* The draw order doesn't always match the order we populate the engine, see: T51695. */ + qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp); + for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits; + buf_iter < buf_end; + buf_iter++) + { + bPoseChannel *pchan_dummy; + Base *base = ED_armature_base_and_pchan_from_select_buffer( + bases, buf_iter->id, &pchan_dummy); + if (base != nullptr) { + base->object->id.tag |= LIB_TAG_DOIT; + } + } + + for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { + if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) { + const bool is_select = base->flag & BASE_SELECTED; + bool is_inside = false; + if (base->object->type == OB_MESH && (object_type == 1 || object_type == 2)) { + is_inside = base->object->id.tag & LIB_TAG_DOIT ? + object_filter( + vc, base, object_type, nullptr, 0, nullptr, circle_data, false) ? + true : + false : + false; + } + else { + is_inside = base->object->id.tag & LIB_TAG_DOIT && + (ED_view3d_project_float_global(vc->region, + base->object->object_to_world[3], + region_co, + V3D_PROJ_TEST_CLIP_DEFAULT) == + V3D_PROJ_RET_OK) && + (len_squared_v2v2(mval_fl, region_co) <= radius_squared); + } + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + + finally: + if (changed) { + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(vc->C, NC_SCENE | ND_OB_SELECT, scene); + } + } return changed; } @@ -5286,10 +7376,17 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) static_cast(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture)); ViewContext vc = ED_view3d_viewcontext_init(C, depsgraph); + bool square = U.square_select; + rcti rect; Object *obact = vc.obact; Object *obedit = vc.obedit; + if (square) { + BLI_rcti_init(&rect, mval[0] - radius, mval[0] + radius, mval[1] - radius, mval[1] + radius); + BKE_object_update_select_id(CTX_data_main(C)); + } + if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & OB_MODE_POSE))) { view3d_operator_needs_opengl(C); if (obedit == nullptr) { @@ -5305,16 +7402,31 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) obedit = vc.obedit; if (obedit) { - obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius)); + obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius), op, square, &rect); } else if (BKE_paint_select_face_test(obact)) { - paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius)); + if (square) { + do_paintface_box_select(&vc, wm_userdata, &rect, sel_op, true); + } + else { + paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius)); + } } else if (BKE_paint_select_vert_test(obact)) { - paint_vertsel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius)); + if (square) { + do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op, true); + } + else { + paint_vertsel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius)); + } } else if (obact->mode & OB_MODE_POSE) { - pose_circle_select(&vc, sel_op, mval, float(radius)); + if (square) { + do_pose_box_select(C, &vc, &rect, sel_op); + } + else { + pose_circle_select(&vc, sel_op, mval, float(radius)); + } ED_outliner_select_sync_from_pose_bone_tag(C); } else { @@ -5324,8 +7436,15 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) FOREACH_OBJECT_IN_MODE_END; } else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) { - if (PE_circle_select(C, wm_userdata, sel_op, mval, float(radius))) { - return OPERATOR_FINISHED; + if (square) { + if (PE_box_select(C, &rect, sel_op)) { + return OPERATOR_FINISHED; + } + } + else { + if (PE_circle_select(C, wm_userdata, sel_op, mval, float(radius))) { + return OPERATOR_FINISHED; + } } return OPERATOR_CANCELLED; } @@ -5337,15 +7456,28 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) nullptr) { ED_view3d_viewcontext_init_object(&vc, obact_pose); - pose_circle_select(&vc, sel_op, mval, float(radius)); + if (square) { + do_pose_box_select(C, &vc, &rect, sel_op); + } + else { + pose_circle_select(&vc, sel_op, mval, float(radius)); + } ED_outliner_select_sync_from_pose_bone_tag(C); } else { - if (object_circle_select(&vc, sel_op, mval, float(radius))) { - DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); - - ED_outliner_select_sync_from_object_tag(C); + if (square) { + if (do_object_box_select(C, &vc, &rect, sel_op, op, true)) { + DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); + ED_outliner_select_sync_from_object_tag(C); + } + } + else { + if (object_circle_select(&vc, sel_op, op, mval, float(radius))) { + DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); + ED_outliner_select_sync_from_object_tag(C); + } } } diff --git a/source/blender/editors/util/ed_transverts.cc b/source/blender/editors/util/ed_transverts.cc index 98766ba5306..bd06d6f9414 100644 --- a/source/blender/editors/util/ed_transverts.cc +++ b/source/blender/editors/util/ed_transverts.cc @@ -315,6 +315,13 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, editmesh_eval_cage, set_mapped_co, userdata, MESH_FOREACH_NOP); } } + + /* Fix vert indices for backfacing mesh filter */ + int index = 0; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + BM_elem_index_set(eve, index); + index++; + } } else if (obedit->type == OB_ARMATURE) { bArmature *arm = static_cast(obedit->data); diff --git a/source/blender/editors/uvedit/uvedit_select.cc b/source/blender/editors/uvedit/uvedit_select.cc index 6bdf5f63741..1ac45afa113 100644 --- a/source/blender/editors/uvedit/uvedit_select.cc +++ b/source/blender/editors/uvedit/uvedit_select.cc @@ -3724,6 +3724,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) int x, y, radius, width, height; float zoomx, zoomy; float offset[2], ellipse[2]; + rctf rectf; const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? (ts->selectmode == SCE_SELECT_FACE) : @@ -3733,6 +3734,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) (ts->uv_selectmode == UV_SELECT_EDGE)); const bool use_select_linked = !(ts->uv_flag & UV_SYNC_SELECTION) && (ts->uv_selectmode == UV_SELECT_ISLAND); + const bool square = U.square_select; /* get operator properties */ x = RNA_int_get(op->ptr, "x"); @@ -3749,6 +3751,11 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) UI_view2d_region_to_view(®ion->v2d, x, y, &offset[0], &offset[1]); + if (square) { + BLI_rctf_init(&rectf, x - radius, x + radius, y - radius, y + radius); + UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); + } + bool changed_multi = false; Vector objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( @@ -3782,7 +3789,9 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) if (select != uvedit_face_select_test(scene, efa, offsets)) { float cent[2]; BM_face_uv_calc_center_median(efa, offsets.uv, cent); - if (uv_circle_select_is_point_inside(cent, offset, ellipse)) { + if (square ? BLI_rctf_isect_pt_v(&rectf, cent) : + uv_circle_select_is_point_inside(cent, offset, ellipse)) + { BM_elem_flag_enable(efa, BM_ELEM_TAG); changed = true; } @@ -3805,7 +3814,9 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv); - if (uv_circle_select_is_edge_inside(luv, luv_prev, offset, ellipse)) { + if (square ? BLI_rctf_isect_pt_v(&rectf, luv) && BLI_rctf_isect_pt_v(&rectf, luv_prev) : + uv_circle_select_is_edge_inside(luv, luv_prev, offset, ellipse)) + { uvedit_edge_select_set_with_sticky(scene, em, l_prev, select, false, offsets); changed = true; } @@ -3825,7 +3836,9 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { if (select != uvedit_uv_select_test(scene, l, offsets)) { luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv); - if (uv_circle_select_is_point_inside(luv, offset, ellipse)) { + if (square ? BLI_rctf_isect_pt_v(&rectf, luv) : + uv_circle_select_is_point_inside(luv, offset, ellipse)) + { changed = true; uvedit_uv_select_set(scene, em->bm, l, select, false, offsets); BM_elem_flag_enable(l->v, BM_ELEM_TAG); @@ -3919,7 +3932,7 @@ static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region, ®ion->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) && BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) && BLI_lasso_is_edge_inside( - mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) + mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED, false)) { return true; } diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index dc9e9d868f8..078b35eb734 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2451,6 +2451,14 @@ typedef enum eSnapTransformMode { SCE_SNAP_TRANSFORM_MODE_SCALE = (1 << 2), } eSnapTransformMode; +/** #Auto X-Ray mode */ +enum { + OPERATOR_MODE_A = (1 << 0), + OPERATOR_MODE_B = (1 << 1), + OPERATOR_MODE_C = (1 << 2), + OPERATOR_MODE_D = (1 << 3), +}; + /** #ToolSettings::selectmode */ enum { SCE_SELECT_VERTEX = 1 << 0, /* for mesh */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 96977c5cd11..0df439048d8 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -967,6 +967,7 @@ typedef struct UserDef { /** Flags for which channels to insert keys at. */ short key_insert_channels; // eKeyInsertChannels char _pad15[6]; + /** Flags for animation. */ short animation_flag; @@ -1035,11 +1036,152 @@ typedef struct UserDef { float collection_instance_empty_size; char text_flag; - char _pad10[1]; char file_preview_type; /* eUserpref_File_Preview_Type */ char statusbar_flag; /* eUserpref_StatusBar_Flag */ + /** Luke's Custom Build Options */ + char keymap_direction; + char custom_highlight; + char header_highlight; + char xray_button; + char direction_downright_box; + char direction_downright_lasso; + char auto_xray_reset; + char custom_cursor; + char system_cursor; + char edit_cursor; + char paint_cursor; + char dot_cursor; + char knife_cursor; + char pencil_cursor; + char eraser_cursor; + char eyedropper_cursor; + char adjustable_click_select; + char selection_radius; + char select_unbiased; + char blank_text; + char drag_control_mode; + char userpref_mode; + char drag_direction_box; + char drag_direction_lasso; + char auto_xray_box; + char auto_xray_downright_box; + char select_through_box; + char select_through_downright_box; + char object_select_box; + char object_select_downright_box; + char face_select_box; + char face_select_downright_box; + char edge_select_box; + char edge_select_downright_box; + char backface_box; + char backface_downright_box; + char auto_xray_lasso; + char auto_xray_downright_lasso; + char select_through_lasso; + char select_through_downright_lasso; + char object_select_lasso; + char object_select_downright_lasso; + char face_select_lasso; + char face_select_downright_lasso; + char edge_select_lasso; + char edge_select_downright_lasso; + char backface_lasso; + char backface_downright_lasso; + char auto_xray_circle; + char select_through_circle; + char object_select_circle; + char face_select_circle; + char edge_select_circle; + char backface_circle; + char object_header; + char object_cycle_touch; + char object_cycle_enclose; + char object_cycle_origin; + char face_header; + char face_cycle_default; + char face_cycle_touch; + char face_cycle_enclose; + char face_cycle_center; + char edge_header; + char edge_cycle_default; + char edge_cycle_touch; + char edge_cycle_enclose; + char backface_header; + char auto_xray_header; + char select_through_header; + char object_cycle_touch_circle; + char object_cycle_enclose_circle; + char object_cycle_origin_circle; + char face_cycle_default_circle; + char face_cycle_touch_circle; + char face_cycle_enclose_circle; + char face_cycle_center_circle; + char edge_cycle_touch_circle; + char edge_cycle_enclose_circle; + char backface_toggle_circle; + char auto_xray_toggle_circle; + char select_through_toggle_circle; + char object_cycle_touch_lasso; + char object_cycle_enclose_lasso; + char object_cycle_origin_lasso; + char face_cycle_default_lasso; + char face_cycle_touch_lasso; + char face_cycle_enclose_lasso; + char face_cycle_center_lasso; + char edge_cycle_default_lasso; + char edge_cycle_touch_lasso; + char edge_cycle_enclose_lasso; + char backface_toggle_lasso; + char auto_xray_toggle_lasso; + char select_through_toggle_lasso; + char quick_assign; + char quick_assign_mode; + char operator_controls; + char box_controls; + char circle_controls; + char lasso_controls; + char backface_toggle; + char auto_xray_toggle; + char select_through_toggle; + char direction_controls; + char drag_direction_mode; + char direction_object; + char direction_face; + char direction_edge; + char direction_backface; + char direction_auto_xray; + char direction_select_through; + char object_pie; + char face_pie; + char edge_pie; + char backface_pie; + char auto_xray_pie; + char select_through_pie; + char box_pie; + char circle_pie; + char lasso_pie; + char pie_menu_controls; + char object_operator; + char face_operator; + char edge_operator; + char backface_operator; + char auto_xray_operator; + char select_through_operator; + char wireless_touch_object; + char square_select; + char sync_box; + char sync_circle; + char sync_lasso; + char facedot_mode; + char shading_button_mode; + char shading_cycle_wire; + char shading_cycle_solid; + char shading_cycle_material; + char shading_cycle_render; + char _pad9[6]; + struct WalkNavigation walk_navigation; /** The UI for the user preferences. */ @@ -1082,6 +1224,7 @@ typedef enum eUserPref_Section { USER_SECTION_FILE_PATHS = 15, USER_SECTION_EXPERIMENTAL = 16, USER_SECTION_EXTENSIONS = 17, + USER_SECTION_CUSTOM = 18, } eUserPref_Section; /** #UserDef_SpaceData.flag (State of the user preferences UI). */ @@ -1247,6 +1390,33 @@ typedef enum eUserpref_TableAPI { USER_TABLET_WINTAB = 2, } eUserpref_TabletAPI; +/** #UserDef.mode */ +typedef enum eUserpref_Mode { + USER_MODE_A = 0, + USER_MODE_B = 1, + USER_MODE_C = 2, + USER_MODE_D = 3, +} eUserpref_Mode; + +/** #UserDef.cursor_types */ +typedef enum eUserpref_Cursor_Types { + USER_CURSOR_SYSTEM = 0, + USER_CURSOR_CROSS = 1, + USER_CURSOR_POINTER = 2, + USER_CURSOR_CROSS_OPEN = 3, + USER_CURSOR_CROSS_DOT = 4, + USER_CURSOR_CROSS_MIN = 5, + USER_CURSOR_BOX = 6, + USER_CURSOR_BOX_DOT = 7, + USER_CURSOR_BOX_POINTER = 8, + USER_CURSOR_DOT = 9, + USER_CURSOR_BLANK = 10, + USER_CURSOR_KNIFE = 11, + USER_CURSOR_PENCIL = 12, + USER_CURSOR_ERASER = 13, + USER_CURSOR_EYEDROPPER = 14, +} eUserpref_Cursor_Types; + /** #UserDef.app_flag */ typedef enum eUserpref_APP_Flag { USER_APP_LOCK_CORNER_SPLIT = (1 << 0), diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 4dd7e16cb11..41fff0473c4 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -610,6 +610,7 @@ enum { V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21), V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS = (1 << 22), + V3D_OVERLAY_EDIT_FACE_DOT_XRAY = (1 << 23), }; /** #View3DOverlay.paint_flag */ diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index c9f7d2d1725..8d4b475030b 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -4661,6 +4661,16 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) "Display face center when face selection is enabled in solid shading modes"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, nullptr); + prop = RNA_def_property(srna, "show_face_center_xray", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "overlay.edit_flag", V3D_OVERLAY_EDIT_FACE_DOT_XRAY); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text( + prop, + "Display Face Center X-Ray", + "Display face center when face selection is enabled in X-Ray shading modes"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, nullptr); + prop = RNA_def_property(srna, "show_edge_crease", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "overlay.edit_flag", V3D_OVERLAY_EDIT_CREASES); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index fb55898f09f..9cd4e2c9858 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -74,6 +74,7 @@ const EnumPropertyItem rna_enum_preference_section_items[] = { {USER_SECTION_FILE_PATHS, "FILE_PATHS", 0, "File Paths", ""}, RNA_ENUM_ITEM_SEPR, {USER_SECTION_EXPERIMENTAL, "EXPERIMENTAL", 0, "Experimental", ""}, + {USER_SECTION_CUSTOM, "CUSTOM", 0, "Luke's Custom Build", ""}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -6210,6 +6211,774 @@ static void rna_def_userdef_input(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem keymap_direction_items[] = { + {USER_MODE_A, "EIGHT", 0, "Eight", "Eight directions (N, NE, E, SE, S, SW, W, NW)"}, + {USER_MODE_B, "LEFT_RIGHT", 0, "Left Right", "Left and right"}, + {USER_MODE_C, "UP_DOWN", 0, "Up Down", "Up and down"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem quick_assign_mode_items[] = { + {USER_MODE_A, "DEFAULT", 0, "Default", "Blender default"}, + {USER_MODE_B, "BETTER", 0, "Better", "Improved selection settings"}, + {USER_MODE_C, "AUTO_XRAY", 0, "Auto X-Ray", "Auto X-Ray with Touch object and mesh"}, + {USER_MODE_D, + "DIRECTIONAL", + 0, + "Directional", + "Use drag directional control for Touch/Enlocse object and mesh with Box Select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem user_direction_box_items[] = { + {USER_MODE_A, "ANY", 0, "Any", "Any direction"}, + {USER_MODE_B, "LEFT_RIGHT", 0, "Left Right", "Left and right"}, + {USER_MODE_C, "UP_DOWN", 0, "Up Down", "Up and down"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem user_direction_lasso_items[] = { + {USER_MODE_A, "ANY", 0, "Any", "Any direction"}, + {USER_MODE_B, "LEFT_RIGHT", 0, "Left Right", "Left and right"}, + {USER_MODE_C, "UP_DOWN", 0, "Up Down", "Up and down"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem user_direction_mode_items[] = { + {USER_MODE_A, "EVERYTHING", 0, "Everything", "Drag direction for every feature"}, + {USER_MODE_B, + "SELECTIVE", + 0, + "Selective", + "Drag direction only affects the features you opt into"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem drag_control_mode_items[] = { + {USER_MODE_A, + "KEYMAP", + 0, + "Keymap", + "The keymap will control additional selection options for box, circle, and lasso"}, + {USER_MODE_B, + "USERPREF", + 0, + "UserPref", + "User Preferences will control additional selection options for box, circle, and lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem userpref_mode_items[] = { + {USER_MODE_A, + "EASY", + 0, + "Easy", + "Box, Circle, and Lasso use the same settings, no directional control"}, + {USER_MODE_B, + "SPLIT", + 0, + "Split", + "Box, Circle, and Lasso have separate settings, directional control is available for Box " + "and Lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem xray_button_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Toggle X-Ray operator in the viewport header"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Toggle X-Ray operator in the viewport header"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem facedot_mode_items[] = { + {USER_MODE_A, + "CURRENT", + 0, + "Current", + "Toggle face centers in the current shading mode (X-Ray or Solid)"}, + {USER_MODE_B, "BOTH", 0, "Both", "Toggle face centers in both X-Ray and Solid shading"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem shading_button_mode_items[] = { + {USER_MODE_A, "DEFAULT", 0, "Default", "Multiple buttons that change shading mode"}, + {USER_MODE_B, "XRAY_COMBINE", 0, "X-Ray", "Combine the X-Ray and Shading header buttons"}, + {USER_MODE_C, + "CYCLE", + 0, + "Cycle", + "One button that cycles through your choice of shading modes"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem adjustable_click_select_items[] = { + {USER_MODE_A, + "DEFAULT", + 0, + "Default", + "Click selection of mesh has a radius of 75, except faces which have a radius of 0"}, + {USER_MODE_B, "CUSTOM", 0, "Custom", "Choose your own radius for click selection of mesh"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem custom_cursor_items[] = { + {USER_MODE_A, "DEFAULT", 0, "Default", "Default mouse cursors"}, + {USER_MODE_B, "CUSTOM", 0, "Custom", "Choose your own mouse cursors"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem custom_highlight_items[] = { + {USER_MODE_A, "DEFAULT", 0, "Default", "The header of the active window gets brighter by 5"}, + {USER_MODE_B, + "CUSTOM", + 0, + "Custom", + "Choose your own brightness for the active window's header"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem system_cursor_items[] = { + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edit_cursor_items[] = { + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem paint_cursor_items[] = { + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {USER_CURSOR_BLANK, "BLANK", 0, "Blank", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem dot_cursor_items[] = { + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_BLANK, "BLANK", 0, "Blank", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem knife_cursor_items[] = { + {USER_CURSOR_KNIFE, "KNIFE", 0, "Knife", ""}, + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem eyedropper_cursor_items[] = { + {USER_CURSOR_EYEDROPPER, "EYEDROPPER", 0, "Eyedropper", ""}, + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem eraser_cursor_items[] = { + {USER_CURSOR_ERASER, "ERASER", 0, "Eraser", ""}, + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem pencil_cursor_items[] = { + {USER_CURSOR_PENCIL, "PENCIL", 0, "Pencil", ""}, + {USER_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {USER_CURSOR_DOT, "DOT", 0, "Dot", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Cycle Object operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Cycle Object operator in the pie menu"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Cycle Face operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Cycle Face operator in the pie menu"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Cycle Edge operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Cycle Edge operator in the pie menu"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Toggle Backface operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Toggle Backface operator in the pie menu"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Toggle Auto X-Ray operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Toggle Auto X-Ray operator in the pie menu"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Toggle Select Through operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Toggle Select Through operator in the pie menu"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem box_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Box Select operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Box Select operator in the pie menu"}, + {USER_MODE_C, + "CONTEXT", + 0, + "Context", + "Show the Box Select operator in the pie menu if it is not the active tool"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem circle_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Circle Select operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Circle Select operator in the pie menu"}, + {USER_MODE_C, + "CONTEXT", + 0, + "Context", + "Show the Circle Select operator in the pie menu if it is not the active tool"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem lasso_pie_items[] = { + {USER_MODE_A, "SHOW", 0, "Show", "Show the Lasso Select operator in the pie menu"}, + {USER_MODE_B, "HIDE", 0, "Hide", "Hide the Lasso Select operator in the pie menu"}, + {USER_MODE_C, + "CONTEXT", + 0, + "Context", + "Show the Lasso Select operator in the pie menu if it is not the active tool"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_toggle_items[] = { + {USER_MODE_A, "BOTH", 0, "Both", "Toggle Automatic X-Ray for Object and Edit mode"}, + {USER_MODE_B, + "CURRENT", + 0, + "Current", + "Toggle Automatic X-Ray for the mode that is currently active (Object or Edit)"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_toggle_circle_items[] = { + {USER_MODE_A, "BOTH", 0, "Both", "Toggle Automatic X-Ray for Object and Edit mode"}, + {USER_MODE_B, + "CURRENT", + 0, + "Current", + "Toggle Automatic X-Ray for the mode that is currently active (Object or Edit)"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_toggle_lasso_items[] = { + {USER_MODE_A, "BOTH", 0, "Both", "Toggle Automatic X-Ray for Object and Edit mode"}, + {USER_MODE_B, + "CURRENT", + 0, + "Current", + "Toggle Automatic X-Ray for the mode that is currently active (Object or Edit)"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_toggle_items[] = { + {USER_MODE_A, "BOTH", 0, "Both", "Toggle Select Through for Object and Edit mode"}, + {USER_MODE_B, + "CURRENT", + 0, + "Current", + "Toggle Select Through for the mode that is currently active (Object or Edit)"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_toggle_circle_items[] = { + {USER_MODE_A, "BOTH", 0, "Both", "Toggle Select Through for Object and Edit mode"}, + {USER_MODE_B, + "CURRENT", + 0, + "Current", + "Toggle Select Through for the mode that is currently active (Object or Edit)"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_toggle_lasso_items[] = { + {USER_MODE_A, "BOTH", 0, "Both", "Toggle Select Through for Object and Edit mode"}, + {USER_MODE_B, + "CURRENT", + 0, + "Current", + "Toggle Select Through for the mode that is currently active (Object or Edit)"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_toggle_items[] = { + {USER_MODE_A, + "NONE", + 0, + "No Backface", + "Toggle the backfacing mesh filter in No Backface mode"}, + {USER_MODE_B, + "NEAR", + 0, + "Edge & Face Near", + "Toggle the backfacing mesh filter in Edge & Face Near mode"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_toggle_circle_items[] = { + {USER_MODE_A, + "NONE", + 0, + "No Backface", + "Toggle the backfacing mesh filter in No Backface mode"}, + {USER_MODE_B, + "NEAR", + 0, + "Edge & Face Near", + "Toggle the backfacing mesh filter in Edge & Face Near mode"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_toggle_lasso_items[] = { + {USER_MODE_A, + "NONE", + 0, + "No Backface", + "Toggle the backfacing mesh filter in No Backface mode"}, + {USER_MODE_B, + "NEAR", + 0, + "Edge & Face Near", + "Toggle the backfacing mesh filter in Edge & Face Near mode"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_box_items[] = { + {USER_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {USER_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {USER_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {USER_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_downright_box_items[] = { + {USER_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {USER_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {USER_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {USER_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_box_items[] = { + {USER_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {USER_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {USER_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {USER_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_downright_box_items[] = { + {USER_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {USER_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {USER_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {USER_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_select_box_items[] = { + {USER_MODE_A, "OBJECT_TOUCH", 0, "Touch", "Select objects that are touched by the box"}, + {USER_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the box"}, + {USER_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_select_downright_box_items[] = { + {USER_MODE_A, "OBJECT_TOUCH", 0, "Touch", "Select objects that are touched by the box"}, + {USER_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the box"}, + {USER_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_box_items[] = { + {USER_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the box in Near Select. Select faces if their center is " + "touched by the box in X-Ray and Select Through"}, + {USER_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the box"}, + {USER_MODE_C, "FACE_ENCLOSE", 0, "Enclose", "Select faces that are fully inside the box"}, + {USER_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_downright_box_items[] = { + {USER_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the box in Near Select. Select faces if their center is " + "touched by the box in X-Ray and Select Through"}, + {USER_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the box"}, + {USER_MODE_C, "FACE_ENCLOSE", 0, "Enclose", "Select faces that are fully inside the box"}, + {USER_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_box_items[] = { + {USER_MODE_A, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the box. If no edges are fully inside the box, select " + "edges that are touched by the box"}, + {USER_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the box"}, + {USER_MODE_C, "EDGE_ENCLOSE", 0, "Enclose", "Select edges that are fully inside the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_downright_box_items[] = { + {USER_MODE_A, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the box. If no edges are fully inside the box, select " + "edges that are touched by the box"}, + {USER_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the box"}, + {USER_MODE_C, "EDGE_ENCLOSE", 0, "Enclose", "Select edges that are fully inside the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_box_items[] = { + {USER_MODE_A, "DEFAULT", 0, "Default", "Select backfacing mesh"}, + {USER_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {USER_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_downright_box_items[] = { + {USER_MODE_A, "BACKFACE_DEFAULT", 0, "Default", "Select backfacing mesh"}, + {USER_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {USER_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_lasso_items[] = { + {USER_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {USER_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {USER_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {USER_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_downright_lasso_items[] = { + {USER_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {USER_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {USER_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {USER_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_lasso_items[] = { + {USER_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {USER_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {USER_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {USER_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_downright_lasso_items[] = { + {USER_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {USER_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {USER_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {USER_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_select_lasso_items[] = { + {USER_MODE_A, "OBJECT_TOUCH", 0, "Touch", "Select objects that are touched by the lasso"}, + {USER_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the lasso"}, + {USER_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_select_downright_lasso_items[] = { + {USER_MODE_A, "OBJECT_TOUCH", 0, "Touch", "Select objects that are touched by the lasso"}, + {USER_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the lasso"}, + {USER_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_lasso_items[] = { + {USER_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the lasso in Near Select. Select faces if their center " + "is " + "touched by the lasso in X-Ray and Select Through"}, + {USER_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the lasso"}, + {USER_MODE_C, "FACE_ENCLOSE", 0, "Enclose", "Select faces that are fully inside the lasso"}, + {USER_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_downright_lasso_items[] = { + {USER_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the lasso in Near Select. Select faces if their center " + "is " + "touched by the lasso in X-Ray and Select Through"}, + {USER_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the lasso"}, + {USER_MODE_C, "FACE_ENCLOSE", 0, "Enclose", "Select faces that are fully inside the lasso"}, + {USER_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_lasso_items[] = { + {USER_MODE_A, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the lasso. If no edges are fully inside the lasso, " + "select " + "edges that are touched by the lasso"}, + {USER_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the lasso"}, + {USER_MODE_C, "EDGE_ENCLOSE", 0, "Enclose", "Select edges that are fully inside the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_downright_lasso_items[] = { + {USER_MODE_A, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the lasso. If no edges are fully inside the lasso, " + "select " + "edges that are touched by the lasso"}, + {USER_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the lasso"}, + {USER_MODE_C, "EDGE_ENCLOSE", 0, "Enclose", "Select edges that are fully inside the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_lasso_items[] = { + {USER_MODE_A, "DEFAULT", 0, "Default", "Select backfacing mesh"}, + {USER_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {USER_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_downright_lasso_items[] = { + {USER_MODE_A, "BACKFACE_DEFAULT", 0, "Default", "Select backfacing mesh"}, + {USER_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {USER_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem auto_xray_circle_items[] = { + {USER_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {USER_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {USER_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {USER_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_circle_items[] = { + {USER_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {USER_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {USER_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {USER_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_select_circle_items[] = { + {USER_MODE_A, "OBJECT_TOUCH", 0, "Touch", "Select objects that are touched by the circle"}, + {USER_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the circle"}, + {USER_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the circle"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_circle_items[] = { + {USER_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the circle in Near Select. Select faces if their center " + "is " + "touched by the circle in X-Ray and Select Through"}, + {USER_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the circle"}, + {USER_MODE_C, "FACE_ENCLOSE", 0, "Enclose", "Select faces that are fully inside the circle"}, + {USER_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the circle"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_circle_items[] = { + {USER_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the circle"}, + {USER_MODE_C, "EDGE_ENCLOSE", 0, "Enclose", "Select edges that are fully inside the circle"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_circle_items[] = { + {USER_MODE_A, "DEFAULT", 0, "Default", "Select backfacing mesh"}, + {USER_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {USER_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + static const EnumPropertyItem view_zoom_styles[] = { {USER_ZOOM_CONTINUE, "CONTINUE", @@ -6316,6 +7085,736 @@ static void rna_def_userdef_input(BlenderRNA *brna) "Release Confirms", "Moving things with a mouse drag confirms when releasing the button"); + /* Drag Select Options */ + prop = RNA_def_property(srna, "auto_xray_reset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "auto_xray_reset", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Reset", "Helper that turns xray off for autoxray"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "direction_downright_box", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_downright_box", 0); + RNA_def_property_ui_text(prop, "", ""); + + prop = RNA_def_property(srna, "direction_downright_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_downright_lasso", 0); + RNA_def_property_ui_text(prop, "", ""); + + prop = RNA_def_property(srna, "facedot_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, facedot_mode_items); + RNA_def_property_enum_sdna(prop, nullptr, "facedot_mode"); + RNA_def_property_ui_text(prop, + "Facedot Toggle", + "Toggle face centers in both solid and X-Ray shading, or only the " + "shading mode that is currently in use"); + + prop = RNA_def_property(srna, "shading_button_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, shading_button_mode_items); + RNA_def_property_enum_sdna(prop, nullptr, "shading_button_mode"); + RNA_def_property_ui_text(prop, "Shading Button", "Shading button style in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "shading_cycle_wire", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "shading_cycle_wire", 0); + RNA_def_property_ui_text( + prop, "", "The viewport shading button will include wireframe shading in its cycle"); + + prop = RNA_def_property(srna, "shading_cycle_solid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "shading_cycle_solid", 0); + RNA_def_property_ui_text( + prop, "", "The viewport shading button will include solid shading in its cycle"); + + prop = RNA_def_property(srna, "shading_cycle_material", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "shading_cycle_material", 0); + RNA_def_property_ui_text( + prop, "", "The viewport shading button will include material preview shading in its cycle"); + + prop = RNA_def_property(srna, "shading_cycle_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "shading_cycle_render", 0); + RNA_def_property_ui_text( + prop, "", "The viewport shading button will include rendered shading in its cycle"); + + prop = RNA_def_property(srna, "sync_box", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "sync_box", 0); + RNA_def_property_ui_text(prop, + "Box", + "Keep box select properties the same as other synchronized selection " + "tools when changing them via operator and pie menu"); + + prop = RNA_def_property(srna, "sync_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "sync_circle", 0); + RNA_def_property_ui_text(prop, + "Circle", + "Keep circle select properties the same as other synchronized " + "selection tools when changing them via operator and pie menu"); + + prop = RNA_def_property(srna, "sync_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "sync_lasso", 0); + RNA_def_property_ui_text(prop, + "Lasso", + "Keep lasso select properties the same as other synchronized selection " + "tools when changing them via operator and pie menu"); + + prop = RNA_def_property(srna, "square_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "square_select", 0); + RNA_def_property_ui_text( + prop, + "Square", + "Change selection shape to a square with side length equal to the circle diameter"); + + prop = RNA_def_property(srna, "wireless_touch_object", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "wireless_touch_object", 0); + RNA_def_property_ui_text(prop, + "Wireless Touch", + "If using Touch select with X-Ray and Wireframe shading, select " + "objects if inside them but not intersecting any wires"); + + prop = RNA_def_property(srna, "adjustable_click_select", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, adjustable_click_select_items); + RNA_def_property_enum_sdna(prop, nullptr, "adjustable_click_select"); + RNA_def_property_ui_text( + prop, "Click Select", "Use additional options for single-click selection"); + + prop = RNA_def_property(srna, "select_unbiased", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "select_unbiased", 0); + RNA_def_property_ui_text( + prop, "Select Unbiased", "Click-select will not favor unselected mesh elements"); + + prop = RNA_def_property(srna, "selection_radius", PROP_INT, PROP_FACTOR); + RNA_def_property_range(prop, 0, 150); + RNA_def_property_ui_range(prop, 0, 150, 1, -1); + RNA_def_property_ui_text(prop, "Radius", "Size of single-click selection radius"); + + prop = RNA_def_property(srna, "custom_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, custom_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "custom_cursor"); + RNA_def_property_ui_text(prop, "Mouse Cursors", "Use custom mouse cursors"); + + prop = RNA_def_property(srna, "system_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, system_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "system_cursor"); + RNA_def_property_ui_text(prop, "System", "System mode cursor style"); + + prop = RNA_def_property(srna, "edit_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edit_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "edit_cursor"); + RNA_def_property_ui_text(prop, "Edit", "Edit mode cursor style"); + + prop = RNA_def_property(srna, "paint_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, paint_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "paint_cursor"); + RNA_def_property_ui_text(prop, "Paint", "Paint mode cursor style"); + + prop = RNA_def_property(srna, "dot_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, dot_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "dot_cursor"); + RNA_def_property_ui_text(prop, "2D", "2D mode cursor style"); + + prop = RNA_def_property(srna, "knife_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, knife_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "knife_cursor"); + RNA_def_property_ui_text(prop, "Knife", "Knife cursor style"); + + prop = RNA_def_property(srna, "eyedropper_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, eyedropper_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "eyedropper_cursor"); + RNA_def_property_ui_text(prop, "Eyedropper", "Eyedropper cursor style"); + + prop = RNA_def_property(srna, "eraser_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, eraser_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "eraser_cursor"); + RNA_def_property_ui_text(prop, "Eraser", "Eraser cursor style"); + + prop = RNA_def_property(srna, "pencil_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, pencil_cursor_items); + RNA_def_property_enum_sdna(prop, nullptr, "pencil_cursor"); + RNA_def_property_ui_text(prop, "Pencil", "Pencil cursor style"); + + prop = RNA_def_property(srna, "blank_text", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "blank_text", 0); + RNA_def_property_ui_text(prop, "", ""); + + prop = RNA_def_property(srna, "keymap_direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, keymap_direction_items); + RNA_def_property_ui_text( + prop, "Keymap Drag Directions", "Style of click-drag direction the keymap will use"); + + prop = RNA_def_property(srna, "custom_highlight", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, custom_highlight_items); + RNA_def_property_ui_text( + prop, "Header Highlights", "Change the brightness of the active header"); + + prop = RNA_def_property(srna, "header_highlight", PROP_INT, PROP_FACTOR); + RNA_def_property_range(prop, 0, 10); + RNA_def_property_ui_range(prop, 0, 10, 1, -1); + RNA_def_property_ui_text(prop, "Amount", "How much brighter the header of the active window is"); + + prop = RNA_def_property(srna, "drag_control_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, drag_control_mode_items); + RNA_def_property_ui_text(prop, + "Control Style", + "Use either the keymap or user preferences to control additional " + "selection options for box, circle and lasso"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "userpref_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, userpref_mode_items); + RNA_def_property_ui_text(prop, "Userpref Mode", "Complexity of the userpref control style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "xray_button", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, xray_button_items); + RNA_def_property_ui_text( + prop, + "X-Ray Button", + "Show an operator that toggles transparent scene display in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "object_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, object_pie_items); + RNA_def_property_ui_text( + prop, + "Object Pie", + "Show an operator that cycles object select modes in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "face_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_pie_items); + RNA_def_property_ui_text( + prop, + "Face Pie", + "Show an operator that cycles face select modes in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "edge_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_pie_items); + RNA_def_property_ui_text( + prop, + "Edge Pie", + "Show an operator that cycles edge select modes in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "backface_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_pie_items); + RNA_def_property_ui_text(prop, + "Backface Pie", + "Show an operator that toggles the backfacing mesh filter in the pie " + "menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "auto_xray_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_pie_items); + RNA_def_property_ui_text( + prop, + "Auto X-Ray Pie", + "Show an operator that toggles Automatic X-Ray in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "select_through_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_pie_items); + RNA_def_property_ui_text( + prop, + "Select_Through Pie", + "Show an operator that toggles Select Through in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "box_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, box_pie_items); + RNA_def_property_ui_text( + prop, + "Box Pie", + "Show an operator that switches to box select in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "circle_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, circle_pie_items); + RNA_def_property_ui_text( + prop, + "Circle Pie", + "Show an operator that switches to circle select in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "lasso_pie", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, lasso_pie_items); + RNA_def_property_ui_text( + prop, + "Lasso Pie", + "Show an operator that switches to lasso select in the pie menu (view3d.pie_drag_select)"); + + prop = RNA_def_property(srna, "quick_assign", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "quick_assign", 0); + RNA_def_property_ui_text(prop, "", "Quickly assign values to the drag select settings"); + + prop = RNA_def_property(srna, "operator_controls", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "operator_controls", 0); + RNA_def_property_ui_text(prop, "", "Operator settings"); + + prop = RNA_def_property(srna, "box_controls", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "box_controls", 0); + RNA_def_property_ui_text(prop, "", "Box settings"); + + prop = RNA_def_property(srna, "circle_controls", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "circle_controls", 0); + RNA_def_property_ui_text(prop, "", "Circle settings"); + + prop = RNA_def_property(srna, "lasso_controls", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "lasso_controls", 0); + RNA_def_property_ui_text(prop, "", "Lasso settings"); + + prop = RNA_def_property(srna, "pie_menu_controls", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "pie_menu_controls", 0); + RNA_def_property_ui_text(prop, "", "Pie Menu settings"); + + prop = RNA_def_property(srna, "object_operator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_operator", 0); + RNA_def_property_ui_text(prop, "", "Object operator"); + + prop = RNA_def_property(srna, "face_operator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_operator", 0); + RNA_def_property_ui_text(prop, "", "Face operator"); + + prop = RNA_def_property(srna, "edge_operator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_operator", 0); + RNA_def_property_ui_text(prop, "", "Edge operator"); + + prop = RNA_def_property(srna, "backface_operator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "backface_operator", 0); + RNA_def_property_ui_text(prop, "", "Backface operator"); + + prop = RNA_def_property(srna, "auto_xray_operator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "auto_xray_operator", 0); + RNA_def_property_ui_text(prop, "", "Auto X-Ray operator"); + + prop = RNA_def_property(srna, "select_through_operator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "select_through_operator", 0); + RNA_def_property_ui_text(prop, "", "Select Through operator"); + + prop = RNA_def_property(srna, "object_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_header", 0); + RNA_def_property_ui_text( + prop, + "Object Header Button", + "Show an operator that cycles object selection modes in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "object_cycle_touch", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_touch", 0); + RNA_def_property_ui_text( + prop, "Touch", "The object selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_enclose", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_enclose", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The object selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_origin", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_origin", 0); + RNA_def_property_ui_text( + prop, "Origin", "The object selection operator will include origin mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_touch_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_touch_circle", 0); + RNA_def_property_ui_text( + prop, "Touch", "The object selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_enclose_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_enclose_circle", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The object selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_origin_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_origin_circle", 0); + RNA_def_property_ui_text( + prop, "Origin", "The object selection operator will include origin mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_touch_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_touch_lasso", 0); + RNA_def_property_ui_text( + prop, "Touch", "The object selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_enclose_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_enclose_lasso", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The object selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "object_cycle_origin_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "object_cycle_origin_lasso", 0); + RNA_def_property_ui_text( + prop, "Origin", "The object selection operator will include origin mode in its cycle"); + + prop = RNA_def_property(srna, "face_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_header", 0); + RNA_def_property_ui_text( + prop, + "Face Header Button", + "Show an operator that cycles face selection modes in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "face_cycle_default", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_default", 0); + RNA_def_property_ui_text( + prop, "Default", "The face selection operator will include default mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_touch", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_touch", 0); + RNA_def_property_ui_text( + prop, "Touch", "The face selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_enclose", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_enclose", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The face selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_center", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_center", 0); + RNA_def_property_ui_text( + prop, "Center", "The face selection operator will include center mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_default_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_default_circle", 0); + RNA_def_property_ui_text( + prop, "Default", "The face selection operator will include default mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_touch_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_touch_circle", 0); + RNA_def_property_ui_text( + prop, "Touch", "The face selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_enclose_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_enclose_circle", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The face selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_center_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_center_circle", 0); + RNA_def_property_ui_text( + prop, "Center", "The face selection operator will include center mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_default_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_default_lasso", 0); + RNA_def_property_ui_text( + prop, "Default", "The face selection operator will include default mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_touch_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_touch_lasso", 0); + RNA_def_property_ui_text( + prop, "Touch", "The face selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_enclose_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_enclose_lasso", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The face selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "face_cycle_center_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "face_cycle_center_lasso", 0); + RNA_def_property_ui_text( + prop, "Center", "The face selection operator will include center mode in its cycle"); + + prop = RNA_def_property(srna, "edge_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_header", 0); + RNA_def_property_ui_text( + prop, + "Edge Header Button", + "Show an operator that cycles edge selection modes in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "edge_cycle_default", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_default", 0); + RNA_def_property_ui_text( + prop, "Default", "The edge selection operator will include default mode in its cycle"); + + prop = RNA_def_property(srna, "edge_cycle_touch", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_touch", 0); + RNA_def_property_ui_text( + prop, "Touch", "The edge selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "edge_cycle_enclose", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_enclose", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The edge selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "edge_cycle_touch_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_touch_circle", 0); + RNA_def_property_ui_text( + prop, "Touch", "The edge selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "edge_cycle_enclose_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_enclose_circle", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The edge selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "edge_cycle_default_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_default_lasso", 0); + RNA_def_property_ui_text( + prop, "Default", "The edge selection operator will include default mode in its cycle"); + + prop = RNA_def_property(srna, "edge_cycle_touch_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_touch_lasso", 0); + RNA_def_property_ui_text( + prop, "Touch", "The edge selection operator will include touch mode in its cycle"); + + prop = RNA_def_property(srna, "edge_cycle_enclose_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_cycle_enclose_lasso", 0); + RNA_def_property_ui_text( + prop, "Enclose", "The edge selection operator will include enclose mode in its cycle"); + + prop = RNA_def_property(srna, "auto_xray_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "auto_xray_header", 0); + RNA_def_property_ui_text(prop, + "Auto X-Ray Header Button", + "Show an operator that toggles Automatic X-Ray in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "auto_xray_toggle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_toggle_items); + RNA_def_property_ui_text( + prop, "Box Toggle", "Which mode the Automatic X-Ray operator will toggle"); + + prop = RNA_def_property(srna, "auto_xray_toggle_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_toggle_circle_items); + RNA_def_property_ui_text( + prop, "Circle Toggle", "Which mode the Automatic X-Ray operator will toggle"); + + prop = RNA_def_property(srna, "auto_xray_toggle_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_toggle_lasso_items); + RNA_def_property_ui_text( + prop, "Lasso Toggle", "Which mode the Automatic X-Ray operator will toggle"); + + prop = RNA_def_property(srna, "select_through_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "select_through_header", 0); + RNA_def_property_ui_text(prop, + "Select Through Header Button", + "Show an operator that toggles Select Through in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "select_through_toggle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_toggle_items); + RNA_def_property_ui_text( + prop, "Box Toggle", "Which mode the Select Through operator will toggle"); + + prop = RNA_def_property(srna, "select_through_toggle_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_toggle_circle_items); + RNA_def_property_ui_text( + prop, "Circle Toggle", "Which mode the Select Through operator will toggle"); + + prop = RNA_def_property(srna, "select_through_toggle_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_toggle_lasso_items); + RNA_def_property_ui_text( + prop, "Lasso Toggle", "Which mode the Select Through operator will toggle"); + + prop = RNA_def_property(srna, "backface_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "backface_header", 0); + RNA_def_property_ui_text( + prop, + "Backface Header Button", + "Show an operator that toggles the backfacing mesh filter in the viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "backface_toggle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_toggle_items); + RNA_def_property_ui_text( + prop, "Box Toggle", "Which mode the backfacing mesh filter operator will toggle"); + + prop = RNA_def_property(srna, "backface_toggle_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_toggle_circle_items); + RNA_def_property_ui_text( + prop, "Circle Toggle", "Which mode the backfacing mesh filter operator will toggle"); + + prop = RNA_def_property(srna, "backface_toggle_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_toggle_lasso_items); + RNA_def_property_ui_text( + prop, "Lasso Toggle", "Which mode the backfacing mesh filter operator will toggle"); + + prop = RNA_def_property(srna, "quick_assign_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, quick_assign_mode_items); + RNA_def_property_ui_text( + prop, "Quick Assign Mode", "Which preset the quick assign operator will apply"); + + prop = RNA_def_property(srna, "drag_direction_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, user_direction_box_items); + RNA_def_property_ui_text(prop, "Box Direction", "Use directional control with box select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "drag_direction_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, user_direction_lasso_items); + RNA_def_property_ui_text(prop, "Lasso Direction", "Use directional control with lasso select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "drag_direction_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, user_direction_mode_items); + RNA_def_property_ui_text( + prop, "Direction Affects", "Opt out of directional control for different features"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "direction_controls", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_controls", 0); + RNA_def_property_ui_text(prop, "", "Drag direction settings"); + + prop = RNA_def_property(srna, "direction_object", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_object", 0); + RNA_def_property_ui_text(prop, "Object", "Directional control will affect object select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "direction_face", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_face", 0); + RNA_def_property_ui_text(prop, "Face", "Directional control will affect face select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "direction_edge", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_edge", 0); + RNA_def_property_ui_text(prop, "Edge", "Directional control will affect edge select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "direction_backface", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_backface", 0); + RNA_def_property_ui_text(prop, "Backface", "Directional control will affect backface select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "direction_auto_xray", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_auto_xray", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray", "Directional control will affect auto xray"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "direction_select_through", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "direction_select_through", 0); + RNA_def_property_ui_text( + prop, "Select Through", "Directional control will affect select through"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "auto_xray_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_box_items); + RNA_def_property_ui_text(prop, "Auto X-Ray", "Transparent scene display during selection"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "auto_xray_downright_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_downright_box_items); + RNA_def_property_ui_text(prop, "Auto X-Ray", "Transparent scene display during selection"); + + prop = RNA_def_property(srna, "select_through_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_box_items); + RNA_def_property_ui_text(prop, "Select Through", "Select occluded objects and mesh"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "select_through_downright_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_downright_box_items); + RNA_def_property_ui_text(prop, "Select Through", "Select occluded objects and mesh"); + + prop = RNA_def_property(srna, "object_select_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, object_select_box_items); + RNA_def_property_ui_text(prop, "Object", "Object selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "object_select_downright_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, object_select_downright_box_items); + RNA_def_property_ui_text(prop, "Object", "Object selection style"); + + prop = RNA_def_property(srna, "face_select_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_box_items); + RNA_def_property_ui_text(prop, "Face", "Face selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "face_select_downright_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_downright_box_items); + RNA_def_property_ui_text(prop, "Face", "Face selection style"); + + prop = RNA_def_property(srna, "edge_select_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_box_items); + RNA_def_property_ui_text(prop, "Edge", "Edge selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "edge_select_downright_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_downright_box_items); + RNA_def_property_ui_text(prop, "Edge", "Edge selection style"); + + prop = RNA_def_property(srna, "backface_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_box_items); + RNA_def_property_ui_text( + prop, "Backface", "Select mesh based on the direction of their normals"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "backface_downright_box", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_downright_box_items); + RNA_def_property_ui_text( + prop, "Backface", "Select mesh based on the direction of their normals"); + + prop = RNA_def_property(srna, "auto_xray_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_lasso_items); + RNA_def_property_ui_text(prop, "Auto X-Ray", "Transparent scene display during selection"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "auto_xray_downright_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_downright_lasso_items); + RNA_def_property_ui_text(prop, "Auto X-Ray", "Transparent scene display during selection"); + + prop = RNA_def_property(srna, "select_through_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_lasso_items); + RNA_def_property_ui_text(prop, "Select Through", "Select occluded objects and mesh"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "select_through_downright_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_downright_lasso_items); + RNA_def_property_ui_text(prop, "Select Through", "Select occluded objects and mesh"); + + prop = RNA_def_property(srna, "object_select_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, object_select_lasso_items); + RNA_def_property_ui_text(prop, "Object", "Object selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "object_select_downright_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, object_select_downright_lasso_items); + RNA_def_property_ui_text(prop, "Object", "Object selection style"); + + prop = RNA_def_property(srna, "face_select_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_lasso_items); + RNA_def_property_ui_text(prop, "Face", "Face selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "face_select_downright_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_downright_lasso_items); + RNA_def_property_ui_text(prop, "Face", "Face selection style"); + + prop = RNA_def_property(srna, "edge_select_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_lasso_items); + RNA_def_property_ui_text(prop, "Edge", "Edge selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "edge_select_downright_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_downright_lasso_items); + RNA_def_property_ui_text(prop, "Edge", "Edge selection style"); + + prop = RNA_def_property(srna, "backface_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_lasso_items); + RNA_def_property_ui_text( + prop, "Backface", "Select mesh based on the direction of their normals"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "backface_downright_lasso", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_downright_lasso_items); + RNA_def_property_ui_text( + prop, "Backface", "Select mesh based on the direction of their normals"); + + prop = RNA_def_property(srna, "auto_xray_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, auto_xray_circle_items); + RNA_def_property_ui_text(prop, "Auto X-Ray", "Transparent scene display during selection"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "select_through_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, select_through_circle_items); + RNA_def_property_ui_text(prop, "Select Through", "Select occluded objects and mesh"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "object_select_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, object_select_circle_items); + RNA_def_property_ui_text(prop, "Object", "Object selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "face_select_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_circle_items); + RNA_def_property_ui_text(prop, "Face", "Face selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "edge_select_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_circle_items); + RNA_def_property_ui_text(prop, "Edge", "Edge selection style"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + + prop = RNA_def_property(srna, "backface_circle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, backface_circle_items); + RNA_def_property_ui_text( + prop, "Backface", "Select mesh based on the direction of their normals"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); + prop = RNA_def_property(srna, "use_numeric_input_advanced", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", USER_FLAG_NUMINPUT_ADVANCED); RNA_def_property_ui_text(prop, diff --git a/source/blender/makesrna/intern/rna_wm_api.cc b/source/blender/makesrna/intern/rna_wm_api.cc index 8b25313aad5..e230fa8f8a8 100644 --- a/source/blender/makesrna/intern/rna_wm_api.cc +++ b/source/blender/makesrna/intern/rna_wm_api.cc @@ -421,6 +421,41 @@ static void rna_KeyMap_item_remove(wmKeyMap *km, ReportList *reports, PointerRNA RNA_POINTER_INVALIDATE(kmi_ptr); } +static void rna_keymap_set_direction_any(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = static_cast(kmi_ptr->data); + kmi->direction = -1; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_left(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = static_cast(kmi_ptr->data); + kmi->direction = 7; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_right(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = static_cast(kmi_ptr->data); + kmi->direction = 3; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_up(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = static_cast(kmi_ptr->data); + kmi->direction = 1; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_down(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = static_cast(kmi_ptr->data); + kmi->direction = 5; + WM_keyconfig_update_tag(km, kmi); +} + static PointerRNA rna_KeyMap_item_find_from_operator(ID *id, wmKeyMap *km, const char *idname, @@ -1278,6 +1313,31 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); + func = RNA_def_function(srna, "set_direction_any", "rna_keymap_set_direction_any"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); + + func = RNA_def_function(srna, "set_direction_left", "rna_keymap_set_direction_left"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); + + func = RNA_def_function(srna, "set_direction_right", "rna_keymap_set_direction_right"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); + + func = RNA_def_function(srna, "set_direction_up", "rna_keymap_set_direction_up"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); + + func = RNA_def_function(srna, "set_direction_down", "rna_keymap_set_direction_down"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); + func = RNA_def_function(srna, "from_id", "WM_keymap_item_find_id"); parm = RNA_def_property(func, "id", PROP_INT, PROP_NONE); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); diff --git a/source/blender/python/intern/bpy_rna.cc b/source/blender/python/intern/bpy_rna.cc index b6c658a52b5..93d55c89697 100644 --- a/source/blender/python/intern/bpy_rna.cc +++ b/source/blender/python/intern/bpy_rna.cc @@ -1356,14 +1356,14 @@ static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val) if (enum_item != rna_enum_dummy_NULL_items) { const char *ptr_name = RNA_struct_name_get_alloc(ptr, nullptr, 0, nullptr); - /* Prefer not to fail silently in case of API errors, maybe disable it later. */ + /* Prefer not to fail silently in case of API errors, maybe disable it later. CLOG_WARN(BPY_LOG_RNA, "current value '%d' " "matches no enum in '%s', '%s', '%s'", val, RNA_struct_identifier(ptr->type), ptr_name, - RNA_property_identifier(prop)); + RNA_property_identifier(prop));*/ #if 0 /* Gives Python decoding errors while generating docs :( */ char error_str[256]; diff --git a/source/blender/windowmanager/WM_api.hh b/source/blender/windowmanager/WM_api.hh index b7953353f1b..f175824ed9d 100644 --- a/source/blender/windowmanager/WM_api.hh +++ b/source/blender/windowmanager/WM_api.hh @@ -791,6 +791,7 @@ int WM_operator_call_notest(bContext *C, wmOperator *op); * Execute this operator again, put here so it can share above code */ int WM_operator_repeat(bContext *C, wmOperator *op); +int WM_operator_invoke_last(bContext *C, wmOperator *op); int WM_operator_repeat_last(bContext *C, wmOperator *op); /** * \return true if #WM_operator_repeat can run. @@ -1745,7 +1746,7 @@ bool WM_event_is_modal_drag_exit(const wmEvent *event, short init_event_val); bool WM_event_is_mouse_drag(const wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); -int WM_event_drag_direction(const wmEvent *event); +int WM_event_drag_direction(bContext *C, const wmEvent *event); char WM_event_utf8_to_ascii(const wmEvent *event) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** diff --git a/source/blender/windowmanager/WM_types.hh b/source/blender/windowmanager/WM_types.hh index 3bd132a83ec..1ba54223df5 100644 --- a/source/blender/windowmanager/WM_types.hh +++ b/source/blender/windowmanager/WM_types.hh @@ -559,6 +559,7 @@ struct wmNotifier { #define WM_GESTURE_LASSO 4 #define WM_GESTURE_CIRCLE 5 #define WM_GESTURE_STRAIGHTLINE 6 +#define WM_GESTURE_SQUARE 7 /** * wmGesture is registered to #wmWindow.gesture, handled by operator callbacks. diff --git a/source/blender/windowmanager/intern/wm_cursors.cc b/source/blender/windowmanager/intern/wm_cursors.cc index 48529de8fef..b2c9bf22894 100644 --- a/source/blender/windowmanager/intern/wm_cursors.cc +++ b/source/blender/windowmanager/intern/wm_cursors.cc @@ -41,17 +41,79 @@ struct BCursor { static BCursor *BlenderCursor[WM_CURSOR_NUM] = {nullptr}; +static GHOST_TStandardCursor window_get_custom_cursor(const int cursor) +{ + if (cursor == 1) { + return GHOST_kStandardCursorCrosshair; + } + else if (cursor == 2) { + return GHOST_kStandardCursorPointer; + } + else if (cursor == 3) { + return GHOST_kStandardCursorCrosshairD; + } + else if (cursor == 4) { + return GHOST_kStandardCursorCrosshairA; + } + else if (cursor == 5) { + return GHOST_kStandardCursorCrosshairC; + } + else if (cursor == 6) { + return GHOST_kStandardCursorBox; + } + else if (cursor == 7) { + return GHOST_kStandardCursorBoxDot; + } + else if (cursor == 8) { + return GHOST_kStandardCursorBoxPointer; + } + else if (cursor == 9) { + return GHOST_kStandardCursorCrosshairB; + } + else if (cursor == 10) { + return GHOST_kStandardCursorBlank; + } + else if (cursor == 11) { + return GHOST_kStandardCursorKnife; + } + else if (cursor == 12) { + return GHOST_kStandardCursorPencil; + } + else if (cursor == 13) { + return GHOST_kStandardCursorEraser; + } + else if (cursor == 14) { + return GHOST_kStandardCursorEyedropper; + } + else { + return GHOST_kStandardCursorDefault; + } +} + /* Blender cursor to GHOST standard cursor conversion. */ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) { + GHOST_TStandardCursor cursor = GHOST_kStandardCursorDefault; switch (curs) { case WM_CURSOR_DEFAULT: - return GHOST_kStandardCursorDefault; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.system_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorDefault; + } case WM_CURSOR_WAIT: return GHOST_kStandardCursorWait; case WM_CURSOR_EDIT: case WM_CURSOR_CROSS: - return GHOST_kStandardCursorCrosshair; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.edit_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorCrosshair; + } case WM_CURSOR_X_MOVE: return GHOST_kStandardCursorLeftRight; case WM_CURSOR_Y_MOVE: @@ -67,7 +129,13 @@ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) case WM_CURSOR_STOP: return GHOST_kStandardCursorStop; case WM_CURSOR_KNIFE: - return GHOST_kStandardCursorKnife; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.knife_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorKnife; + } case WM_CURSOR_NSEW_SCROLL: return GHOST_kStandardCursorNSEWScroll; case WM_CURSOR_NS_SCROLL: @@ -75,19 +143,43 @@ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) case WM_CURSOR_EW_SCROLL: return GHOST_kStandardCursorEWScroll; case WM_CURSOR_EYEDROPPER: - return GHOST_kStandardCursorEyedropper; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.eyedropper_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorEyedropper; + } case WM_CURSOR_N_ARROW: return GHOST_kStandardCursorUpArrow; case WM_CURSOR_S_ARROW: return GHOST_kStandardCursorDownArrow; case WM_CURSOR_PAINT: - return GHOST_kStandardCursorCrosshairA; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.paint_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorCrosshairA; + } case WM_CURSOR_DOT: - return GHOST_kStandardCursorCrosshairB; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.dot_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorCrosshairB; + } case WM_CURSOR_CROSSC: return GHOST_kStandardCursorCrosshairC; case WM_CURSOR_ERASER: - return GHOST_kStandardCursorEraser; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.eraser_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorEraser; + } case WM_CURSOR_ZOOM_IN: return GHOST_kStandardCursorZoomIn; case WM_CURSOR_ZOOM_OUT: @@ -95,7 +187,13 @@ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) case WM_CURSOR_TEXT_EDIT: return GHOST_kStandardCursorText; case WM_CURSOR_PAINT_BRUSH: - return GHOST_kStandardCursorPencil; + if (U.custom_cursor) { + cursor = window_get_custom_cursor(U.pencil_cursor); + return cursor; + } + else { + return GHOST_kStandardCursorPencil; + } case WM_CURSOR_E_ARROW: return GHOST_kStandardCursorRightArrow; case WM_CURSOR_W_ARROW: diff --git a/source/blender/windowmanager/intern/wm_event_query.cc b/source/blender/windowmanager/intern/wm_event_query.cc index b5b3cb6ceee..bf962401530 100644 --- a/source/blender/windowmanager/intern/wm_event_query.cc +++ b/source/blender/windowmanager/intern/wm_event_query.cc @@ -267,36 +267,86 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); } -int WM_event_drag_direction(const wmEvent *event) +int WM_event_drag_direction(bContext *C, const wmEvent *event) { const int delta[2] = { event->xy[0] - event->prev_press_xy[0], event->xy[1] - event->prev_press_xy[1], }; - int theta = round_fl_to_int(4.0f * atan2f(float(delta[1]), float(delta[0])) / float(M_PI)); - int val = KM_DIRECTION_W; + const bool box_left_right = U.drag_direction_box == 1 && U.drag_control_mode == 1; + const bool box_up_down = U.drag_direction_box == 2 && U.drag_control_mode == 1; + const bool lasso_left_right = U.drag_direction_lasso == 1 && U.drag_control_mode == 1; + const bool lasso_up_down = U.drag_direction_lasso == 2 && U.drag_control_mode == 1; + const bool left_right = U.keymap_direction == 1; + const bool up_down = U.keymap_direction == 2; + int val = up_down ? KM_DIRECTION_N : KM_DIRECTION_W; - if (theta == 0) { - val = KM_DIRECTION_E; + if (left_right || up_down || box_left_right || box_up_down || lasso_left_right || lasso_up_down) + { + const float thetaf = 4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI; + + if (U.direction_downright_box) { + U.direction_downright_box ^= true; + } + + if (U.direction_downright_lasso) { + U.direction_downright_lasso ^= true; + } + + if (thetaf > -2.0f && thetaf < 2.0f) { + if (left_right) { + val = KM_DIRECTION_E; + } + + if (box_left_right) { + U.direction_downright_box ^= true; + } + + if (lasso_left_right) { + U.direction_downright_lasso ^= true; + } + } + + if (thetaf < 0.0f) { + if (up_down) { + val = KM_DIRECTION_S; + } + + if (box_up_down) { + U.direction_downright_box ^= true; + } + + if (lasso_up_down) { + U.direction_downright_lasso ^= true; + } + } } - else if (theta == 1) { - val = KM_DIRECTION_NE; - } - else if (theta == 2) { - val = KM_DIRECTION_N; - } - else if (theta == 3) { - val = KM_DIRECTION_NW; - } - else if (theta == -1) { - val = KM_DIRECTION_SE; - } - else if (theta == -2) { - val = KM_DIRECTION_S; - } - else if (theta == -3) { - val = KM_DIRECTION_SW; + + if (!left_right && !up_down) { + const int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / + (float)M_PI); + if (theta == 0) { + val = KM_DIRECTION_E; + } + else if (theta == 1) { + val = KM_DIRECTION_NE; + } + else if (theta == 2) { + val = KM_DIRECTION_N; + } + else if (theta == 3) { + val = KM_DIRECTION_NW; + } + else if (theta == -1) { + val = KM_DIRECTION_SE; + } + else if (theta == -2) { + val = KM_DIRECTION_S; + } + else if (theta == -3) { + val = KM_DIRECTION_SW; + } } #if 0 diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 89b7769fc0f..92294e2de16 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -1323,6 +1323,11 @@ int WM_operator_repeat(bContext *C, wmOperator *op) op->flag &= ~op_flag; return ret; } +int WM_operator_invoke_last(bContext *C, wmOperator *op) +{ + const int ret = WM_operator_name_call_ptr(C, op->type, WM_OP_INVOKE_DEFAULT, nullptr, nullptr); + return ret; +} int WM_operator_repeat_last(bContext *C, wmOperator *op) { const int op_flag = OP_IS_REPEAT_LAST; @@ -3470,7 +3475,7 @@ static eHandlerActionFlag wm_handlers_do(bContext *C, wmEvent *event, ListBase * WM_event_drag_test(event, event->prev_press_xy)) { win->event_queue_check_drag_handled = true; - const int direction = WM_event_drag_direction(event); + const int direction = WM_event_drag_direction(C, event); /* Intentionally leave `event->xy` as-is, event users are expected to use * `event->prev_press_xy` if they need to access the drag start location. */ diff --git a/source/blender/windowmanager/intern/wm_gesture.cc b/source/blender/windowmanager/intern/wm_gesture.cc index e1c3005b2f2..ae8e339ab6a 100644 --- a/source/blender/windowmanager/intern/wm_gesture.cc +++ b/source/blender/windowmanager/intern/wm_gesture.cc @@ -56,6 +56,7 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, WM_GESTURE_CIRCLE, + WM_GESTURE_SQUARE, WM_GESTURE_STRAIGHTLINE)) { rcti *rect = static_cast(MEM_callocN(sizeof(rcti), "gesture rect new")); @@ -63,7 +64,7 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent gesture->customdata = rect; rect->xmin = xy[0] - gesture->winrct.xmin; rect->ymin = xy[1] - gesture->winrct.ymin; - if (type == WM_GESTURE_CIRCLE) { + if (type == WM_GESTURE_CIRCLE || type == WM_GESTURE_SQUARE) { /* caller is responsible for initializing 'xmax' to radius. */ } else { @@ -275,6 +276,51 @@ static void wm_gesture_draw_circle(wmGesture *gt) immUnbindProgram(); } +static void wm_gesture_draw_square(wmGesture *gt) +{ + rcti *rect = (rcti *)gt->customdata; + + uint shdr_pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + + GPU_blend(GPU_BLEND_ALPHA); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f); + + immRecti(shdr_pos, + rect->xmin - rect->xmax, + rect->ymin - rect->xmax, + rect->xmin + rect->xmax, + rect->ymin + rect->xmax); + + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + + shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("colors_len", 2); /* "advanced" mode */ + immUniform4f("color", 0.4f, 0.4f, 0.4f, 1.0f); + immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f); + immUniform1f("dash_width", 8.0f); + immUniform1f("udash_factor", 0.5f); + + imm_draw_box_wire_2d(shdr_pos, + float(rect->xmin - rect->xmax), + float(rect->ymin - rect->xmax), + float(rect->xmin + rect->xmax), + float(rect->ymin + rect->xmax)); + + immUnbindProgram(); +} + struct LassoFillData { uchar *px; int width; @@ -447,6 +493,9 @@ void wm_gesture_draw(wmWindow *win) else if (gt->type == WM_GESTURE_CIRCLE) { wm_gesture_draw_circle(gt); } + else if (gt->type == WM_GESTURE_SQUARE) { + wm_gesture_draw_square(gt); + } else if (gt->type == WM_GESTURE_CROSS_RECT) { if (gt->is_active) { wm_gesture_draw_rect(gt); diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.cc b/source/blender/windowmanager/intern/wm_gesture_ops.cc index 012e2c8441a..bc7a407ee3a 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.cc +++ b/source/blender/windowmanager/intern/wm_gesture_ops.cc @@ -31,6 +31,7 @@ #include "ED_screen.hh" #include "ED_select_utils.hh" +#include "ED_view3d.hh" #include "UI_interface.hh" @@ -87,6 +88,92 @@ static void gesture_modal_state_to_operator(wmOperator *op, int modal_state) } } +static void gesture_toggle_xray(bContext *C) +{ + wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_toggle_xray", true); + BLI_assert(ot); + PointerRNA ptr; + WM_operator_properties_create_ptr(&ptr, ot); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr); + WM_operator_properties_free(&ptr); + U.auto_xray_reset ^= true; +} + +static void gesture_auto_xray(bContext *C, wmOperator *op, const bool lasso, const bool circle) +{ + View3D *v3d = CTX_wm_view3d(C); + + if (!v3d || XRAY_FLAG_ENABLED(v3d)) { + return; + } + + Object *mesh = CTX_data_edit_object(C); + bool auto_xray = false; + + if (U.drag_control_mode == 1) { + if (U.userpref_mode == 1 && circle) { + if (mesh) { + auto_xray = U.auto_xray_circle == 2 || U.auto_xray_circle == 3; + } + else { + auto_xray = U.auto_xray_circle == 1 || U.auto_xray_circle == 3; + } + } + else if (U.userpref_mode == 1 && lasso) { + if (mesh) { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_auto_xray)) { + auto_xray = U.auto_xray_downright_lasso == 2 || U.auto_xray_downright_lasso == 3; + } + else { + auto_xray = U.auto_xray_lasso == 2 || U.auto_xray_lasso == 3; + } + } + else { + if (U.direction_downright_lasso && (U.drag_direction_mode == 0 || U.direction_auto_xray)) { + auto_xray = U.auto_xray_downright_lasso == 1 || U.auto_xray_downright_lasso == 3; + } + else { + auto_xray = U.auto_xray_lasso == 1 || U.auto_xray_lasso == 3; + } + } + } + else { + if (mesh) { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_auto_xray)) + { + auto_xray = U.auto_xray_downright_box == 2 || U.auto_xray_downright_box == 3; + } + else { + auto_xray = U.auto_xray_box == 2 || U.auto_xray_box == 3; + } + } + else { + if (U.userpref_mode == 1 && U.direction_downright_box && + (U.drag_direction_mode == 0 || U.direction_auto_xray)) + { + auto_xray = U.auto_xray_downright_box == 1 || U.auto_xray_downright_box == 3; + } + else { + auto_xray = U.auto_xray_box == 1 || U.auto_xray_box == 3; + } + } + } + } + else { + const int mode = RNA_enum_get(op->ptr, "auto_xray"); + auto_xray = mesh ? mode == 4 || mode == 8 : mode == 2 || mode == 8; + } + + if (U.auto_xray_reset) { + U.auto_xray_reset ^= true; + } + + if (auto_xray) { + gesture_toggle_xray(C); + } +} + static int UNUSED_FUNCTION(gesture_modal_state_from_operator)(wmOperator *op) { PropertyRNA *prop; @@ -169,6 +256,8 @@ int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && RNA_boolean_get(op->ptr, "wait_for_input"); + gesture_auto_xray(C, op, false, false); + if (wait_for_input) { op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT); } @@ -216,13 +305,22 @@ int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event) gesture->modal_state = event->val; } if (gesture_box_apply(C, op)) { + if (U.auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_FINISHED; } + if (U.auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } case GESTURE_MODAL_CANCEL: { + if (U.auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } @@ -290,8 +388,15 @@ int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event) wmWindow *win = CTX_wm_window(C); const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && RNA_boolean_get(op->ptr, "wait_for_input"); + gesture_auto_xray(C, op, false, true); + + if (U.square_select) { + op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_SQUARE); + } + else { + op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_CIRCLE); + } - op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_CIRCLE); wmGesture *gesture = static_cast(op->customdata); rcti *rect = static_cast(gesture->customdata); @@ -417,6 +522,9 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (is_finished) { + if (U.auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_FINISHED; /* use finish or we don't get an undo */ } @@ -481,6 +589,8 @@ int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event) wmWindow *win = CTX_wm_window(C); PropertyRNA *prop; + gesture_auto_xray(C, op, true, false); + op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_LASSO); /* add modal handler */ @@ -540,6 +650,10 @@ static int gesture_lasso_apply(bContext *C, wmOperator *op) OPERATOR_RETVAL_CHECK(retval); } + if (U.auto_xray_reset) { + gesture_toggle_xray(C); + } + return retval; } @@ -599,6 +713,9 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } case EVT_ESCKEY: { + if (U.auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/windowmanager/intern/wm_operator_props.cc b/source/blender/windowmanager/intern/wm_operator_props.cc index fad16982f65..6a0989fad11 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.cc +++ b/source/blender/windowmanager/intern/wm_operator_props.cc @@ -416,8 +416,116 @@ void WM_operator_properties_gesture_box_ex(wmOperatorType *ot, bool deselect, bo { PropertyRNA *prop; + static const EnumPropertyItem auto_xray_items[] = { + {OPERATOR_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {OPERATOR_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {OPERATOR_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {OPERATOR_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_items[] = { + {OPERATOR_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {OPERATOR_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {OPERATOR_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {OPERATOR_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_select_items[] = { + {OPERATOR_MODE_A, "OBJECT_TOUCH", 0, "Touch", "Select objects that are touched by the box"}, + {OPERATOR_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the box"}, + {OPERATOR_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_items[] = { + {OPERATOR_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the box in Near Select. Select faces if their center is " + "touched by the box in X-Ray and Select Through"}, + {OPERATOR_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the box"}, + {OPERATOR_MODE_C, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the box"}, + {OPERATOR_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {OPERATOR_MODE_A, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the box. If no edges are fully inside the box, select " + "edges that are touched by the box"}, + {OPERATOR_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the box"}, + {OPERATOR_MODE_C, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the box"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_filter_items[] = { + {OPERATOR_MODE_A, "BACKFACE_DEFAULT", 0, "Default", "Select backfacing mesh"}, + {OPERATOR_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {OPERATOR_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + WM_operator_properties_border(ot); + prop = RNA_def_enum(ot->srna, + "auto_xray", + auto_xray_items, + 0, + "Auto X-Ray", + "Transparent scene display during selection"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "select_through", + select_through_items, + 0, + "Select Through", + "Select occluded objects and mesh"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum( + ot->srna, "object_type", object_select_items, 0, "Object", "Object selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face", "Face selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge", "Edge selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "backface_filter", + backface_filter_items, + 0, + "Backface", + "Select mesh based on the direction of their normals"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + if (deselect) { prop = RNA_def_boolean( ot->srna, "deselect", false, "Deselect", "Deselect rather than select items"); @@ -521,9 +629,122 @@ void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } +static const EnumPropertyItem auto_xray_items[] = { + {OPERATOR_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {OPERATOR_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {OPERATOR_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {OPERATOR_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static const EnumPropertyItem select_through_items[] = { + {OPERATOR_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {OPERATOR_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {OPERATOR_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {OPERATOR_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + void WM_operator_properties_gesture_lasso(wmOperatorType *ot) { PropertyRNA *prop; + static const EnumPropertyItem object_select_items[] = { + {OPERATOR_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the lasso"}, + {OPERATOR_MODE_A, + "OBJECT_TOUCH", + 0, + "Touch", + "Select objects that are touched by the lasso"}, + {OPERATOR_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_items[] = { + {OPERATOR_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the lasso in Near Select. Select faces if their center " + "is touched by the lasso in X-Ray and Select Through"}, + {OPERATOR_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the lasso"}, + {OPERATOR_MODE_C, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the lasso"}, + {OPERATOR_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {OPERATOR_MODE_A, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the lasso. If no edges are fully inside the lasso, " + "select edges that are touched by the lasso"}, + {OPERATOR_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the lasso"}, + {OPERATOR_MODE_C, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the lasso"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_filter_items[] = { + {OPERATOR_MODE_A, "BACKFACE_DEFAULT", 0, "Default", "Select backfacing mesh"}, + {OPERATOR_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {OPERATOR_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + WM_operator_properties_border(ot); + + prop = RNA_def_enum(ot->srna, + "auto_xray", + auto_xray_items, + 0, + "Auto X-Ray", + "Transparent scene display during selection"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "select_through", + select_through_items, + 0, + "Select Through", + "Select occluded objects and mesh"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum( + ot->srna, "object_type", object_select_items, 0, "Object", "Object selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face", "Face selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge", "Edge selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "backface_filter", + backface_filter_items, + 0, + "Backface", + "Select mesh based on the direction of their normals"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -562,6 +783,83 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot) PropertyRNA *prop; const int radius_default = 25; + static const EnumPropertyItem auto_xray_items[] = { + {OPERATOR_MODE_A, "AUTO_XRAY_DISABLE", 0, "Disable", "No Auto X-Ray"}, + {OPERATOR_MODE_B, "AUTO_XRAY_OBJECT", 0, "Object", "X-Ray during object select"}, + {OPERATOR_MODE_C, "AUTO_XRAY_EDIT", 0, "Mesh", "X-Ray during mesh select"}, + {OPERATOR_MODE_D, "AUTO_XRAY_BOTH", 0, "Both", "X-Ray during object and mesh select"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem select_through_items[] = { + {OPERATOR_MODE_B, "SELECT_THROUGH_OBJECT", 0, "Object", "Select through objects"}, + {OPERATOR_MODE_C, "SELECT_THROUGH_EDIT", 0, "Mesh", "Select through mesh"}, + {OPERATOR_MODE_D, "SELECT_THROUGH_BOTH", 0, "Both", "Select through objects and mesh"}, + {OPERATOR_MODE_A, "SELECT_THROUGH_DISABLE", 0, "Disable", "No select through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem object_select_items[] = { + {OPERATOR_MODE_C, + "OBJECT_ORIGIN", + 0, + "Origin", + "Select objects if their origin is touched by the circle"}, + {OPERATOR_MODE_A, + "OBJECT_TOUCH", + 0, + "Touch", + "Select objects that are touched by the circle"}, + {OPERATOR_MODE_B, + "OBJECT_ENCLOSE", + 0, + "Enclose", + "Select objects that are fully inside the circle"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem face_select_items[] = { + {OPERATOR_MODE_A, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the circle in Near Select. Select faces if their center " + "is touched by the circle in X-Ray and Select Through"}, + {OPERATOR_MODE_B, "FACE_TOUCH", 0, "Touch", "Select faces that are touched by the circle"}, + {OPERATOR_MODE_C, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the circle"}, + {OPERATOR_MODE_D, + "FACE_CENTER", + 0, + "Center", + "Select faces if their center is touched by the circle"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {OPERATOR_MODE_B, "EDGE_TOUCH", 0, "Touch", "Select edges that are touched by the circle"}, + {OPERATOR_MODE_C, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the circle"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem backface_filter_items[] = { + {OPERATOR_MODE_A, "BACKFACE_DEFAULT", 0, "Default", "Select backfacing mesh"}, + {OPERATOR_MODE_B, "NONE", 0, "No Backface", "Don't select backfacing mesh"}, + {OPERATOR_MODE_C, + "NEAR", + 0, + "Edge & Face Near", + "Ignore backfacing edges and faces when not using X-Ray or Select Through"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + prop = RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); @@ -570,6 +868,35 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot) prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_enum(ot->srna, + "auto_xray", + auto_xray_items, + 0, + "Auto X-Ray", + "Transparent scene display during selection"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "select_through", + select_through_items, + 0, + "Select Through", + "Select occluded objects and mesh"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum( + ot->srna, "object_type", object_select_items, 0, "Object", "Object selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face", "Face selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge", "Edge selection style"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "backface_filter", + backface_filter_items, + 0, + "Backface", + "Select mesh based on the direction of their normals"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } void WM_operator_properties_mouse_select(wmOperatorType *ot)