Layer actions and navigations features
1.9.0 - feat: New shortcuts: - `F2` in Paint and Edit to rename active layer - `Insert` add a new layer (same as Krita) - `Shift + Insert` add a new layer and immediately pop-up a rename box - `page up / page down` change active layer up/down with a temporary fade (settings in addon prefs) - fix: error when tweaking `gp.duplicate_send_to_layer` shortcutgpv2
parent
3c7477c442
commit
97b09444ab
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,5 +1,15 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
|
||||||
|
1.9.0
|
||||||
|
|
||||||
|
- feat: New shortcuts:
|
||||||
|
- `F2` in Paint and Edit to rename active layer
|
||||||
|
- `Insert` add a new layer (same as Krita)
|
||||||
|
- `Shift + Insert` add a new layer and immediately pop-up a rename box
|
||||||
|
- `page up / page down` change active layer up/down with a temporary fade (settings in addon prefs)
|
||||||
|
- fix: error when tweaking `gp.duplicate_send_to_layer` shortcut
|
||||||
|
|
||||||
1.8.1
|
1.8.1
|
||||||
|
|
||||||
- fix: Gp clipboard paste `Paste layers` don't skip empty frames anymore
|
- fix: Gp clipboard paste `Paste layers` don't skip empty frames anymore
|
||||||
|
@ -13,7 +23,6 @@
|
||||||
1.7.8
|
1.7.8
|
||||||
|
|
||||||
- fix: reset rotation in draw cam mode keep view in the same place (counter camera rotation)
|
- fix: reset rotation in draw cam mode keep view in the same place (counter camera rotation)
|
||||||
- code: initial enhancement for palette linking
|
|
||||||
|
|
||||||
1.7.7
|
1.7.7
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,7 @@ class OBJ_OT_breakdown_obj_anim(bpy.types.Operator):
|
||||||
|
|
||||||
### --- KEYMAP ---
|
### --- KEYMAP ---
|
||||||
|
|
||||||
breakdowner_addon_keymaps = []
|
addon_keymaps = []
|
||||||
def register_keymaps():
|
def register_keymaps():
|
||||||
if bpy.app.background:
|
if bpy.app.background:
|
||||||
return
|
return
|
||||||
|
@ -322,16 +322,15 @@ def register_keymaps():
|
||||||
if ops_id not in km.keymap_items:
|
if ops_id not in km.keymap_items:
|
||||||
km = addon.keymaps.new(name='3D View', space_type='VIEW_3D')#EMPTY
|
km = addon.keymaps.new(name='3D View', space_type='VIEW_3D')#EMPTY
|
||||||
kmi = km.keymap_items.new(ops_id, type="E", value="PRESS", shift=True)
|
kmi = km.keymap_items.new(ops_id, type="E", value="PRESS", shift=True)
|
||||||
breakdowner_addon_keymaps.append((km, kmi))
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
def unregister_keymaps():
|
def unregister_keymaps():
|
||||||
if bpy.app.background:
|
if bpy.app.background:
|
||||||
return
|
return
|
||||||
for km, kmi in breakdowner_addon_keymaps:
|
for km, kmi in addon_keymaps:
|
||||||
km.keymap_items.remove(kmi)
|
km.keymap_items.remove(kmi)
|
||||||
|
|
||||||
breakdowner_addon_keymaps.clear()
|
addon_keymaps.clear()
|
||||||
# del breakdowner_addon_keymaps[:]
|
|
||||||
|
|
||||||
### --- REGISTER ---
|
### --- REGISTER ---
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@ from bpy.types import Operator
|
||||||
|
|
||||||
def get_layer_list(self, context):
|
def get_layer_list(self, context):
|
||||||
'''return (identifier, name, description) of enum content'''
|
'''return (identifier, name, description) of enum content'''
|
||||||
|
if not context:
|
||||||
|
return [('None', 'None','None')]
|
||||||
if not context.object:
|
if not context.object:
|
||||||
return
|
return [('None', 'None','None')]
|
||||||
return [(l.info, l.info, '') for l in context.object.data.layers if l != context.object.data.layers.active]
|
return [(l.info, l.info, '') for l in context.object.data.layers if l != context.object.data.layers.active]
|
||||||
# try:
|
# try:
|
||||||
# except:
|
# except:
|
||||||
|
@ -23,7 +25,8 @@ class GPTB_OT_duplicate_send_to_layer(Operator) :
|
||||||
layers_enum : bpy.props.EnumProperty(
|
layers_enum : bpy.props.EnumProperty(
|
||||||
name="Duplicate to layers",
|
name="Duplicate to layers",
|
||||||
description="Duplicate selected keys in active layer and send them to choosen layer",
|
description="Duplicate selected keys in active layer and send them to choosen layer",
|
||||||
items=get_layer_list
|
items=get_layer_list,
|
||||||
|
options={'HIDDEN'},
|
||||||
)
|
)
|
||||||
|
|
||||||
delete_source : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
|
delete_source : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
|
||||||
|
@ -110,12 +113,10 @@ def register_keymaps():
|
||||||
# if not pref.kfj_use_shortcut:
|
# if not pref.kfj_use_shortcut:
|
||||||
# return
|
# return
|
||||||
addon = bpy.context.window_manager.keyconfigs.addon
|
addon = bpy.context.window_manager.keyconfigs.addon
|
||||||
# km = addon.keymaps.new(name = "Screen", space_type = "EMPTY")
|
|
||||||
km = addon.keymaps.new(name = "Dopesheet", space_type = "DOPESHEET_EDITOR")
|
km = addon.keymaps.new(name = "Dopesheet", space_type = "DOPESHEET_EDITOR")
|
||||||
kmi = km.keymap_items.new('gp.duplicate_send_to_layer', type='D', value="PRESS", ctrl=True, shift=True)
|
kmi = km.keymap_items.new('gp.duplicate_send_to_layer', type='D', value="PRESS", ctrl=True, shift=True)
|
||||||
addon_keymaps.append((km,kmi))
|
addon_keymaps.append((km,kmi))
|
||||||
|
|
||||||
|
|
||||||
# km = addon.keymaps.new(name = "Dopesheet", space_type = "DOPESHEET_EDITOR") # try duplicating km (seem to be error at unregsiter)
|
# km = addon.keymaps.new(name = "Dopesheet", space_type = "DOPESHEET_EDITOR") # try duplicating km (seem to be error at unregsiter)
|
||||||
kmi = km.keymap_items.new('gp.duplicate_send_to_layer', type='X', value="PRESS", ctrl=True, shift=True)
|
kmi = km.keymap_items.new('gp.duplicate_send_to_layer', type='X', value="PRESS", ctrl=True, shift=True)
|
||||||
kmi.properties.delete_source = True
|
kmi.properties.delete_source = True
|
||||||
|
|
|
@ -633,6 +633,107 @@ def subscribe_handler(dummy):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
##--- Add layers
|
||||||
|
|
||||||
|
class GPTB_PT_layer_name_ui(bpy.types.Panel):
|
||||||
|
bl_space_type = 'TOPBAR' # dummy
|
||||||
|
bl_region_type = 'HEADER'
|
||||||
|
bl_options = {'INSTANCED'}
|
||||||
|
bl_label = 'Layer Rename'
|
||||||
|
bl_ui_units_x = 14
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
# all_addons_l = get_modifier_list()
|
||||||
|
wm = context.window_manager
|
||||||
|
wm.invoke_props_dialog(self) # , width=600
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
# def row_with_icon(layout, icon):
|
||||||
|
# # Edit first editable button in popup
|
||||||
|
# row = layout.row()
|
||||||
|
# row.activate_init = True
|
||||||
|
# row.label(icon=icon)
|
||||||
|
# return row
|
||||||
|
# row = row_with_icon(layout, 'OUTLINER_DATA_GP_LAYER')
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.activate_init = True
|
||||||
|
row.label(icon='OUTLINER_DATA_GP_LAYER')
|
||||||
|
row.prop(context.object.data.layers.active, 'info', text='')
|
||||||
|
|
||||||
|
def add_layer(context):
|
||||||
|
bpy.ops.gpencil.layer_add()
|
||||||
|
context.object.data.layers.active.use_lights = False
|
||||||
|
|
||||||
|
class GPTB_OT_add_gp_layer_with_rename(Operator):
|
||||||
|
bl_idname = "gp.add_layer_rename"
|
||||||
|
bl_label = "Add Rename GPencil Layer"
|
||||||
|
bl_description = "Create a new gp layer with use light toggled off and popup a rename box"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
add_layer(context)
|
||||||
|
bpy.ops.wm.call_panel(name="GPTB_PT_layer_name_ui", keep_open = False)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class GPTB_OT_add_gp_layer(Operator):
|
||||||
|
bl_idname = "gp.add_layer"
|
||||||
|
bl_label = "Add GPencil Layer"
|
||||||
|
bl_description = "Create a new gp layer with use light toggled off"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
add_layer(context)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
addon_keymaps = []
|
||||||
|
def register_keymaps():
|
||||||
|
addon = bpy.context.window_manager.keyconfigs.addon
|
||||||
|
|
||||||
|
##---# Insert Layers
|
||||||
|
## Insert new gp layer (with no use_light)
|
||||||
|
km = addon.keymaps.new(name = "Grease Pencil", space_type = "EMPTY") # global (only paint ?)
|
||||||
|
kmi = km.keymap_items.new('gp.add_layer', type='INSERT', value='PRESS')
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
## Insert new gp layer (with no use_light and immediately pop up a box to rename)
|
||||||
|
# km = addon.keymaps.new(name = "Grease Pencil", space_type = "EMPTY") # global (only paint ?)
|
||||||
|
kmi = km.keymap_items.new('gp.add_layer_rename', type='INSERT', value='PRESS', shift=True)
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
##---# F2 rename calls
|
||||||
|
## Direct rename active layer in Paint mode
|
||||||
|
km = addon.keymaps.new(name = "Grease Pencil Stroke Paint Mode", space_type = "EMPTY")
|
||||||
|
kmi = km.keymap_items.new('wm.call_panel', type='F2', value='PRESS')
|
||||||
|
kmi.properties.name = 'GPTB_PT_layer_name_ui'
|
||||||
|
kmi.properties.keep_open = False
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
## Same in edit mode
|
||||||
|
km = addon.keymaps.new(name = "Grease Pencil Stroke Edit Mode", space_type = "EMPTY")
|
||||||
|
kmi = km.keymap_items.new('wm.call_panel', type='F2', value='PRESS')
|
||||||
|
kmi.properties.name = 'GPTB_PT_layer_name_ui'
|
||||||
|
kmi.properties.keep_open = False
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
|
||||||
|
def unregister_keymaps():
|
||||||
|
for km, kmi in addon_keymaps:
|
||||||
|
km.keymap_items.remove(kmi)
|
||||||
|
addon_keymaps.clear()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
classes=(
|
classes=(
|
||||||
GPTB_OT_rename_gp_layer,
|
GPTB_OT_rename_gp_layer,
|
||||||
GPTB_OT_layer_name_build,
|
GPTB_OT_layer_name_build,
|
||||||
|
@ -640,9 +741,14 @@ classes=(
|
||||||
GPTB_OT_layer_new_group,
|
GPTB_OT_layer_new_group,
|
||||||
GPTB_OT_select_set_same_prefix,
|
GPTB_OT_select_set_same_prefix,
|
||||||
GPTB_OT_select_set_same_color,
|
GPTB_OT_select_set_same_color,
|
||||||
|
|
||||||
|
## Layer add and pop-up rename
|
||||||
|
GPTB_PT_layer_name_ui, # pop-up
|
||||||
|
GPTB_OT_add_gp_layer_with_rename, # shift+Ins
|
||||||
|
GPTB_OT_add_gp_layer, # Ins
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
@ -650,8 +756,10 @@ def register():
|
||||||
bpy.types.DOPESHEET_HT_header.append(gpencil_dopesheet_header)
|
bpy.types.DOPESHEET_HT_header.append(gpencil_dopesheet_header)
|
||||||
bpy.types.GPENCIL_MT_layer_context_menu.append(gpencil_layer_dropdown_menu)
|
bpy.types.GPENCIL_MT_layer_context_menu.append(gpencil_layer_dropdown_menu)
|
||||||
bpy.app.handlers.load_post.append(subscribe_handler) # need to restart after first activation
|
bpy.app.handlers.load_post.append(subscribe_handler) # need to restart after first activation
|
||||||
|
register_keymaps()
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
|
unregister_keymaps()
|
||||||
bpy.app.handlers.load_post.remove(subscribe_handler)
|
bpy.app.handlers.load_post.remove(subscribe_handler)
|
||||||
bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu)
|
bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu)
|
||||||
bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header)
|
bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header)
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
import bpy
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
class GPT_OT_layer_nav(bpy.types.Operator):
|
||||||
|
bl_idname = "gp.layer_nav"
|
||||||
|
bl_label = "GP Layer Navigator"
|
||||||
|
bl_description = "Change active GP layer and highlight active for a moment"
|
||||||
|
bl_options = {'REGISTER', 'INTERNAL', 'UNDO'}
|
||||||
|
|
||||||
|
direction : bpy.props.EnumProperty(
|
||||||
|
name='direction',
|
||||||
|
items=(('NONE', 'None', ''),('UP', 'Up', ''),('DOWN', 'Down', '')),
|
||||||
|
default='NONE',
|
||||||
|
description='Direction to change layer in active GPencil stack',
|
||||||
|
options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
## hardcoded values
|
||||||
|
# interval = 0.04 # 0.1
|
||||||
|
# limit = 1.8
|
||||||
|
# fade_val = 0.35
|
||||||
|
# use_fade_in = True
|
||||||
|
# fade_in_time = 0.5
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
## initialise vvalue from prefs
|
||||||
|
prefs = utils.get_addon_prefs()
|
||||||
|
|
||||||
|
if not prefs.nav_use_fade:
|
||||||
|
if self.direction == 'DOWN' or (event.type == 'PAGE_DOWN' and event.value == 'PRESS'):
|
||||||
|
utils.iterate_selector(context.object.data.layers, 'active_index', -1, info_attr = 'info')
|
||||||
|
|
||||||
|
if self.direction == 'UP' or (event.type == 'PAGE_UP' and event.value == 'PRESS'):
|
||||||
|
utils.iterate_selector(context.object.data.layers, 'active_index', 1, info_attr = 'info')
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
self.interval = prefs.nav_interval
|
||||||
|
self.limit = prefs.nav_limit
|
||||||
|
self.fade_val = prefs.nav_fade_val
|
||||||
|
self.use_fade_in = prefs.nav_use_fade_in
|
||||||
|
self.fade_in_time = prefs.nav_fade_in_time
|
||||||
|
|
||||||
|
self.lapse = 0
|
||||||
|
wm = context.window_manager
|
||||||
|
args = (self, context)
|
||||||
|
|
||||||
|
if context.space_data.overlay.use_gpencil_fade_layers:
|
||||||
|
self.fade_target = context.space_data.overlay.gpencil_fade_layer
|
||||||
|
else:
|
||||||
|
self.fade_target = 1.0
|
||||||
|
|
||||||
|
self.fade_start = self.limit - self.fade_in_time
|
||||||
|
|
||||||
|
self.first = True
|
||||||
|
self._timer = wm.event_timer_add(self.interval, window=context.window) # 0.1
|
||||||
|
wm.modal_handler_add(self)
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def store_settings(self, context):
|
||||||
|
self.org_use_gpencil_fade_layers = context.space_data.overlay.use_gpencil_fade_layers
|
||||||
|
self.org_gpencil_fade_layer = context.space_data.overlay.gpencil_fade_layer
|
||||||
|
context.space_data.overlay.use_gpencil_fade_layers = True
|
||||||
|
context.space_data.overlay.gpencil_fade_layer = self.fade_val
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
|
trigger = False
|
||||||
|
if event.type in {'RIGHTMOUSE', 'ESC', 'LEFTMOUSE'}:
|
||||||
|
self.stop_mod(context)
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
if event.type == 'TIMER':
|
||||||
|
self.lapse += self.interval
|
||||||
|
|
||||||
|
if self.lapse >= self.limit:
|
||||||
|
self.stop_mod(context)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
## Fade
|
||||||
|
if self.use_fade_in and (self.lapse > self.fade_start):
|
||||||
|
fade = utils.transfer_value(self.lapse, self.fade_start, self.limit, self.fade_val, self.fade_target)
|
||||||
|
# print(f'lapse {self.lapse} - fade {fade}')
|
||||||
|
context.space_data.overlay.gpencil_fade_layer = fade
|
||||||
|
|
||||||
|
if self.direction == 'DOWN' or (event.type == 'PAGE_DOWN' and event.value == 'PRESS'):
|
||||||
|
_val = utils.iterate_selector(context.object.data.layers, 'active_index', -1, info_attr = 'info')
|
||||||
|
trigger = True
|
||||||
|
|
||||||
|
if self.direction == 'UP' or (event.type == 'PAGE_UP' and event.value == 'PRESS'):
|
||||||
|
_val = utils.iterate_selector(context.object.data.layers, 'active_index', 1, info_attr = 'info')
|
||||||
|
# utils.iterate_selector(bpy.context.scene.grease_pencil.layers, 'active_index', 1, info_attr = 'info')#layers
|
||||||
|
trigger = True
|
||||||
|
|
||||||
|
if trigger:
|
||||||
|
self.direction = 'NONE'
|
||||||
|
if self.first:
|
||||||
|
self.store_settings(context)
|
||||||
|
self.first=False
|
||||||
|
|
||||||
|
if self.use_fade_in:
|
||||||
|
# reset fade to wanted value
|
||||||
|
context.space_data.overlay.gpencil_fade_layer = self.fade_val
|
||||||
|
|
||||||
|
self.lapse = 0 # reset counter
|
||||||
|
return {'RUNNING_MODAL'}#running modal prevent original usage to be triggered (capture keys)
|
||||||
|
|
||||||
|
return {'PASS_THROUGH'}
|
||||||
|
|
||||||
|
def stop_mod(self, context):
|
||||||
|
# restore fade
|
||||||
|
context.space_data.overlay.use_gpencil_fade_layers = self.org_use_gpencil_fade_layers
|
||||||
|
context.space_data.overlay.gpencil_fade_layer = self.org_gpencil_fade_layer
|
||||||
|
wm = context.window_manager
|
||||||
|
wm.event_timer_remove(self._timer)
|
||||||
|
|
||||||
|
|
||||||
|
addon_keymaps = []
|
||||||
|
|
||||||
|
def register_keymap():
|
||||||
|
addon = bpy.context.window_manager.keyconfigs.addon
|
||||||
|
|
||||||
|
km = addon.keymaps.new(name = "Grease Pencil Stroke Paint Mode", space_type = "EMPTY")
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new('gp.layer_nav', type='PAGE_UP', value='PRESS')
|
||||||
|
kmi.properties.direction = 'UP'
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new('gp.layer_nav', type='PAGE_DOWN', value='PRESS')
|
||||||
|
kmi.properties.direction = 'DOWN'
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
def unregister_keymap():
|
||||||
|
for km, kmi in addon_keymaps:
|
||||||
|
km.keymap_items.remove(kmi)
|
||||||
|
|
||||||
|
addon_keymaps.clear()
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(GPT_OT_layer_nav)
|
||||||
|
register_keymap()
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
unregister_keymap()
|
||||||
|
bpy.utils.unregister_class(GPT_OT_layer_nav)
|
57
__init__.py
57
__init__.py
|
@ -15,7 +15,7 @@ bl_info = {
|
||||||
"name": "GP toolbox",
|
"name": "GP toolbox",
|
||||||
"description": "Tool set for Grease Pencil in animation production",
|
"description": "Tool set for Grease Pencil in animation production",
|
||||||
"author": "Samuel Bernou, Christophe Seux",
|
"author": "Samuel Bernou, Christophe Seux",
|
||||||
"version": (1, 8, 1),
|
"version": (1, 9, 0),
|
||||||
"blender": (2, 91, 0),
|
"blender": (2, 91, 0),
|
||||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
@ -51,6 +51,7 @@ from . import OP_depth_move
|
||||||
from . import OP_key_duplicate_send
|
from . import OP_key_duplicate_send
|
||||||
from . import OP_layer_manager
|
from . import OP_layer_manager
|
||||||
from . import OP_layer_picker
|
from . import OP_layer_picker
|
||||||
|
from . import OP_layer_nav
|
||||||
from . import OP_material_picker
|
from . import OP_material_picker
|
||||||
from . import OP_eraser_brush
|
from . import OP_eraser_brush
|
||||||
from . import TOOL_eraser_brush
|
from . import TOOL_eraser_brush
|
||||||
|
@ -310,6 +311,38 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
||||||
|
|
||||||
fixprops: bpy.props.PointerProperty(type = GP_PG_FixSettings)
|
fixprops: bpy.props.PointerProperty(type = GP_PG_FixSettings)
|
||||||
|
|
||||||
|
## GP Layer navigator
|
||||||
|
|
||||||
|
nav_use_fade : BoolProperty(
|
||||||
|
name='Fade Inactive Layers',
|
||||||
|
description='Fade Inactive layers to determine active layer in a glimpse',
|
||||||
|
default=True)
|
||||||
|
|
||||||
|
nav_fade_val : FloatProperty(
|
||||||
|
name='Fade Value',
|
||||||
|
description='Fade value for other layers when navigating (0=invisible)',
|
||||||
|
default=0.35, min=0.0, max=0.95, step=1, precision=2)
|
||||||
|
|
||||||
|
nav_limit : FloatProperty(
|
||||||
|
name='Fade Duration',
|
||||||
|
description='Time of other layer faded when using layer navigation',
|
||||||
|
default=1.4, min=0.1, max=5, step=2, precision=1, subtype='TIME', unit='TIME')
|
||||||
|
|
||||||
|
nav_use_fade_in : BoolProperty(
|
||||||
|
name='Progressive Fade Back',
|
||||||
|
description='Use a fade on other layer when navigating',
|
||||||
|
default=True)
|
||||||
|
|
||||||
|
nav_fade_in_time : FloatProperty(
|
||||||
|
name='Fade-In Time',
|
||||||
|
description='Duration of the fade',
|
||||||
|
default=0.5, min=0.1, max=5, step=2, precision=2, subtype='TIME', unit='TIME')
|
||||||
|
|
||||||
|
nav_interval : FloatProperty(
|
||||||
|
name='Refresh Rate',
|
||||||
|
description='Refresh rate for fade updating (upper value means stepped fade)',
|
||||||
|
default=0.04, min=0.01, max=0.5, step=3, precision=2, subtype='TIME', unit='TIME')
|
||||||
|
|
||||||
## Temp cutter
|
## Temp cutter
|
||||||
# temp_cutter_use_shortcut: BoolProperty(
|
# temp_cutter_use_shortcut: BoolProperty(
|
||||||
# name = "Use temp cutter Shortcut",
|
# name = "Use temp cutter Shortcut",
|
||||||
|
@ -415,6 +448,21 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
||||||
|
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text='Tools options:')
|
box.label(text='Tools options:')
|
||||||
|
|
||||||
|
subbox= box.box()
|
||||||
|
subbox.label(text='Layer Navigation')
|
||||||
|
col = subbox.column()
|
||||||
|
col.prop(self, 'nav_use_fade')
|
||||||
|
if self.nav_use_fade:
|
||||||
|
row = col.row()
|
||||||
|
row.prop(self, 'nav_fade_val')
|
||||||
|
row.prop(self, 'nav_limit')
|
||||||
|
row = subbox.row(align=False)
|
||||||
|
row.prop(self, 'nav_use_fade_in')
|
||||||
|
if self.nav_use_fade_in:
|
||||||
|
row.prop(self, 'nav_fade_in_time', text='Fade Back Time')
|
||||||
|
# row.prop(self, 'nav_interval') # Do not expose refresh rate for now, not usefull to user...
|
||||||
|
|
||||||
box.prop(self, 'use_precise_eraser')
|
box.prop(self, 'use_precise_eraser')
|
||||||
|
|
||||||
if self.pref_tabs == 'KEYS':
|
if self.pref_tabs == 'KEYS':
|
||||||
|
@ -427,14 +475,15 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
||||||
## TOOL_eraser_brush.addon_keymaps # has a checkbox in
|
## TOOL_eraser_brush.addon_keymaps # has a checkbox in
|
||||||
|
|
||||||
prev_key_category = ''
|
prev_key_category = ''
|
||||||
kmi_see_list = []
|
|
||||||
for kms in [
|
for kms in [
|
||||||
OP_keyframe_jump.addon_keymaps,
|
OP_keyframe_jump.addon_keymaps,
|
||||||
OP_copy_paste.addon_keymaps,
|
OP_copy_paste.addon_keymaps,
|
||||||
OP_breakdowner.breakdowner_addon_keymaps,
|
OP_breakdowner.addon_keymaps,
|
||||||
OP_key_duplicate_send.addon_keymaps,
|
OP_key_duplicate_send.addon_keymaps,
|
||||||
OP_layer_picker.addon_keymaps,
|
OP_layer_picker.addon_keymaps,
|
||||||
OP_material_picker.addon_keymaps,
|
OP_material_picker.addon_keymaps,
|
||||||
|
OP_layer_nav.addon_keymaps,
|
||||||
|
# OP_layer_manager.addon_keymaps, # Do not display, wm.call_panel call panel ops mixed with natives shortcut (F2)
|
||||||
]:
|
]:
|
||||||
|
|
||||||
ct = 0
|
ct = 0
|
||||||
|
@ -644,6 +693,7 @@ def register():
|
||||||
OP_eraser_brush.register()
|
OP_eraser_brush.register()
|
||||||
OP_material_picker.register()
|
OP_material_picker.register()
|
||||||
OP_layer_picker.register()
|
OP_layer_picker.register()
|
||||||
|
OP_layer_nav.register()
|
||||||
TOOL_eraser_brush.register()
|
TOOL_eraser_brush.register()
|
||||||
handler_draw_cam.register()
|
handler_draw_cam.register()
|
||||||
UI_tools.register()
|
UI_tools.register()
|
||||||
|
@ -670,6 +720,7 @@ def unregister():
|
||||||
UI_tools.unregister()
|
UI_tools.unregister()
|
||||||
handler_draw_cam.unregister()
|
handler_draw_cam.unregister()
|
||||||
TOOL_eraser_brush.unregister()
|
TOOL_eraser_brush.unregister()
|
||||||
|
OP_layer_nav.unregister()
|
||||||
OP_layer_picker.unregister()
|
OP_layer_picker.unregister()
|
||||||
OP_material_picker.unregister()
|
OP_material_picker.unregister()
|
||||||
OP_eraser_brush.unregister()
|
OP_eraser_brush.unregister()
|
||||||
|
|
|
@ -8,6 +8,8 @@ def register_keymaps():
|
||||||
# km = addon.keymaps.new(name = "3D View", space_type = "VIEW_3D")# in 3D context
|
# km = addon.keymaps.new(name = "3D View", space_type = "VIEW_3D")# in 3D context
|
||||||
# km = addon.keymaps.new(name = "Window", space_type = "EMPTY")# from everywhere
|
# km = addon.keymaps.new(name = "Window", space_type = "EMPTY")# from everywhere
|
||||||
|
|
||||||
|
|
||||||
|
## Sculpt mode toggles
|
||||||
km = addon.keymaps.new(name = "Grease Pencil Stroke Sculpt Mode", space_type = "EMPTY", region_type='WINDOW')
|
km = addon.keymaps.new(name = "Grease Pencil Stroke Sculpt Mode", space_type = "EMPTY", region_type='WINDOW')
|
||||||
|
|
||||||
kmi = km.keymap_items.new('wm.context_toggle', type='ONE', value='PRESS')
|
kmi = km.keymap_items.new('wm.context_toggle', type='ONE', value='PRESS')
|
||||||
|
@ -22,6 +24,12 @@ def register_keymaps():
|
||||||
kmi.properties.data_path='scene.tool_settings.use_gpencil_select_mask_segment'
|
kmi.properties.data_path='scene.tool_settings.use_gpencil_select_mask_segment'
|
||||||
addon_keymaps.append((km, kmi))
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
## T temp cutter (need disabling of native T shortcut, maybe expose a button to set the shortcut as user ?)
|
||||||
|
# km = addon.keymaps.new(name = "Grease Pencil", space_type = "EMPTY")
|
||||||
|
# kmi = km.keymap_items.new('gpencil.stroke_cutter', type='LEFTMOUSE', value='PRESS', key_modifier='T')
|
||||||
|
# kmi.properties.flat_caps=False
|
||||||
|
# addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
def unregister_keymaps():
|
def unregister_keymaps():
|
||||||
for km, kmi in addon_keymaps:
|
for km, kmi in addon_keymaps:
|
||||||
km.keymap_items.remove(kmi)
|
km.keymap_items.remove(kmi)
|
||||||
|
|
65
utils.py
65
utils.py
|
@ -30,19 +30,14 @@ def set_matrix(gp_frame,mat):
|
||||||
|
|
||||||
# get view vector location (the 2 methods work fine)
|
# get view vector location (the 2 methods work fine)
|
||||||
def get_view_origin_position():
|
def get_view_origin_position():
|
||||||
#method 1
|
## method 1
|
||||||
# from bpy_extras import view3d_utils
|
# from bpy_extras import view3d_utils
|
||||||
# region = bpy.context.region
|
# region = bpy.context.region
|
||||||
# rv3d = bpy.context.region_data
|
# rv3d = bpy.context.region_data
|
||||||
# view_loc = view3d_utils.region_2d_to_origin_3d(region, rv3d, (region.width/2.0, region.height/2.0))
|
# view_loc = view3d_utils.region_2d_to_origin_3d(region, rv3d, (region.width/2.0, region.height/2.0))
|
||||||
# print("view_loc1", view_loc)#Dbg
|
## method 2
|
||||||
|
|
||||||
#method 2
|
|
||||||
r3d = bpy.context.space_data.region_3d
|
r3d = bpy.context.space_data.region_3d
|
||||||
view_loc2 = r3d.view_matrix.inverted().translation
|
view_loc2 = r3d.view_matrix.inverted().translation
|
||||||
# print("view_loc2", view_loc2)#Dbg
|
|
||||||
# if view_loc != view_loc2: print('Might be an error when finding view coordinate')
|
|
||||||
|
|
||||||
return view_loc2
|
return view_loc2
|
||||||
|
|
||||||
def location_to_region(worldcoords):
|
def location_to_region(worldcoords):
|
||||||
|
@ -76,6 +71,10 @@ def object_derived_get(ob, scene):
|
||||||
return ob_matrix_pairs
|
return ob_matrix_pairs
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
### Bmesh
|
||||||
|
# -----------------
|
||||||
|
|
||||||
def link_vert(v,ordered_vert) :
|
def link_vert(v,ordered_vert) :
|
||||||
for e in v.link_edges :
|
for e in v.link_edges :
|
||||||
other_vert = e.other_vert(v)
|
other_vert = e.other_vert(v)
|
||||||
|
@ -147,7 +146,11 @@ def gp_stroke_to_bmesh(strokes):
|
||||||
return strokes_info
|
return strokes_info
|
||||||
|
|
||||||
|
|
||||||
def simple_draw_gp_stroke(pts,frame,width = 2, mat_id = 0):
|
# -----------------
|
||||||
|
### GP Drawing
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
def simple_draw_gp_stroke(pts, frame, width = 2, mat_id = 0):
|
||||||
'''
|
'''
|
||||||
draw basic stroke by passing list of point 3D coordinate
|
draw basic stroke by passing list of point 3D coordinate
|
||||||
the frame to draw on and optional width parameter (default = 2)
|
the frame to draw on and optional width parameter (default = 2)
|
||||||
|
@ -169,11 +172,11 @@ def simple_draw_gp_stroke(pts,frame,width = 2, mat_id = 0):
|
||||||
return stroke
|
return stroke
|
||||||
|
|
||||||
## OLD - need update
|
## OLD - need update
|
||||||
def draw_gp_stroke(loop_info,frame,palette,width = 2) :
|
def draw_gp_stroke(loop_info, frame, palette, width = 2) :
|
||||||
stroke = frame.strokes.new(palette)
|
stroke = frame.strokes.new(palette)
|
||||||
|
|
||||||
stroke.line_width = width
|
stroke.line_width = width
|
||||||
stroke.display_mode = '3DSPACE'# old->draw_mode
|
stroke.display_mode = '3DSPACE'# old -> draw_mode
|
||||||
|
|
||||||
for i,info in enumerate(loop_info) :
|
for i,info in enumerate(loop_info) :
|
||||||
stroke.points.add()
|
stroke.points.add()
|
||||||
|
@ -894,13 +897,6 @@ def draw_kmi(km, kmi, layout):
|
||||||
### linking utility
|
### linking utility
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
"""
|
|
||||||
def link_objects_in_blend(filepath, obj_name, link=True):
|
|
||||||
'''Link an object by name from a file, if link is False, append instead of linking'''
|
|
||||||
with bpy.data.libraries.load(filepath, link=link) as (data_from, data_to):
|
|
||||||
data_to.objects = [o for o in data_from.objects if o == obj_name] # c.startswith(obj_name)
|
|
||||||
return data_to.objects
|
|
||||||
"""
|
|
||||||
def link_objects_in_blend(filepath, obj_name_list, link=True):
|
def link_objects_in_blend(filepath, obj_name_list, link=True):
|
||||||
'''Link an object by name from a file, if link is False, append instead of linking'''
|
'''Link an object by name from a file, if link is False, append instead of linking'''
|
||||||
if isinstance(obj_name_list, str):
|
if isinstance(obj_name_list, str):
|
||||||
|
@ -922,4 +918,37 @@ def check_objects_in_blend(filepath, avoid_camera=True):
|
||||||
l = [o for o in data_from.objects if not any(x in o.lower() for x in ('camera', 'draw_cam', 'obj_cam'))]
|
l = [o for o in data_from.objects if not any(x in o.lower() for x in ('camera', 'draw_cam', 'obj_cam'))]
|
||||||
else:
|
else:
|
||||||
l = [o for o in data_from.objects]
|
l = [o for o in data_from.objects]
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
### props handling
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
def iterate_selector(zone, attr, state, info_attr = None, active_access='active'):
|
||||||
|
'''Iterate with given attribute'''
|
||||||
|
item_number = len(zone)
|
||||||
|
if item_number <= 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
if getattr(zone, attr) == None:
|
||||||
|
print('no', attr, 'in', zone)
|
||||||
|
return
|
||||||
|
|
||||||
|
if state: # swap
|
||||||
|
info = None
|
||||||
|
bottom = None
|
||||||
|
new_index = getattr(zone, attr) + state
|
||||||
|
setattr(zone, attr, new_index % item_number)
|
||||||
|
|
||||||
|
if new_index == item_number:
|
||||||
|
bottom = 1 # bottom reached, cycle to first
|
||||||
|
elif new_index < 0:
|
||||||
|
bottom = -1 # up reached, cycle to last
|
||||||
|
|
||||||
|
if info_attr:
|
||||||
|
active_item = getattr(zone, active_access) # active by default
|
||||||
|
if active_item:
|
||||||
|
info = getattr(active_item, info_attr)
|
||||||
|
|
||||||
|
return info, bottom
|
||||||
|
|
Loading…
Reference in New Issue