WIP: Brush assets project #106303
|
@ -107,10 +107,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
|||
kmi_hack_properties = kmi_hack.properties
|
||||
kmi_hack.active = False
|
||||
|
||||
kmi_hack_brush_select = keymap.keymap_items.new("paint.brush_select", 'NONE', 'PRESS')
|
||||
kmi_hack_brush_select_properties = kmi_hack_brush_select.properties
|
||||
kmi_hack_brush_select.active = False
|
||||
|
||||
if use_release_confirm or use_tap_reset:
|
||||
kmi_toolbar = wm.keyconfigs.find_item_from_operator(
|
||||
idname="wm.toolbar",
|
||||
|
@ -169,47 +165,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
|||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
|
||||
if kmi_found is None:
|
||||
if item.data_block:
|
||||
# PAINT_OT_brush_select
|
||||
mode = context.active_object.mode
|
||||
# See: BKE_paint_get_tool_prop_id_from_paintmode
|
||||
if space_type == 'IMAGE_EDITOR':
|
||||
if context.space_data.mode == 'PAINT':
|
||||
attr = "image_tool"
|
||||
else:
|
||||
attr = None
|
||||
elif space_type == 'VIEW_3D':
|
||||
attr = {
|
||||
'SCULPT': "sculpt_tool",
|
||||
'VERTEX_PAINT': "vertex_tool",
|
||||
'WEIGHT_PAINT': "weight_tool",
|
||||
'TEXTURE_PAINT': "image_tool",
|
||||
'PAINT_GPENCIL': "gpencil_tool",
|
||||
'PAINT_GREASE_PENCIL': "gpencil_tool",
|
||||
'VERTEX_GPENCIL': "gpencil_vertex_tool",
|
||||
'SCULPT_GPENCIL': "gpencil_sculpt_tool",
|
||||
'WEIGHT_GPENCIL': "gpencil_weight_tool",
|
||||
'SCULPT_CURVES': "curves_sculpt_tool",
|
||||
}.get(mode, None)
|
||||
else:
|
||||
attr = None
|
||||
|
||||
if attr is not None:
|
||||
setattr(kmi_hack_brush_select_properties, attr, item.data_block)
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname="paint.brush_select",
|
||||
context='INVOKE_REGION_WIN',
|
||||
properties=kmi_hack_brush_select_properties,
|
||||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
elif mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL'}:
|
||||
# Doesn't use brushes
|
||||
pass
|
||||
else:
|
||||
print("Unsupported mode:", mode)
|
||||
del mode, attr
|
||||
|
||||
else:
|
||||
kmi_found = None
|
||||
|
||||
|
@ -402,7 +357,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
|||
|
||||
if use_hack_properties:
|
||||
keymap.keymap_items.remove(kmi_hack)
|
||||
keymap.keymap_items.remove(kmi_hack_brush_select)
|
||||
|
||||
# Keep last so we can try add a key without any modifiers
|
||||
# in the case this toolbar was activated with modifiers.
|
||||
|
|
|
@ -5594,27 +5594,37 @@ def km_sculpt(params):
|
|||
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
|
||||
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
|
||||
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
|
||||
# Tools
|
||||
("paint.brush_select", {"type": 'V', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'DRAW')]}),
|
||||
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'SMOOTH')]}),
|
||||
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'PINCH')]}),
|
||||
("paint.brush_select", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'INFLATE')]}),
|
||||
("paint.brush_select", {"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'GRAB')]}),
|
||||
("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("sculpt_tool", 'SCRAPE')]}),
|
||||
("paint.brush_select", {"type": 'C', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'CLAY_STRIPS')]}),
|
||||
("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("sculpt_tool", 'CREASE')]}),
|
||||
("paint.brush_select", {"type": 'K', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
|
||||
("paint.brush_select", {"type": 'M', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
|
||||
# Brushes
|
||||
("brush.asset_select", {"type": 'V', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Draw")]}),
|
||||
("brush.asset_select", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Smooth")]}),
|
||||
("brush.asset_select", {"type": 'P', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Pinch/Magnify")]}),
|
||||
("brush.asset_select", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Inflate/Deflate")]}),
|
||||
("brush.asset_select", {"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Grab")]}),
|
||||
("brush.asset_select", {"type": 'T', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Scrape/Fill")]}),
|
||||
("brush.asset_select", {"type": 'C', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Clay Strips")]}),
|
||||
("brush.asset_select", {"type": 'C', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Crease")]}),
|
||||
("brush.asset_select", {"type": 'K', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Snake Hook")]}),
|
||||
("brush.asset_select", {"type": 'M', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Mask")]}),
|
||||
])
|
||||
|
||||
# Lasso Masking.
|
||||
|
|
|
@ -151,19 +151,27 @@ class BrushSelectPanel(BrushPanel):
|
|||
brush = settings.brush
|
||||
|
||||
row = layout.row()
|
||||
large_preview = True
|
||||
if large_preview:
|
||||
row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False)
|
||||
else:
|
||||
row.column().template_ID(settings, "brush", new="brush.add")
|
||||
# TODO: hide buttons since they are confusing with menu entries.
|
||||
# But some of this functionality may still be needed.
|
||||
row.column().template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
if brush is None:
|
||||
return
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
if not brush.is_asset_library_data and not brush.asset_data:
|
||||
# Legacy custom icon, mostly replaced by asset preview.
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
if brush.use_custom_icon:
|
||||
layout.prop(brush, "icon_filepath", text="")
|
||||
col = layout.column(heading="Custom Icon", align=True)
|
||||
row = col.row()
|
||||
row.prop(brush, "use_custom_icon", text="")
|
||||
sub = row.row()
|
||||
sub.active = brush.use_custom_icon
|
||||
sub.prop(brush, "icon_filepath", text="")
|
||||
|
||||
|
||||
class ColorPalettePanel(BrushPanel):
|
||||
|
@ -985,6 +993,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
use_frontface = False
|
||||
|
||||
if mode == 'SCULPT':
|
||||
layout.prop(brush, "sculpt_tool")
|
||||
layout.separator()
|
||||
|
||||
capabilities = brush.sculpt_capabilities
|
||||
use_accumulate = capabilities.has_accumulate
|
||||
use_frontface = True
|
||||
|
@ -1075,6 +1086,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# 3D and 2D Texture Paint.
|
||||
elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}:
|
||||
layout.prop(brush, "image_tool")
|
||||
layout.separator()
|
||||
|
||||
capabilities = brush.image_paint_capabilities
|
||||
use_accumulate = capabilities.has_accumulate
|
||||
|
||||
|
@ -1102,6 +1116,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# Vertex Paint #
|
||||
elif mode == 'PAINT_VERTEX':
|
||||
layout.prop(brush, "vertex_tool")
|
||||
layout.separator()
|
||||
|
||||
layout.prop(brush, "use_alpha")
|
||||
if brush.vertex_tool != 'SMEAR':
|
||||
use_accumulate = True
|
||||
|
@ -1109,10 +1126,17 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# Weight Paint
|
||||
elif mode == 'PAINT_WEIGHT':
|
||||
layout.prop(brush, "weight_tool")
|
||||
layout.separator()
|
||||
|
||||
if brush.weight_tool != 'SMEAR':
|
||||
use_accumulate = True
|
||||
use_frontface = True
|
||||
|
||||
# Sculpt Curves
|
||||
elif mode == 'SCULPT_CURVES':
|
||||
layout.prop(brush, "curves_sculpt_tool")
|
||||
|
||||
# Draw shared settings.
|
||||
if use_accumulate:
|
||||
layout.prop(brush, "use_accumulate")
|
||||
|
@ -1120,6 +1144,21 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
if use_frontface:
|
||||
layout.prop(brush, "use_frontface", text="Front Faces Only")
|
||||
|
||||
# Brush modes
|
||||
header, panel = layout.panel("modes", default_closed=True)
|
||||
header.label(text="Modes")
|
||||
if panel:
|
||||
panel.use_property_split = True
|
||||
panel.use_property_decorate = False
|
||||
|
||||
col = panel.column(align=True)
|
||||
col.prop(brush, "use_paint_sculpt", text="Sculpt")
|
||||
col.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt")
|
||||
col.prop(brush, "use_paint_vertex", text="Vertex Paint")
|
||||
col.prop(brush, "use_paint_weight", text="Weight Paint")
|
||||
col.prop(brush, "use_paint_image", text="Texture Paint")
|
||||
col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves")
|
||||
|
||||
|
||||
def draw_color_settings(context, layout, brush, color_type=False):
|
||||
"""Draw color wheel and gradient settings."""
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from bpy.types import (
|
||||
AssetShelf,
|
||||
Header,
|
||||
Menu,
|
||||
Panel,
|
||||
|
@ -30,6 +31,9 @@ from bl_ui.properties_grease_pencil_common import (
|
|||
from bl_ui.space_toolsystem_common import (
|
||||
ToolActivePanelHelper,
|
||||
)
|
||||
from bl_ui.space_view3d import (
|
||||
BrushAssetShelf,
|
||||
)
|
||||
|
||||
from bpy.app.translations import (
|
||||
contexts as i18n_contexts,
|
||||
|
@ -1688,6 +1692,15 @@ class IMAGE_PT_annotation(AnnotationDataPanel, Panel):
|
|||
# Grease Pencil drawing tools.
|
||||
|
||||
|
||||
class ImageAssetShelf(BrushAssetShelf):
|
||||
bl_space_type = "IMAGE_EDITOR"
|
||||
|
||||
|
||||
class IMAGE_AST_brush_sculpt(ImageAssetShelf, AssetShelf):
|
||||
mode = 'TEXTURE_PAINT'
|
||||
mode_prop = "use_paint_image"
|
||||
|
||||
|
||||
classes = (
|
||||
IMAGE_MT_view,
|
||||
IMAGE_MT_view_zoom,
|
||||
|
@ -1757,6 +1770,7 @@ classes = (
|
|||
IMAGE_PT_overlay_uv_edit_geometry,
|
||||
IMAGE_PT_overlay_texture_paint,
|
||||
IMAGE_PT_overlay_image,
|
||||
IMAGE_AST_brush_sculpt,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1396,16 +1396,23 @@ class _defs_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.sculpt.",
|
||||
type=bpy.types.Brush,
|
||||
attr="sculpt_tool",
|
||||
# TODO(@ideasman42): we may want to enable this,
|
||||
# it causes awkward grouping with 2x column button layout.
|
||||
use_separators=False,
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.sculpt.brush
|
||||
if brush:
|
||||
tool = brush.sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def hide_border():
|
||||
|
@ -1694,13 +1701,23 @@ class _defs_vertex_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_vertex.",
|
||||
type=bpy.types.Brush,
|
||||
attr="vertex_tool",
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.vertex_paint.brush
|
||||
if brush:
|
||||
tool = brush.vertex_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_texture_paint:
|
||||
|
@ -1715,14 +1732,24 @@ class _defs_texture_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_texture.",
|
||||
type=bpy.types.Brush,
|
||||
attr="image_tool",
|
||||
cursor='PAINT_CROSS',
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.image_paint.brush
|
||||
if brush:
|
||||
tool = brush.image_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
cursor='PAINT_CROSS',
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_weight_paint:
|
||||
|
@ -1742,13 +1769,23 @@ class _defs_weight_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_weight.",
|
||||
type=bpy.types.Brush,
|
||||
attr="weight_tool",
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.weight_paint.brush
|
||||
if brush:
|
||||
tool = brush.weight_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def sample_weight():
|
||||
|
@ -1821,36 +1858,25 @@ class _defs_weight_paint:
|
|||
|
||||
class _defs_paint_grease_pencil:
|
||||
|
||||
# FIXME: Replace brush tools with code below once they are all implemented:
|
||||
#
|
||||
# @staticmethod
|
||||
# def generate_from_brushes(context):
|
||||
# return generate_from_enum_ex(
|
||||
# context,
|
||||
# idname_prefix="builtin_brush.",
|
||||
# icon_prefix="brush.gpencil_draw.",
|
||||
# type=bpy.types.Brush,
|
||||
# attr="gpencil_tool",
|
||||
# cursor='DOT',
|
||||
# )
|
||||
|
||||
@ToolDef.from_fn
|
||||
def draw():
|
||||
return dict(
|
||||
idname="builtin_brush.Draw",
|
||||
label="Draw",
|
||||
icon="brush.gpencil_draw.draw",
|
||||
data_block='DRAW',
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def erase():
|
||||
return dict(
|
||||
idname="builtin_brush.Erase",
|
||||
label="Erase",
|
||||
icon="brush.gpencil_draw.erase",
|
||||
data_block='ERASE',
|
||||
)
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def cutter():
|
||||
|
@ -2288,17 +2314,23 @@ class _defs_gpencil_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.gpencil_draw.",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_tool",
|
||||
cursor='DOT',
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.draw",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def cutter():
|
||||
|
@ -2654,16 +2686,24 @@ class _defs_gpencil_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_sculpt_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.sculpt_paint",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_sculpt_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
||||
class _defs_grease_pencil_sculpt:
|
||||
|
@ -2684,33 +2724,46 @@ class _defs_grease_pencil_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
# Uses GPv2 tool settings
|
||||
attr="gpencil_sculpt_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="grease_pencil.sculpt_paint",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_sculpt_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_gpencil_weight:
|
||||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_weight_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.weight_paint",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_weight_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_weight_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_grease_pencil_weight:
|
||||
|
@ -2734,17 +2787,23 @@ class _defs_curves_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.curves.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="curves_sculpt_tool",
|
||||
icon_map={
|
||||
# Use the generic icon for selection painting.
|
||||
"ops.curves.sculpt_selection_paint": "ops.generic.select_paint",
|
||||
},
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.curves_sculpt.brush
|
||||
if brush:
|
||||
tool = brush.curves_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_gpencil_vertex:
|
||||
|
@ -2766,18 +2825,23 @@ class _defs_gpencil_vertex:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_vertex.",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_vertex_tool",
|
||||
cursor='DOT',
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.vertex_paint",
|
||||
),
|
||||
)
|
||||
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_vertex_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_vertex_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
class _defs_node_select:
|
||||
|
||||
|
@ -3466,8 +3530,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
'PAINT_GREASE_PENCIL': [
|
||||
_defs_view3d_generic.cursor,
|
||||
None,
|
||||
_defs_paint_grease_pencil.draw,
|
||||
_defs_paint_grease_pencil.erase,
|
||||
_defs_paint_grease_pencil.generate_from_brushes,
|
||||
_defs_paint_grease_pencil.cutter,
|
||||
_defs_paint_grease_pencil.tint,
|
||||
None,
|
||||
|
|
|
@ -235,9 +235,18 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
paint = context.tool_settings.sculpt
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
|
||||
brush_name = brush.name if brush else None
|
||||
preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0
|
||||
fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE'
|
||||
layout.template_asset_shelf_popover(
|
||||
BrushAssetShelf.get_shelf_name_from_mode(context.object.mode),
|
||||
name=brush_name,
|
||||
icon=fallback_icon,
|
||||
icon_value=preview_icon_id,
|
||||
)
|
||||
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
|
@ -3481,23 +3490,6 @@ class VIEW3D_MT_make_links(Menu):
|
|||
layout.operator("object.datalayout_transfer")
|
||||
|
||||
|
||||
class VIEW3D_MT_brush_paint_modes(Menu):
|
||||
bl_label = "Enabled Modes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
settings = UnifiedPaintPanel.paint_settings(context)
|
||||
brush = settings.brush
|
||||
|
||||
layout.prop(brush, "use_paint_sculpt", text="Sculpt")
|
||||
layout.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt")
|
||||
layout.prop(brush, "use_paint_vertex", text="Vertex Paint")
|
||||
layout.prop(brush, "use_paint_weight", text="Weight Paint")
|
||||
layout.prop(brush, "use_paint_image", text="Texture Paint")
|
||||
layout.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves")
|
||||
|
||||
|
||||
class VIEW3D_MT_paint_vertex(Menu):
|
||||
bl_label = "Paint"
|
||||
|
||||
|
@ -9012,24 +9004,104 @@ class VIEW3D_PT_viewport_debug(Panel):
|
|||
layout.prop(overlay, "use_debug_freeze_view_culling")
|
||||
|
||||
|
||||
class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf):
|
||||
# Experimental: Asset shelf for sculpt brushes, only shows up if both the
|
||||
# "Asset Shelf" and the "Extended Asset Browser" experimental features are
|
||||
# enabled.
|
||||
|
||||
bl_space_type = 'VIEW_3D'
|
||||
class BrushAssetShelf:
|
||||
bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'}
|
||||
bl_activate_operator = "BRUSH_OT_asset_select"
|
||||
bl_default_preview_size = 48
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
prefs = context.preferences
|
||||
if not prefs.experimental.use_extended_asset_browser:
|
||||
return False
|
||||
|
||||
return context.mode == 'SCULPT'
|
||||
return context.object and context.object.mode == cls.mode
|
||||
|
||||
@classmethod
|
||||
def asset_poll(cls, asset):
|
||||
return asset.id_type == 'BRUSH'
|
||||
if asset.id_type != 'BRUSH':
|
||||
return False
|
||||
|
||||
return asset.metadata.get(cls.mode_prop, False)
|
||||
|
||||
@classmethod
|
||||
def get_active_asset(cls):
|
||||
paint_settings = UnifiedPaintPanel.paint_settings(bpy.context)
|
||||
return paint_settings.brush_asset_reference if paint_settings else None
|
||||
|
||||
@classmethod
|
||||
def draw_context_menu(self, context, asset, layout):
|
||||
# Currently this menu adds operators that deal with the affected brush and don't take the
|
||||
# asset into account. Luckily that is okay for now, since right clicking in the grid view
|
||||
# also activates the item.
|
||||
layout.menu_contents("VIEW3D_MT_brush_context_menu")
|
||||
|
||||
# Not nice, but needed unfortunately.
|
||||
@staticmethod
|
||||
def get_shelf_name_from_mode(obmode):
|
||||
mode_map = {
|
||||
'SCULPT': "VIEW3D_AST_brush_sculpt",
|
||||
'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves",
|
||||
'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint",
|
||||
'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint",
|
||||
'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint",
|
||||
'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint",
|
||||
'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint",
|
||||
'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
|
||||
'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex",
|
||||
'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight",
|
||||
}
|
||||
return mode_map[obmode]
|
||||
|
||||
|
||||
class View3DAssetShelf(BrushAssetShelf):
|
||||
bl_space_type = "VIEW_3D"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT'
|
||||
mode_prop = "use_paint_sculpt"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_sculpt_curves(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT_CURVES'
|
||||
mode_prop = "use_paint_sculpt_curves"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_vertex_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'VERTEX_PAINT'
|
||||
mode_prop = "use_paint_vertex"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_weight_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'WEIGHT_PAINT'
|
||||
mode_prop = "use_paint_weight"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_texture_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'TEXTURE_PAINT'
|
||||
mode_prop = "use_paint_image"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'PAINT_GPENCIL'
|
||||
mode_prop = "use_paint_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_grease_pencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'PAINT_GREASE_PENCIL'
|
||||
mode_prop = "use_paint_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT_GPENCIL'
|
||||
mode_prop = "use_sculpt_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_vertex(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'VERTEX_GPENCIL'
|
||||
mode_prop = "use_vertex_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_weight(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'WEIGHT_GPENCIL'
|
||||
mode_prop = "use_weight_grease_pencil"
|
||||
|
||||
|
||||
classes = (
|
||||
|
@ -9110,7 +9182,6 @@ classes = (
|
|||
VIEW3D_MT_object_cleanup,
|
||||
VIEW3D_MT_make_single_user,
|
||||
VIEW3D_MT_make_links,
|
||||
VIEW3D_MT_brush_paint_modes,
|
||||
VIEW3D_MT_paint_vertex,
|
||||
VIEW3D_MT_hook,
|
||||
VIEW3D_MT_vertex_group,
|
||||
|
@ -9295,7 +9366,16 @@ classes = (
|
|||
VIEW3D_PT_curves_sculpt_parameter_falloff,
|
||||
VIEW3D_PT_curves_sculpt_grow_shrink_scaling,
|
||||
VIEW3D_PT_viewport_debug,
|
||||
VIEW3D_AST_sculpt_brushes,
|
||||
VIEW3D_AST_brush_sculpt,
|
||||
VIEW3D_AST_brush_sculpt_curves,
|
||||
VIEW3D_AST_brush_vertex_paint,
|
||||
VIEW3D_AST_brush_weight_paint,
|
||||
VIEW3D_AST_brush_texture_paint,
|
||||
VIEW3D_AST_brush_gpencil_paint,
|
||||
VIEW3D_AST_brush_grease_pencil_paint,
|
||||
VIEW3D_AST_brush_gpencil_sculpt,
|
||||
VIEW3D_AST_brush_gpencil_vertex,
|
||||
VIEW3D_AST_brush_gpencil_weight,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -43,25 +43,21 @@ class VIEW3D_MT_brush_context_menu(Menu):
|
|||
|
||||
# skip if no active brush
|
||||
if not brush:
|
||||
layout.label(text="No Brushes currently available", icon='INFO')
|
||||
layout.label(text="No brush selected", icon='INFO')
|
||||
return
|
||||
|
||||
# brush paint modes
|
||||
layout.menu("VIEW3D_MT_brush_paint_modes")
|
||||
if brush.is_asset_library_data:
|
||||
layout.operator("brush.asset_save_as", text="Duplicate Asset...", icon='DUPLICATE')
|
||||
layout.operator("brush.asset_delete", text="Delete Asset")
|
||||
|
||||
# brush tool
|
||||
layout.separator()
|
||||
|
||||
if context.image_paint_object:
|
||||
layout.prop_menu_enum(brush, "image_tool")
|
||||
elif context.vertex_paint_object:
|
||||
layout.prop_menu_enum(brush, "vertex_tool")
|
||||
elif context.weight_paint_object:
|
||||
layout.prop_menu_enum(brush, "weight_tool")
|
||||
elif context.sculpt_object:
|
||||
layout.prop_menu_enum(brush, "sculpt_tool")
|
||||
layout.operator("brush.reset")
|
||||
elif context.tool_settings.curves_sculpt:
|
||||
layout.prop_menu_enum(brush, "curves_sculpt_tool")
|
||||
layout.operator("brush.asset_edit_metadata", text="Edit Metadata")
|
||||
layout.operator("brush.asset_update", text="Update Asset")
|
||||
layout.operator("brush.asset_revert", text="Revert to Asset")
|
||||
else:
|
||||
layout.operator("brush.asset_save_as", text="Save As Asset...", icon='FILE_TICK')
|
||||
layout.operator("brush.asset_delete", text="Delete")
|
||||
|
||||
|
||||
class VIEW3D_MT_brush_gpencil_context_menu(Menu):
|
||||
|
|
|
@ -114,6 +114,9 @@ class AssetLibrary {
|
|||
*/
|
||||
static void foreach_loaded(FunctionRef<void(AssetLibrary &)> fn, bool include_all_library);
|
||||
|
||||
static std::string resolve_asset_weak_reference_to_full_path(
|
||||
const AssetWeakReference &asset_reference);
|
||||
|
||||
void load_catalogs();
|
||||
|
||||
AssetCatalogService &catalog_service() const;
|
||||
|
@ -172,8 +175,6 @@ class AssetLibrary {
|
|||
*/
|
||||
AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path);
|
||||
|
||||
std::string resolve_asset_weak_reference_to_full_path(const AssetWeakReference &asset_reference);
|
||||
|
||||
eAssetLibraryType library_type() const;
|
||||
StringRefNull name() const;
|
||||
StringRefNull root_path() const;
|
||||
|
|
|
@ -70,6 +70,74 @@ TEST_F(AssetRepresentationTest, weak_reference__custom_library)
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(AssetRepresentationTest, weak_reference__compare)
|
||||
{
|
||||
{
|
||||
AssetWeakReference a;
|
||||
AssetWeakReference b;
|
||||
EXPECT_EQ(a, b);
|
||||
|
||||
/* Arbitrary individual member changes to test how it affects the comparison. */
|
||||
b.asset_library_identifier = "My lib";
|
||||
EXPECT_NE(a, b);
|
||||
a.asset_library_identifier = "My lib";
|
||||
EXPECT_EQ(a, b);
|
||||
a.asset_library_type = ASSET_LIBRARY_ESSENTIALS;
|
||||
EXPECT_NE(a, b);
|
||||
b.asset_library_type = ASSET_LIBRARY_LOCAL;
|
||||
EXPECT_NE(a, b);
|
||||
b.asset_library_type = ASSET_LIBRARY_ESSENTIALS;
|
||||
EXPECT_EQ(a, b);
|
||||
a.relative_asset_identifier = "Foo";
|
||||
EXPECT_NE(a, b);
|
||||
b.relative_asset_identifier = "Bar";
|
||||
EXPECT_NE(a, b);
|
||||
a.relative_asset_identifier = "Bar";
|
||||
EXPECT_EQ(a, b);
|
||||
|
||||
/* Make the destructor work. */
|
||||
a.asset_library_identifier = b.asset_library_identifier = nullptr;
|
||||
a.relative_asset_identifier = b.relative_asset_identifier = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
AssetWeakReference a;
|
||||
a.asset_library_type = ASSET_LIBRARY_LOCAL;
|
||||
a.asset_library_identifier = "My custom lib";
|
||||
a.relative_asset_identifier = "path/to/an/asset";
|
||||
|
||||
AssetWeakReference b;
|
||||
EXPECT_NE(a, b);
|
||||
|
||||
b.asset_library_type = ASSET_LIBRARY_LOCAL;
|
||||
b.asset_library_identifier = "My custom lib";
|
||||
b.relative_asset_identifier = "path/to/an/asset";
|
||||
EXPECT_EQ(a, b);
|
||||
|
||||
/* Make the destructor work. */
|
||||
a.asset_library_identifier = b.asset_library_identifier = nullptr;
|
||||
a.relative_asset_identifier = b.relative_asset_identifier = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
AssetLibraryService *service = AssetLibraryService::get();
|
||||
AssetLibrary *const library = service->get_asset_library_on_disk_custom("My custom lib",
|
||||
asset_library_root_);
|
||||
AssetRepresentation &asset = add_dummy_asset(*library, "path/to/an/asset");
|
||||
|
||||
AssetWeakReference weak_ref = asset.make_weak_reference();
|
||||
AssetWeakReference other;
|
||||
other.asset_library_type = ASSET_LIBRARY_CUSTOM;
|
||||
other.asset_library_identifier = "My custom lib";
|
||||
other.relative_asset_identifier = "path/to/an/asset";
|
||||
EXPECT_EQ(weak_ref, other);
|
||||
|
||||
/* Make the destructor work. */
|
||||
other.asset_library_identifier = nullptr;
|
||||
other.relative_asset_identifier = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AssetRepresentationTest, weak_reference__resolve_to_full_path__current_file)
|
||||
{
|
||||
AssetLibraryService *service = AssetLibraryService::get();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_asset_types.h"
|
||||
|
||||
struct AssetLibraryReference;
|
||||
|
@ -20,6 +21,7 @@ struct BlendDataReader;
|
|||
struct BlendWriter;
|
||||
struct ID;
|
||||
struct IDProperty;
|
||||
struct Main;
|
||||
struct PreviewImage;
|
||||
|
||||
using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data);
|
||||
|
@ -78,3 +80,13 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data)
|
|||
|
||||
void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref);
|
||||
void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref);
|
||||
|
||||
void BKE_asset_catalog_path_list_free(ListBase &catalog_path_list);
|
||||
ListBase BKE_asset_catalog_path_list_duplicate(const ListBase &catalog_path_list);
|
||||
void BKE_asset_catalog_path_list_blend_write(BlendWriter *writer,
|
||||
const ListBase &catalog_path_list);
|
||||
void BKE_asset_catalog_path_list_blend_read_data(BlendDataReader *reader,
|
||||
ListBase &catalog_path_list);
|
||||
bool BKE_asset_catalog_path_list_has_path(const ListBase &catalog_path_list,
|
||||
const char *catalog_path);
|
||||
void BKE_asset_catalog_path_list_add_path(ListBase &catalog_path_list, const char *catalog_path);
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Editing of datablocks from asset libraries, separate from the current open
|
||||
* blend file.
|
||||
*
|
||||
* Each asset blend file is loaded into a separate main database, including the
|
||||
* asset datablocks and their dependencies. These datablocks are all tagged with
|
||||
* LIB_TAG_ASSET_MAIN. These can not be linked with other datablocks in the
|
||||
* current blend file.
|
||||
*
|
||||
* For editable assets in user asset libraries, each asset is stored in its own
|
||||
* blend file. This way the blend file can be easily saved, reloaded and deleted.
|
||||
*
|
||||
* This mechanism is currently only used for brush assets.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "AS_asset_catalog.hh"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
|
||||
struct bUserAssetLibrary;
|
||||
struct AssetMetaData;
|
||||
struct AssetWeakReference;
|
||||
struct ID;
|
||||
struct Main;
|
||||
struct ReportList;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/** Get datablock from weak reference, loading the blend file as needed. */
|
||||
ID *asset_edit_id_from_weak_reference(Main &global_main,
|
||||
ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref);
|
||||
|
||||
/** Get main database that a given asset datablock corresponds to. */
|
||||
Main *asset_edit_main(const ID &id);
|
||||
|
||||
/** Asset editing operations. */
|
||||
|
||||
bool asset_edit_id_is_editable(const ID &id);
|
||||
|
||||
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
|
||||
const ID &id,
|
||||
StringRef name,
|
||||
const bUserAssetLibrary &user_library,
|
||||
ReportList &reports);
|
||||
|
||||
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports);
|
||||
bool asset_edit_id_revert(Main &global_main, const ID &id, ReportList &reports);
|
||||
bool asset_edit_id_delete(Main &global_main, const ID &id, ReportList &reports);
|
||||
|
||||
/** Clean up on exit. */
|
||||
void asset_edit_main_free_all();
|
||||
|
||||
} // namespace blender::bke
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 22
|
||||
#define BLENDER_FILE_SUBVERSION 24
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -19,6 +19,11 @@ struct ReportList;
|
|||
struct UserDef;
|
||||
struct WorkspaceConfigFileData;
|
||||
|
||||
/**
|
||||
* The suffix used for blendfiles managed by the asset system.
|
||||
*/
|
||||
#define BLENDER_ASSET_FILE_SUFFIX ".asset.blend"
|
||||
|
||||
/**
|
||||
* Check whether given path ends with a blend file compatible extension
|
||||
* (`.blend`, `.ble` or `.blend.gz`).
|
||||
|
|
|
@ -185,16 +185,5 @@ void BKE_brush_scale_size(int *r_brush_size,
|
|||
*/
|
||||
bool BKE_brush_has_cube_tip(const Brush *brush, PaintMode paint_mode);
|
||||
|
||||
/* Accessors */
|
||||
#define BKE_brush_tool_get(brush, p) \
|
||||
(CHECK_TYPE_ANY(brush, Brush *, const Brush *), \
|
||||
*(const char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset))
|
||||
#define BKE_brush_tool_set(brush, p, tool) \
|
||||
{ \
|
||||
CHECK_TYPE_ANY(brush, Brush *); \
|
||||
*(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset) = tool; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* debugging only */
|
||||
void BKE_brush_debug_print_state(Brush *br);
|
||||
|
|
|
@ -699,12 +699,6 @@ void BKE_library_make_local(
|
|||
void BKE_id_tag_set_atomic(ID *id, int tag);
|
||||
void BKE_id_tag_clear_atomic(ID *id, int tag);
|
||||
|
||||
/**
|
||||
* Check that given ID pointer actually is in G_MAIN.
|
||||
* Main intended use is for debug asserts in places we cannot easily get rid of #G_Main.
|
||||
*/
|
||||
bool BKE_id_is_in_global_main(ID *id);
|
||||
|
||||
bool BKE_id_can_be_asset(const ID *id);
|
||||
|
||||
/**
|
||||
|
|
|
@ -143,6 +143,14 @@ struct Main {
|
|||
* could try to use more refined detection on load. */
|
||||
bool has_forward_compatibility_issues;
|
||||
|
||||
/**
|
||||
* The currently opened .blend file was created as an asset library storage.
|
||||
*
|
||||
* This is used to warn the user when they try to save it from Blender UI, since this will likely
|
||||
* break the automatic management from the asset library system.
|
||||
*/
|
||||
bool is_asset_repository;
|
||||
|
||||
/** Commit timestamp from `buildinfo`. */
|
||||
uint64_t build_commit_timestamp;
|
||||
/** Commit Hash from `buildinfo`. */
|
||||
|
@ -183,6 +191,11 @@ struct Main {
|
|||
*/
|
||||
bool is_global_main;
|
||||
|
||||
/**
|
||||
* True if main used to store weakly referenced assets.
|
||||
*/
|
||||
bool is_asset_weak_reference_main;
|
||||
|
||||
BlendThumbnail *blen_thumb;
|
||||
|
||||
Library *curlib;
|
||||
|
@ -301,6 +314,17 @@ void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &report
|
|||
*/
|
||||
bool BKE_main_is_empty(Main *bmain);
|
||||
|
||||
/**
|
||||
* Check whether the bmain has issues, e.g. for reporting in the status bar.
|
||||
*/
|
||||
bool BKE_main_has_issues(const Main *bmain);
|
||||
|
||||
/**
|
||||
* Check whether user confirmation should be required when overwriting this `bmain` into its source
|
||||
* blendfile.
|
||||
*/
|
||||
bool BKE_main_needs_overwrite_confirm(const Main *bmain);
|
||||
|
||||
void BKE_main_lock(Main *bmain);
|
||||
void BKE_main_unlock(Main *bmain);
|
||||
|
||||
|
@ -489,6 +513,17 @@ ListBase *which_libbase(Main *bmain, short type);
|
|||
*/
|
||||
int set_listbasepointers(Main *main, ListBase *lb[]);
|
||||
|
||||
/**
|
||||
* Return main database this ID is a member of.
|
||||
*
|
||||
* Use this in operator and draw code instead of assuming the main
|
||||
* in the context owns datablocks. Some datablock can be part of
|
||||
* main datablocks from asset libraries instead.
|
||||
*
|
||||
* Optionally can verify membership of global_main, but this is expensive.
|
||||
*/
|
||||
Main *BKE_main_from_id(Main *global_main, const ID *id, bool verify = false);
|
||||
|
||||
#define MAIN_VERSION_FILE_ATLEAST(main, ver, subver) \
|
||||
((main)->versionfile > (ver) || \
|
||||
((main)->versionfile == (ver) && (main)->subversionfile >= (subver)))
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_bit_vector.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
@ -22,6 +24,7 @@
|
|||
|
||||
#include "BKE_pbvh.hh"
|
||||
|
||||
struct AssetWeakReference;
|
||||
struct BMFace;
|
||||
struct BMLog;
|
||||
struct BMesh;
|
||||
|
@ -172,7 +175,7 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name);
|
|||
/**
|
||||
* Call when entering each respective paint mode.
|
||||
*/
|
||||
bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint);
|
||||
bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint);
|
||||
void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3]);
|
||||
void BKE_paint_free(Paint *p);
|
||||
/**
|
||||
|
@ -182,24 +185,41 @@ void BKE_paint_free(Paint *p);
|
|||
*/
|
||||
void BKE_paint_copy(const Paint *src, Paint *tar, int flag);
|
||||
|
||||
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint);
|
||||
|
||||
void BKE_paint_cavity_curve_preset(Paint *p, int preset);
|
||||
|
||||
eObjectMode BKE_paint_object_mode_from_paintmode(PaintMode mode);
|
||||
bool BKE_paint_ensure_from_paintmode(Scene *sce, PaintMode mode);
|
||||
bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, PaintMode mode);
|
||||
Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode);
|
||||
const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(PaintMode mode);
|
||||
const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(PaintMode mode);
|
||||
const char *BKE_paint_get_tool_prop_id_from_paintmode(PaintMode mode);
|
||||
uint BKE_paint_get_brush_tool_offset_from_paintmode(PaintMode mode);
|
||||
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer);
|
||||
Paint *BKE_paint_get_active_from_context(const bContext *C);
|
||||
PaintMode BKE_paintmode_get_active_from_context(const bContext *C);
|
||||
PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref);
|
||||
|
||||
/* Paint brush retrieval and assignment. */
|
||||
|
||||
Brush *BKE_paint_brush(Paint *paint);
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *p);
|
||||
void BKE_paint_brush_set(Paint *paint, Brush *br);
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *paint);
|
||||
|
||||
bool BKE_paint_brush_set(Paint *paint, Brush *brush);
|
||||
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint);
|
||||
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name);
|
||||
|
||||
void BKE_paint_brush_set_default_references(ToolSettings *ts);
|
||||
|
||||
void BKE_paint_brush_validate(Main *bmain, Paint *paint);
|
||||
|
||||
/**
|
||||
* Set the active brush of given paint struct, and store the weak asset reference to it.
|
||||
* \note Takes ownership of the given `weak_asset_reference`.
|
||||
*/
|
||||
bool BKE_paint_brush_asset_set(Paint *paint,
|
||||
Brush *brush,
|
||||
const AssetWeakReference &weak_asset_reference);
|
||||
|
||||
/* Paint palette. */
|
||||
|
||||
Palette *BKE_paint_palette(Paint *paint);
|
||||
void BKE_paint_palette_set(Paint *p, Palette *palette);
|
||||
void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, int add_index);
|
||||
|
@ -252,19 +272,6 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, f
|
|||
|
||||
void BKE_paint_stroke_get_average(const Scene *scene, const Object *ob, float stroke[3]);
|
||||
|
||||
/* Tool slot API. */
|
||||
|
||||
void BKE_paint_toolslots_init_from_main(Main *bmain);
|
||||
void BKE_paint_toolslots_len_ensure(Paint *paint, int len);
|
||||
void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush);
|
||||
void BKE_paint_toolslots_brush_update(Paint *paint);
|
||||
/**
|
||||
* Run this to ensure brush types are set for each slot on entering modes
|
||||
* (for new scenes for example).
|
||||
*/
|
||||
void BKE_paint_toolslots_brush_validate(Main *bmain, Paint *paint);
|
||||
Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index);
|
||||
|
||||
/* .blend I/O */
|
||||
|
||||
void BKE_paint_blend_write(BlendWriter *writer, Paint *paint);
|
||||
|
@ -726,7 +733,7 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph,
|
|||
Main *bmain,
|
||||
Object *ob,
|
||||
MultiresModifierData *mmd);
|
||||
void BKE_sculpt_toolsettings_data_ensure(Scene *scene);
|
||||
void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene);
|
||||
|
||||
PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ extern "C" {
|
|||
struct UserDef;
|
||||
struct bUserExtensionRepo;
|
||||
struct bUserAssetLibrary;
|
||||
struct bUserAssetShelfSettings;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Assert Libraries
|
||||
|
@ -122,6 +123,27 @@ int BKE_preferences_extension_repo_get_index(const UserDef *userdef,
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #bUserAssetShelvesSettings
|
||||
* \{ */
|
||||
|
||||
bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_get(const UserDef *userdef,
|
||||
const char *shelf_idname);
|
||||
bool BKE_preferences_asset_shelf_settings_is_catalog_path_enabled(const UserDef *userdef,
|
||||
const char *shelf_idname,
|
||||
const char *catalog_path);
|
||||
/**
|
||||
* Enable a catalog path for a asset shelf identified by \a shelf_idname. Will create the shelf
|
||||
* settings in the Preferences if necessary.
|
||||
* \return Return true if the catalog was newly enabled. The Preferences should be tagged as dirty
|
||||
* then.
|
||||
*/
|
||||
bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *userdef,
|
||||
const char *shelf_idname,
|
||||
const char *catalog_path);
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -233,6 +233,12 @@ struct ARegionType {
|
|||
/* return context data */
|
||||
bContextDataCallback context;
|
||||
|
||||
/**
|
||||
* Called on every frame in which the region's poll succeeds, regardless of visibility, before
|
||||
* drawing, visibility evaluation and initialization. Allows the region to override visibility.
|
||||
*/
|
||||
void (*on_poll_success)(const bContext *C, ARegion *region);
|
||||
|
||||
/**
|
||||
* Called whenever the user changes the region's size. Not called when the size is changed
|
||||
* through other means, like to adjust for a scaled down window.
|
||||
|
@ -516,6 +522,8 @@ enum AssetShelfTypeFlag {
|
|||
/** Do not trigger asset dragging on drag events. Drag events can be overridden with custom
|
||||
* keymap items then. */
|
||||
ASSET_SHELF_TYPE_FLAG_NO_ASSET_DRAG = (1 << 0),
|
||||
ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE = (1 << 1),
|
||||
ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS = (1 << 2),
|
||||
|
||||
ASSET_SHELF_TYPE_FLAG_MAX
|
||||
};
|
||||
|
@ -526,8 +534,13 @@ struct AssetShelfType {
|
|||
|
||||
int space_type;
|
||||
|
||||
/** Operator to call when activating a grid view item. */
|
||||
std::string activate_operator;
|
||||
|
||||
AssetShelfTypeFlag flag;
|
||||
|
||||
short default_preview_size;
|
||||
|
||||
/** Determine if asset shelves of this type should be available in current context or not. */
|
||||
bool (*poll)(const bContext *C, const AssetShelfType *shelf_type);
|
||||
|
||||
|
@ -542,6 +555,8 @@ struct AssetShelfType {
|
|||
const blender::asset_system::AssetRepresentation *asset,
|
||||
uiLayout *layout);
|
||||
|
||||
const AssetWeakReference *(*get_active_asset)(const AssetShelfType *shelf_type);
|
||||
|
||||
/* RNA integration */
|
||||
ExtensionRNA rna_ext;
|
||||
};
|
||||
|
|
|
@ -66,6 +66,7 @@ set(SRC
|
|||
intern/armature_update.cc
|
||||
intern/asset.cc
|
||||
intern/asset_weak_reference.cc
|
||||
intern/asset_edit.cc
|
||||
intern/attribute.cc
|
||||
intern/attribute_access.cc
|
||||
intern/attribute_math.cc
|
||||
|
@ -254,7 +255,6 @@ set(SRC
|
|||
intern/packedFile.cc
|
||||
intern/paint.cc
|
||||
intern/paint_canvas.cc
|
||||
intern/paint_toolslots.cc
|
||||
intern/particle.cc
|
||||
intern/particle_child.cc
|
||||
intern/particle_distribute.cc
|
||||
|
@ -335,6 +335,7 @@ set(SRC
|
|||
BKE_appdir.hh
|
||||
BKE_armature.hh
|
||||
BKE_asset.hh
|
||||
BKE_asset_edit.hh
|
||||
BKE_attribute.h
|
||||
BKE_attribute.hh
|
||||
BKE_attribute_math.hh
|
||||
|
|
|
@ -0,0 +1,497 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "AS_asset_identifier.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_blendfile_link_append.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
#include "BLO_readfile.hh"
|
||||
#include "BLO_writefile.hh"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Asset library blend file, with editable contents.
|
||||
*/
|
||||
struct AssetEditBlend {
|
||||
std::string filepath;
|
||||
Main *main;
|
||||
bool is_editable = false;
|
||||
|
||||
AssetEditBlend(const std::string &filepath);
|
||||
~AssetEditBlend();
|
||||
|
||||
AssetEditBlend(const AssetEditBlend &) = delete;
|
||||
AssetEditBlend(AssetEditBlend &&other);
|
||||
AssetEditBlend &operator=(AssetEditBlend &&other);
|
||||
|
||||
ID *ensure_id(ID_Type id_type, const char *asset_name);
|
||||
void reload(Main &global_main);
|
||||
void clear_users(Main &global_main);
|
||||
};
|
||||
|
||||
AssetEditBlend::AssetEditBlend(const std::string &filepath)
|
||||
: filepath(std::move(filepath)), main(BKE_main_new())
|
||||
{
|
||||
this->main->is_asset_weak_reference_main = true;
|
||||
BLI_assert(!BLI_path_is_rel(filepath.c_str()));
|
||||
|
||||
/* Fairly simple check based on filepath only.
|
||||
* - Ends with `.asset.bend` extensions.
|
||||
* - Is located in user asset library.
|
||||
*
|
||||
* TODO?
|
||||
* - Check file contents.
|
||||
* - Check file is writable.
|
||||
*/
|
||||
this->is_editable = StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) &&
|
||||
BKE_preferences_asset_library_containing_path(&U, filepath.c_str());
|
||||
}
|
||||
|
||||
AssetEditBlend::~AssetEditBlend()
|
||||
{
|
||||
if (main) {
|
||||
BKE_main_free(main);
|
||||
}
|
||||
}
|
||||
|
||||
AssetEditBlend::AssetEditBlend(AssetEditBlend &&other)
|
||||
: filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr))
|
||||
{
|
||||
}
|
||||
AssetEditBlend &AssetEditBlend::operator=(AssetEditBlend &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->filepath = std::exchange(other.filepath, "");
|
||||
this->main = std::exchange(other.main, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ID *AssetEditBlend::ensure_id(const ID_Type id_type, const char *asset_name)
|
||||
{
|
||||
/* Check if we have the asset already. */
|
||||
ID *local_asset = BKE_libblock_find_name(this->main, id_type, asset_name);
|
||||
if (local_asset) {
|
||||
BLI_assert(ID_IS_ASSET(local_asset));
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
/* Load asset from asset library. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = this->main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, filepath.c_str(), nullptr);
|
||||
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, asset_name, id_type, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
BKE_blendfile_append(lapp_context, nullptr);
|
||||
|
||||
local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true);
|
||||
|
||||
/* Verify that the name matches. It must for referencing the same asset again to work. */
|
||||
BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name));
|
||||
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
static std::string asset_root_path_for_save(const bUserAssetLibrary &user_library,
|
||||
const ID_Type id_type)
|
||||
{
|
||||
BLI_assert(user_library.dirpath[0] != '\0');
|
||||
|
||||
char libpath[FILE_MAX];
|
||||
BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath));
|
||||
BLI_path_slash_native(libpath);
|
||||
BLI_path_normalize(libpath);
|
||||
|
||||
/* Capitalize folder name. Ideally this would already available in
|
||||
* the type info to work correctly with multiple words. */
|
||||
const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_idcode(id_type);
|
||||
std::string name = id_type_info->name_plural;
|
||||
name[0] = BLI_toupper_ascii(name[0]);
|
||||
|
||||
return std::string(libpath) + SEP + "Saved" + SEP + name;
|
||||
}
|
||||
|
||||
static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_library,
|
||||
const StringRef base_name,
|
||||
const ID_Type id_type,
|
||||
ReportList &reports)
|
||||
{
|
||||
std::string root_path = asset_root_path_for_save(user_library, id_type);
|
||||
BLI_assert(!root_path.empty());
|
||||
|
||||
if (!BLI_dir_create_recursive(root_path.c_str())) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to create asset library directory to save brush");
|
||||
return "";
|
||||
}
|
||||
|
||||
char base_name_filesafe[FILE_MAXFILE];
|
||||
BLI_strncpy(base_name_filesafe,
|
||||
base_name.data(),
|
||||
std::min(sizeof(base_name_filesafe), size_t(base_name.size())));
|
||||
BLI_path_make_safe_filename(base_name_filesafe);
|
||||
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX;
|
||||
if (!BLI_is_file(filepath.c_str())) {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
for (int i = 1;; i++) {
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) +
|
||||
BLENDER_ASSET_FILE_SUFFIX;
|
||||
if (!BLI_is_file((filepath.c_str()))) {
|
||||
return filepath;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static bool asset_write_in_library(Main *bmain,
|
||||
const ID &id_const,
|
||||
const StringRef name,
|
||||
const StringRefNull filepath,
|
||||
std::string &final_full_file_path,
|
||||
ReportList &reports)
|
||||
{
|
||||
/* TODO: Comment seems to be resolved by separate #Main storage?
|
||||
* XXX
|
||||
* FIXME
|
||||
*
|
||||
* This code is _pure evil_. It does in-place manipulation on IDs in global Main database,
|
||||
* temporarilly remove them and add them back...
|
||||
*
|
||||
* Use it as-is for now (in a similar way as python API or copy-to-buffer works). Nut the whole
|
||||
* 'BKE_blendfile_write_partial' code needs to be completely refactored.
|
||||
*
|
||||
* Ideas:
|
||||
* - Have `BKE_blendfile_write_partial_begin` return a new temp Main.
|
||||
* - Replace `BKE_blendfile_write_partial_tag_ID` by API to add IDs to this temp Main.
|
||||
* + This should _duplicate_ the ID, not remove the original one from the source Main!
|
||||
* - Have API to automatically also duplicate dependencies into temp Main.
|
||||
* + Have options to e.g. make all duplicated IDs 'local' (i.e. remove their library data).
|
||||
* - `BKE_blendfile_write_partial` then simply write the given temp main.
|
||||
* - `BKE_blendfile_write_partial_end` frees the temp Main.
|
||||
*/
|
||||
|
||||
ID &id = const_cast<ID &>(id_const);
|
||||
|
||||
const short prev_flag = id.flag;
|
||||
const int prev_tag = id.tag;
|
||||
const int prev_us = id.us;
|
||||
const std::string prev_name = id.name + 2;
|
||||
/* TODO: Remove library overrides stuff now that they are not used for brush assets. */
|
||||
IDOverrideLibrary *prev_liboverride = id.override_library;
|
||||
const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */
|
||||
const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
|
||||
|
||||
BKE_blendfile_write_partial_begin(bmain);
|
||||
|
||||
id.flag |= LIB_FAKEUSER;
|
||||
id.tag &= ~LIB_TAG_RUNTIME;
|
||||
id.us = 1;
|
||||
BLI_strncpy(id.name + 2, name.data(), std::min(sizeof(id.name) - 2, size_t(name.size())));
|
||||
id.override_library = nullptr;
|
||||
|
||||
BKE_blendfile_write_partial_tag_ID(&id, true);
|
||||
|
||||
/* TODO: check overwriting existing file. */
|
||||
/* TODO: ensure filepath contains only valid characters for file system. */
|
||||
const bool sucess = BKE_blendfile_write_partial(
|
||||
bmain, filepath.c_str(), write_flags, remap_mode, &reports);
|
||||
|
||||
if (sucess) {
|
||||
final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name;
|
||||
}
|
||||
|
||||
BKE_blendfile_write_partial_end(bmain);
|
||||
|
||||
BKE_blendfile_write_partial_tag_ID(&id, false);
|
||||
id.flag = prev_flag;
|
||||
id.tag = prev_tag;
|
||||
id.us = prev_us;
|
||||
BLI_strncpy(id.name + 2, prev_name.c_str(), sizeof(id.name) - 2);
|
||||
id.override_library = prev_liboverride;
|
||||
|
||||
return sucess;
|
||||
}
|
||||
|
||||
void AssetEditBlend::reload(Main &global_main)
|
||||
{
|
||||
Main *old_main = this->main;
|
||||
this->main = BKE_main_new();
|
||||
this->main->is_asset_weak_reference_main = true;
|
||||
|
||||
/* Fill fresh main database with same datablock as before. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = this->main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, this->filepath.c_str(), nullptr);
|
||||
|
||||
/* Requests all existing datablocks to be appended again. */
|
||||
ID *old_id;
|
||||
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
|
||||
ID_Type old_id_code = GS(old_id->name);
|
||||
if (BKE_idtype_idcode_is_linkable(old_id_code)) {
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, old_id->name + 2, old_id_code, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
BKE_blendfile_append(lapp_context, nullptr);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true);
|
||||
|
||||
/* Remap old to new. */
|
||||
bke::id::IDRemapper mappings;
|
||||
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
|
||||
ID *new_id = BKE_libblock_find_name(this->main, GS(old_id->name), old_id->name + 2);
|
||||
mappings.add(old_id, new_id);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_libblock_remap_multiple(&global_main, mappings, 0);
|
||||
|
||||
/* Free old database. */
|
||||
BKE_main_free(old_main);
|
||||
}
|
||||
|
||||
void AssetEditBlend::clear_users(Main &global_main)
|
||||
{
|
||||
/* Remap old to null pointer. */
|
||||
bke::id::IDRemapper mappings;
|
||||
ID *old_id;
|
||||
FOREACH_MAIN_ID_BEGIN (this->main, old_id) {
|
||||
mappings.add(old_id, nullptr);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_libblock_remap_multiple(&global_main, mappings, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
|
||||
static Vector<AssetEditBlend> &asset_edit_blend_get_all()
|
||||
{
|
||||
static Vector<AssetEditBlend> mains;
|
||||
return mains;
|
||||
}
|
||||
|
||||
static AssetEditBlend *asset_edit_blend_from_id(const ID &id)
|
||||
{
|
||||
BLI_assert(id.tag & LIB_TAG_ASSET_MAIN);
|
||||
|
||||
for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) {
|
||||
/* TODO: Look into make this whole thing more efficient. */
|
||||
ListBase *lb = which_libbase(asset_blend.main, GS(id.name));
|
||||
LISTBASE_FOREACH (ID *, other_id, lb) {
|
||||
if (&id == other_id) {
|
||||
return &asset_blend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Main *asset_edit_main(const ID &id)
|
||||
{
|
||||
const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
return (asset_blend) ? asset_blend->main : nullptr;
|
||||
}
|
||||
|
||||
static AssetEditBlend &asset_edit_blend_file_ensure(const StringRef filepath)
|
||||
{
|
||||
for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) {
|
||||
if (asset_blend.filepath == filepath) {
|
||||
return asset_blend;
|
||||
}
|
||||
}
|
||||
|
||||
asset_edit_blend_get_all().append_as(filepath);
|
||||
return asset_edit_blend_get_all().last();
|
||||
}
|
||||
|
||||
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
|
||||
const ID &id,
|
||||
const StringRef name,
|
||||
const bUserAssetLibrary &user_library,
|
||||
ReportList &reports)
|
||||
{
|
||||
const std::string filepath = asset_blendfile_path_for_save(
|
||||
user_library, name, GS(id.name), reports);
|
||||
|
||||
/* Save to asset library. */
|
||||
Main *asset_main = BKE_main_from_id(&global_main, &id);
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(
|
||||
asset_main, id, name, filepath, final_full_asset_filepath, reports);
|
||||
if (!success) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
BKE_reportf(&reports, RPT_INFO, "Saved \"%s\"", filepath.c_str());
|
||||
|
||||
return final_full_asset_filepath;
|
||||
}
|
||||
|
||||
bool asset_edit_id_save(Main & /*global_main*/, const ID &id, ReportList &reports)
|
||||
{
|
||||
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
if (asset_blend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(asset_blend->main,
|
||||
id,
|
||||
id.name + 2,
|
||||
asset_blend->filepath.c_str(),
|
||||
final_full_asset_filepath,
|
||||
reports);
|
||||
|
||||
if (!success) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_revert(Main &global_main, const ID &id, ReportList & /*reports*/)
|
||||
{
|
||||
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
if (asset_blend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reload entire main, including texture dependencies. This relies on there
|
||||
* being only a single brush asset per blend file. */
|
||||
asset_blend->reload(global_main);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_delete(Main &global_main, const ID &id, ReportList &reports)
|
||||
{
|
||||
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
if (asset_blend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BLI_delete(asset_blend->filepath.c_str(), false, false) != 0) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to delete asset library file");
|
||||
return false;
|
||||
}
|
||||
|
||||
asset_blend->clear_users(global_main);
|
||||
|
||||
int index = 0;
|
||||
for (AssetEditBlend &asset_blend_iter : asset_edit_blend_get_all()) {
|
||||
if (&asset_blend_iter == asset_blend) {
|
||||
asset_edit_blend_get_all().remove(index);
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ID *asset_edit_id_from_weak_reference(Main &global_main,
|
||||
const ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref)
|
||||
{
|
||||
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *asset_lib_path, *asset_group, *asset_name;
|
||||
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
&weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
|
||||
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(asset_name != nullptr);
|
||||
|
||||
/* Find asset in current blend file. */
|
||||
if (asset_lib_path == nullptr) {
|
||||
ID *local_asset = BKE_libblock_find_name(&global_main, id_type, asset_name);
|
||||
BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset));
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
/* If weak reference resolves to a null library path, assume we are in local asset case. */
|
||||
AssetEditBlend &asset_blend = asset_edit_blend_file_ensure(asset_lib_path);
|
||||
return asset_blend.ensure_id(id_type, asset_name);
|
||||
}
|
||||
|
||||
bool asset_edit_id_is_editable(const ID &id)
|
||||
{
|
||||
if (!(id.tag & LIB_TAG_ASSET_MAIN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
return (asset_blend) ? asset_blend->is_editable : false;
|
||||
}
|
||||
|
||||
void asset_edit_main_free_all()
|
||||
{
|
||||
asset_edit_blend_get_all().clear_and_shrink();
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
|
@ -7,15 +7,26 @@
|
|||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "AS_asset_identifier.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_blendfile_link_append.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
#include "BLO_readfile.hh"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
|
||||
|
@ -73,6 +84,20 @@ AssetWeakReference &AssetWeakReference::operator=(AssetWeakReference &&other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const AssetWeakReference &a, const AssetWeakReference &b)
|
||||
{
|
||||
if (a.asset_library_type != b.asset_library_type) {
|
||||
return false;
|
||||
}
|
||||
if (StringRef(a.asset_library_identifier) != StringRef(b.asset_library_identifier)) {
|
||||
return false;
|
||||
}
|
||||
if (StringRef(a.relative_asset_identifier) != StringRef(b.relative_asset_identifier)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
AssetWeakReference AssetWeakReference::make_reference(
|
||||
const asset_system::AssetLibrary &library,
|
||||
const asset_system::AssetIdentifier &asset_identifier)
|
||||
|
@ -104,3 +129,58 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *
|
|||
BLO_read_string(reader, &weak_ref->asset_library_identifier);
|
||||
BLO_read_string(reader, &weak_ref->relative_asset_identifier);
|
||||
}
|
||||
|
||||
void BKE_asset_catalog_path_list_free(ListBase &catalog_path_list)
|
||||
{
|
||||
LISTBASE_FOREACH_MUTABLE (AssetCatalogPathLink *, catalog_path, &catalog_path_list) {
|
||||
MEM_delete(catalog_path->path);
|
||||
BLI_freelinkN(&catalog_path_list, catalog_path);
|
||||
}
|
||||
BLI_assert(BLI_listbase_is_empty(&catalog_path_list));
|
||||
}
|
||||
|
||||
ListBase BKE_asset_catalog_path_list_duplicate(const ListBase &catalog_path_list)
|
||||
{
|
||||
ListBase duplicated_list = {nullptr};
|
||||
|
||||
LISTBASE_FOREACH (AssetCatalogPathLink *, catalog_path, &catalog_path_list) {
|
||||
AssetCatalogPathLink *copied_path = MEM_cnew<AssetCatalogPathLink>(__func__);
|
||||
copied_path->path = BLI_strdup(catalog_path->path);
|
||||
|
||||
BLI_addtail(&duplicated_list, copied_path);
|
||||
}
|
||||
|
||||
return duplicated_list;
|
||||
}
|
||||
|
||||
void BKE_asset_catalog_path_list_blend_write(BlendWriter *writer,
|
||||
const ListBase &catalog_path_list)
|
||||
{
|
||||
LISTBASE_FOREACH (const AssetCatalogPathLink *, catalog_path, &catalog_path_list) {
|
||||
BLO_write_struct(writer, AssetCatalogPathLink, catalog_path);
|
||||
BLO_write_string(writer, catalog_path->path);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_asset_catalog_path_list_blend_read_data(BlendDataReader *reader,
|
||||
ListBase &catalog_path_list)
|
||||
{
|
||||
BLO_read_struct_list(reader, AssetCatalogPathLink, &catalog_path_list);
|
||||
LISTBASE_FOREACH (AssetCatalogPathLink *, catalog_path, &catalog_path_list) {
|
||||
BLO_read_data_address(reader, &catalog_path->path);
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_asset_catalog_path_list_has_path(const ListBase &catalog_path_list,
|
||||
const char *catalog_path)
|
||||
{
|
||||
return BLI_findstring_ptr(
|
||||
&catalog_path_list, catalog_path, offsetof(AssetCatalogPathLink, path)) != nullptr;
|
||||
}
|
||||
|
||||
void BKE_asset_catalog_path_list_add_path(ListBase &catalog_path_list, const char *catalog_path)
|
||||
{
|
||||
AssetCatalogPathLink *new_path = MEM_cnew<AssetCatalogPathLink>(__func__);
|
||||
new_path->path = BLI_strdup(catalog_path);
|
||||
BLI_addtail(&catalog_path_list, new_path);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "IMB_moviecache.hh"
|
||||
|
||||
#include "BKE_addon.h"
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blender.hh" /* own include */
|
||||
#include "BKE_blender_user_menu.hh" /* own include */
|
||||
#include "BKE_blender_version.h" /* own include */
|
||||
|
@ -32,6 +34,7 @@
|
|||
#include "BKE_idprop.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_studiolight.h"
|
||||
|
@ -59,6 +62,11 @@ void BKE_blender_free()
|
|||
|
||||
BKE_blender_globals_clear();
|
||||
|
||||
/* Free separate data-bases after #BKE_blender_globals_clear in case any data-blocks in the
|
||||
* global main use pointers to asset main data-blocks when they're freed. That generally
|
||||
* shouldn't happen but it's better to be safe. */
|
||||
blender::bke::asset_edit_main_free_all();
|
||||
|
||||
if (G.log.file != nullptr) {
|
||||
fclose(static_cast<FILE *>(G.log.file));
|
||||
}
|
||||
|
@ -340,6 +348,12 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
|
|||
BLI_freelistN(&userdef->script_directories);
|
||||
BLI_freelistN(&userdef->asset_libraries);
|
||||
BLI_freelistN(&userdef->extension_repos);
|
||||
LISTBASE_FOREACH_MUTABLE (bUserAssetShelfSettings *, settings, &userdef->asset_shelves_settings)
|
||||
{
|
||||
BKE_asset_catalog_path_list_free(settings->enabled_catalog_paths);
|
||||
MEM_freeN(settings);
|
||||
}
|
||||
BLI_listbase_clear(&userdef->asset_shelves_settings);
|
||||
|
||||
BLI_freelistN(&userdef->uistyles);
|
||||
BLI_freelistN(&userdef->uifonts);
|
||||
|
|
|
@ -675,6 +675,9 @@ static void setup_app_data(bContext *C,
|
|||
* (but may have a null `curscreen`). */
|
||||
else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) {
|
||||
BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene");
|
||||
if (blender::StringRefNull(bfd->main->filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
|
||||
bfd->main->is_asset_repository = true;
|
||||
}
|
||||
mode = LOAD_UI_OFF;
|
||||
}
|
||||
else if (G.fileflags & G_FILE_NO_UI) {
|
||||
|
@ -1259,6 +1262,15 @@ UserDef *BKE_blendfile_userdef_from_defaults()
|
|||
BKE_preferences_extension_repo_add_default(userdef);
|
||||
BKE_preferences_extension_repo_add_default_user(userdef);
|
||||
|
||||
{
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting");
|
||||
}
|
||||
|
||||
return userdef;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,23 +7,30 @@
|
|||
*/
|
||||
#include <optional>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_rand.h"
|
||||
|
||||
#include "BLO_readfile.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_bpath.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_query.hh"
|
||||
|
@ -34,6 +41,8 @@
|
|||
#include "BKE_preview_image.hh"
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
@ -378,38 +387,44 @@ static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id)
|
|||
}
|
||||
}
|
||||
|
||||
static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data)
|
||||
static void brush_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_data)
|
||||
{
|
||||
BlendLibReader *reader = (BlendLibReader *)cb_data->user_data;
|
||||
ID *self_id = cb_data->self_id;
|
||||
ID *id_old = *cb_data->id_pointer;
|
||||
/* Old data has not been remapped to new values of the pointers, if we want to keep the old
|
||||
* pointer here we need its new address. */
|
||||
ID *id_old_new = id_old != nullptr ? BLO_read_get_new_id_address(
|
||||
reader, self_id, ID_IS_LINKED(self_id), id_old) :
|
||||
nullptr;
|
||||
BLI_assert(id_old_new == nullptr || ELEM(id_old, id_old_new, id_old_new->orig_id));
|
||||
if (cb_data->cb_flag & IDWALK_CB_USER) {
|
||||
id_us_plus_no_lib(id_old_new);
|
||||
id_us_min(id_old);
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
|
||||
Brush *brush = reinterpret_cast<Brush *>(asset_ptr);
|
||||
BLI_assert(GS(brush->id.name) == ID_BR);
|
||||
|
||||
/* Most names copied from brush RNA (not all are available there though). */
|
||||
constexpr std::array mode_map{
|
||||
std::pair{"use_paint_sculpt", OB_MODE_SCULPT},
|
||||
std::pair{"use_paint_vertex", OB_MODE_VERTEX_PAINT},
|
||||
std::pair{"use_paint_weight", OB_MODE_WEIGHT_PAINT},
|
||||
std::pair{"use_paint_image", OB_MODE_TEXTURE_PAINT},
|
||||
/* Sculpt UVs in the image editor while in edit mode. */
|
||||
std::pair{"use_paint_uv_sculpt", OB_MODE_EDIT},
|
||||
std::pair{"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY},
|
||||
/* Note: Not defined in brush RNA, own name. */
|
||||
std::pair{"use_sculpt_grease_pencil", OB_MODE_SCULPT_GPENCIL_LEGACY},
|
||||
std::pair{"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY},
|
||||
std::pair{"use_weight_grease_pencil", OB_MODE_WEIGHT_GPENCIL_LEGACY},
|
||||
std::pair{"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES},
|
||||
};
|
||||
|
||||
for (const auto &mode_mapping : mode_map) {
|
||||
/* Only add bools for supported modes. */
|
||||
if (!(brush->ob_mode & mode_mapping.second)) {
|
||||
continue;
|
||||
}
|
||||
auto mode_property = idprop::create_bool(mode_mapping.first, true);
|
||||
BKE_asset_metadata_idprop_ensure(asset_data, mode_property.release());
|
||||
}
|
||||
*cb_data->id_pointer = id_old_new;
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old)
|
||||
{
|
||||
/* Whole Brush is preserved across undo-steps. */
|
||||
BKE_lib_id_swap(nullptr, id_new, id_old, false, 0);
|
||||
|
||||
/* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid.
|
||||
* NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */
|
||||
BKE_library_foreach_ID_link(nullptr, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP);
|
||||
|
||||
/* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be
|
||||
* fairly delicate. */
|
||||
std::swap(id_new->properties, id_old->properties);
|
||||
}
|
||||
static AssetTypeInfo AssetType_BR = {
|
||||
/*pre_save_fn*/ brush_asset_metadata_ensure,
|
||||
/*on_mark_asset_fn*/ brush_asset_metadata_ensure,
|
||||
};
|
||||
|
||||
IDTypeInfo IDType_ID_BR = {
|
||||
/*id_code*/ ID_BR,
|
||||
|
@ -421,8 +436,8 @@ IDTypeInfo IDType_ID_BR = {
|
|||
/*name*/ "Brush",
|
||||
/*name_plural*/ N_("brushes"),
|
||||
/*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH,
|
||||
/*flags*/ IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
/*asset_type_info*/ nullptr,
|
||||
/*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO,
|
||||
/*asset_type_info*/ &AssetType_BR,
|
||||
|
||||
/*init_data*/ brush_init_data,
|
||||
/*copy_data*/ brush_copy_data,
|
||||
|
@ -437,7 +452,7 @@ IDTypeInfo IDType_ID_BR = {
|
|||
/*blend_read_data*/ brush_blend_read_data,
|
||||
/*blend_read_after_liblink*/ brush_blend_read_after_liblink,
|
||||
|
||||
/*blend_read_undo_preserve*/ brush_undo_preserve,
|
||||
/*blend_read_undo_preserve*/ nullptr,
|
||||
|
||||
/*lib_override_apply_post*/ nullptr,
|
||||
};
|
||||
|
|
|
@ -1325,6 +1325,9 @@ void *BKE_libblock_alloc_in_lib(Main *bmain,
|
|||
if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) {
|
||||
DEG_id_type_tag(bmain, type);
|
||||
}
|
||||
if (bmain->is_asset_weak_reference_main) {
|
||||
id->tag |= LIB_TAG_ASSET_MAIN;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2);
|
||||
|
@ -1539,6 +1542,10 @@ void BKE_libblock_copy_in_lib(Main *bmain,
|
|||
DEG_id_type_tag(bmain, GS(new_id->name));
|
||||
}
|
||||
|
||||
if (bmain && bmain->is_asset_weak_reference_main) {
|
||||
new_id->tag |= LIB_TAG_ASSET_MAIN;
|
||||
}
|
||||
|
||||
*r_newid = new_id;
|
||||
}
|
||||
|
||||
|
@ -2194,13 +2201,6 @@ void BKE_id_tag_clear_atomic(ID *id, int tag)
|
|||
atomic_fetch_and_and_int32(&id->tag, ~tag);
|
||||
}
|
||||
|
||||
bool BKE_id_is_in_global_main(ID *id)
|
||||
{
|
||||
/* We do not want to fail when id is nullptr here, even though this is a bit strange behavior...
|
||||
*/
|
||||
return (id == nullptr || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1);
|
||||
}
|
||||
|
||||
bool BKE_id_can_be_asset(const ID *id)
|
||||
{
|
||||
return !ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY(id) &&
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_bpath.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
|
@ -445,6 +446,16 @@ bool BKE_main_is_empty(Main *bmain)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool BKE_main_has_issues(const Main *bmain)
|
||||
{
|
||||
return bmain->has_forward_compatibility_issues || bmain->is_asset_repository;
|
||||
}
|
||||
|
||||
bool BKE_main_needs_overwrite_confirm(const Main *bmain)
|
||||
{
|
||||
return bmain->has_forward_compatibility_issues || bmain->is_asset_repository;
|
||||
}
|
||||
|
||||
void BKE_main_lock(Main *bmain)
|
||||
{
|
||||
BLI_spin_lock((SpinLock *)bmain->lock);
|
||||
|
@ -955,3 +966,34 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/])
|
|||
|
||||
return (INDEX_ID_MAX - 1);
|
||||
}
|
||||
|
||||
Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify)
|
||||
{
|
||||
if (id == nullptr || (id->tag & LIB_TAG_NO_MAIN)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (id->tag & LIB_TAG_ASSET_MAIN) {
|
||||
return blender::bke::asset_edit_main(*id);
|
||||
}
|
||||
|
||||
if (verify) {
|
||||
/* This is rather expensive, so don't do by default and assume valid input. */
|
||||
if (BLI_findindex(which_libbase(global_main, GS(id->name)), id) == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Debug assert, especially for places that pass in G_MAIN. */
|
||||
#ifndef NDEBUG
|
||||
if (id->flag & LIB_EMBEDDED_DATA) {
|
||||
const ID *id_owner = BKE_id_owner_get(const_cast<ID *>(id));
|
||||
BLI_assert(BLI_findindex(which_libbase(global_main, GS(id_owner->name)), id_owner) != -1);
|
||||
}
|
||||
else {
|
||||
BLI_assert(BLI_findindex(which_libbase(global_main, GS(id->name)), id) != -1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return global_main;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "DNA_object_enums.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
|
@ -33,12 +34,15 @@
|
|||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_ccg.h"
|
||||
|
@ -323,7 +327,7 @@ void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag)
|
|||
overlay_flags &= ~(flag);
|
||||
}
|
||||
|
||||
bool BKE_paint_ensure_from_paintmode(Scene *sce, PaintMode mode)
|
||||
bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, PaintMode mode)
|
||||
{
|
||||
ToolSettings *ts = sce->toolsettings;
|
||||
Paint **paint_ptr = nullptr;
|
||||
|
@ -368,7 +372,7 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, PaintMode mode)
|
|||
break;
|
||||
}
|
||||
if (paint_ptr) {
|
||||
BKE_paint_ensure(ts, paint_ptr);
|
||||
BKE_paint_ensure(bmain, ts, paint_ptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -441,61 +445,6 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode m
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const char *BKE_paint_get_tool_prop_id_from_paintmode(const PaintMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case PaintMode::Sculpt:
|
||||
return "sculpt_tool";
|
||||
case PaintMode::Vertex:
|
||||
return "vertex_tool";
|
||||
case PaintMode::Weight:
|
||||
return "weight_tool";
|
||||
case PaintMode::Texture2D:
|
||||
case PaintMode::Texture3D:
|
||||
return "image_tool";
|
||||
case PaintMode::GPencil:
|
||||
return "gpencil_tool";
|
||||
case PaintMode::VertexGPencil:
|
||||
return "gpencil_vertex_tool";
|
||||
case PaintMode::SculptGPencil:
|
||||
return "gpencil_sculpt_tool";
|
||||
case PaintMode::WeightGPencil:
|
||||
return "gpencil_weight_tool";
|
||||
case PaintMode::SculptCurves:
|
||||
return "curves_sculpt_tool";
|
||||
case PaintMode::SculptGreasePencil:
|
||||
return "gpencil_sculpt_tool";
|
||||
case PaintMode::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invalid paint mode. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(const PaintMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case PaintMode::Sculpt:
|
||||
case PaintMode::GPencil:
|
||||
case PaintMode::Texture2D:
|
||||
case PaintMode::Texture3D:
|
||||
return BLT_I18NCONTEXT_ID_BRUSH;
|
||||
case PaintMode::Vertex:
|
||||
case PaintMode::Weight:
|
||||
case PaintMode::VertexGPencil:
|
||||
case PaintMode::SculptGPencil:
|
||||
case PaintMode::WeightGPencil:
|
||||
case PaintMode::SculptCurves:
|
||||
case PaintMode::SculptGreasePencil:
|
||||
case PaintMode::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invalid paint mode. */
|
||||
return BLT_I18NCONTEXT_DEFAULT;
|
||||
}
|
||||
|
||||
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
|
||||
{
|
||||
if (sce && view_layer) {
|
||||
|
@ -660,68 +609,229 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
|
|||
return PaintMode::Invalid;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_brush(Paint *p)
|
||||
static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint)
|
||||
{
|
||||
return (Brush *)BKE_paint_brush_for_read((const Paint *)p);
|
||||
/* Attempt to restore a valid active brush from brush asset information. */
|
||||
if (paint->brush != nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (paint->brush_asset_reference == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
|
||||
*bmain, ID_BR, *paint->brush_asset_reference));
|
||||
BLI_assert(brush == nullptr || (brush->id.tag & LIB_TAG_ASSET_MAIN));
|
||||
|
||||
/* Ensure we have a brush with appropriate mode to assign.
|
||||
* Could happen if contents of asset blend was manually changed. */
|
||||
if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
paint->brush_asset_reference = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->brush = brush;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *p)
|
||||
Brush *BKE_paint_brush(Paint *paint)
|
||||
{
|
||||
return p ? p->brush : nullptr;
|
||||
return (Brush *)BKE_paint_brush_for_read((const Paint *)paint);
|
||||
}
|
||||
|
||||
void BKE_paint_brush_set(Paint *p, Brush *br)
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *paint)
|
||||
{
|
||||
if (p) {
|
||||
id_us_min((ID *)p->brush);
|
||||
id_us_plus((ID *)br);
|
||||
p->brush = br;
|
||||
return paint ? paint->brush : nullptr;
|
||||
}
|
||||
|
||||
BKE_paint_toolslots_brush_update(p);
|
||||
bool BKE_paint_brush_set(Paint *paint, Brush *brush)
|
||||
{
|
||||
if (paint == nullptr || paint->brush == brush) {
|
||||
return false;
|
||||
}
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->brush = brush;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void paint_brush_asset_update(Paint &paint,
|
||||
Brush *brush,
|
||||
const AssetWeakReference &brush_asset_reference)
|
||||
{
|
||||
BLI_assert(&brush_asset_reference != paint.brush_asset_reference);
|
||||
MEM_delete(paint.brush_asset_reference);
|
||||
paint.brush_asset_reference = nullptr;
|
||||
|
||||
if (brush == nullptr || brush != paint.brush || !(brush->id.tag & LIB_TAG_ASSET_MAIN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
paint.brush_asset_reference = MEM_new<AssetWeakReference>(__func__, brush_asset_reference);
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_asset_set(Paint *paint,
|
||||
Brush *brush,
|
||||
const AssetWeakReference &weak_asset_reference)
|
||||
{
|
||||
/* Should not happen for users if brush assets are properly filtered by mode, but still protect
|
||||
* against it in case of invalid API usage. */
|
||||
if (brush && paint->runtime.ob_mode != brush->ob_mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
paint_brush_asset_update(*paint, brush, weak_asset_reference);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void paint_brush_set_essentials_reference(Paint *paint, const char *name)
|
||||
{
|
||||
/* Set brush asset reference to a named brush in the essentials asset library. */
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
paint->brush_asset_reference = weak_ref;
|
||||
paint->brush = nullptr;
|
||||
}
|
||||
|
||||
static void paint_brush_set_default_reference(Paint *paint)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
|
||||
switch (paint->runtime.ob_mode) {
|
||||
case OB_MODE_SCULPT:
|
||||
name = "Draw";
|
||||
break;
|
||||
case OB_MODE_VERTEX_PAINT:
|
||||
name = "Paint Vertex";
|
||||
break;
|
||||
case OB_MODE_WEIGHT_PAINT:
|
||||
name = "Paint Weight";
|
||||
break;
|
||||
case OB_MODE_TEXTURE_PAINT:
|
||||
name = "Paint Texture";
|
||||
break;
|
||||
case OB_MODE_SCULPT_CURVES:
|
||||
name = "Comb Curves";
|
||||
break;
|
||||
case OB_MODE_EDIT:
|
||||
/* TODO: UV sculpt. */
|
||||
break;
|
||||
case OB_MODE_PAINT_GPENCIL_LEGACY:
|
||||
name = "Pencil";
|
||||
break;
|
||||
case OB_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
name = "Paint Point Color";
|
||||
break;
|
||||
case OB_MODE_SCULPT_GPENCIL_LEGACY:
|
||||
name = "Smooth Stroke";
|
||||
break;
|
||||
case OB_MODE_WEIGHT_GPENCIL_LEGACY:
|
||||
name = "Paint Point Weight";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
paint_brush_set_essentials_reference(paint, name);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
|
||||
void BKE_paint_brush_set_default_references(ToolSettings *ts)
|
||||
{
|
||||
if (ts->sculpt) {
|
||||
paint_brush_set_default_reference(&ts->sculpt->paint);
|
||||
}
|
||||
if (ts->curves_sculpt) {
|
||||
paint_brush_set_default_reference(&ts->curves_sculpt->paint);
|
||||
}
|
||||
if (ts->wpaint) {
|
||||
paint_brush_set_default_reference(&ts->wpaint->paint);
|
||||
}
|
||||
if (ts->vpaint) {
|
||||
paint_brush_set_default_reference(&ts->vpaint->paint);
|
||||
}
|
||||
if (ts->gp_paint) {
|
||||
paint_brush_set_default_reference(&ts->gp_paint->paint);
|
||||
}
|
||||
if (ts->gp_vertexpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_vertexpaint->paint);
|
||||
}
|
||||
if (ts->gp_sculptpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_sculptpaint->paint);
|
||||
}
|
||||
if (ts->gp_weightpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_weightpaint->paint);
|
||||
}
|
||||
paint_brush_set_default_reference(&ts->imapaint.paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint)
|
||||
{
|
||||
paint_brush_set_default_reference(paint);
|
||||
return paint_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name)
|
||||
{
|
||||
paint_brush_set_essentials_reference(paint, name);
|
||||
return paint_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
void BKE_paint_brush_validate(Main *bmain, Paint *paint)
|
||||
{
|
||||
/* Clear brush with invalid mode. Unclear if this can still happen,
|
||||
* but kept from old paint toolslots code. */
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
BKE_paint_brush_set(paint, nullptr);
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
}
|
||||
}
|
||||
|
||||
static void paint_runtime_init(const ToolSettings *ts, Paint *paint)
|
||||
{
|
||||
if (paint == &ts->imapaint.paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT;
|
||||
}
|
||||
else if (ts->sculpt && paint == &ts->sculpt->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT;
|
||||
}
|
||||
else if (ts->vpaint && paint == &ts->vpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT;
|
||||
}
|
||||
else if (ts->wpaint && paint == &ts->wpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT;
|
||||
}
|
||||
else if (ts->gp_paint && paint == &ts->gp_paint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES;
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
paint->runtime.initialized = true;
|
||||
}
|
||||
|
||||
uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode)
|
||||
|
@ -1088,17 +1198,15 @@ eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode)
|
|||
}
|
||||
}
|
||||
|
||||
bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
|
||||
bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint)
|
||||
{
|
||||
Paint *paint = nullptr;
|
||||
if (*r_paint) {
|
||||
/* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to
|
||||
* check if already initialized. */
|
||||
if ((*r_paint)->runtime.tool_offset == 0) {
|
||||
if (!(*r_paint)->runtime.initialized) {
|
||||
/* Currently only image painting is initialized this way, others have to be allocated. */
|
||||
BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint));
|
||||
|
||||
BKE_paint_runtime_init(ts, *r_paint);
|
||||
paint_runtime_init(ts, *r_paint);
|
||||
}
|
||||
else {
|
||||
BLI_assert(ELEM(*r_paint,
|
||||
|
@ -1114,13 +1222,13 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
|
|||
(Paint *)&ts->imapaint));
|
||||
#ifndef NDEBUG
|
||||
Paint paint_test = **r_paint;
|
||||
BKE_paint_runtime_init(ts, *r_paint);
|
||||
paint_runtime_init(ts, *r_paint);
|
||||
/* Swap so debug doesn't hide errors when release fails. */
|
||||
std::swap(**r_paint, paint_test);
|
||||
BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode);
|
||||
BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset);
|
||||
#endif
|
||||
}
|
||||
paint_brush_set_from_asset_reference(bmain, *r_paint);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1163,7 +1271,8 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
|
|||
|
||||
*r_paint = paint;
|
||||
|
||||
BKE_paint_runtime_init(ts, paint);
|
||||
paint_runtime_init(ts, paint);
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1173,19 +1282,7 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
|
|||
UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings;
|
||||
Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode);
|
||||
|
||||
BKE_paint_ensure_from_paintmode(sce, mode);
|
||||
|
||||
/* If there's no brush, create one */
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush == nullptr) {
|
||||
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
|
||||
brush = BKE_brush_first_search(bmain, ob_mode);
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, "Brush", ob_mode);
|
||||
id_us_min(&brush->id); /* Fake user only. */
|
||||
}
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
}
|
||||
BKE_paint_ensure_from_paintmode(bmain, sce, mode);
|
||||
|
||||
copy_v3_v3_uchar(paint->paint_cursor_col, col);
|
||||
paint->paint_cursor_col[3] = 128;
|
||||
|
@ -1200,23 +1297,21 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
|
|||
void BKE_paint_free(Paint *paint)
|
||||
{
|
||||
BKE_curvemapping_free(paint->cavity_curve);
|
||||
MEM_SAFE_FREE(paint->tool_slots);
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
}
|
||||
|
||||
void BKE_paint_copy(const Paint *src, Paint *tar, const int flag)
|
||||
void BKE_paint_copy(const Paint *src, Paint *dst, const int flag)
|
||||
{
|
||||
tar->brush = src->brush;
|
||||
tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
|
||||
tar->tool_slots = static_cast<PaintToolSlot *>(MEM_dupallocN(src->tool_slots));
|
||||
dst->brush = src->brush;
|
||||
dst->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
|
||||
|
||||
if (src->brush_asset_reference) {
|
||||
dst->brush_asset_reference = MEM_new<AssetWeakReference>(__func__,
|
||||
*src->brush_asset_reference);
|
||||
}
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
||||
id_us_plus((ID *)tar->brush);
|
||||
id_us_plus((ID *)tar->palette);
|
||||
if (src->tool_slots != nullptr) {
|
||||
for (int i = 0; i < tar->tool_slots_len; i++) {
|
||||
id_us_plus((ID *)tar->tool_slots[i].brush);
|
||||
}
|
||||
}
|
||||
id_us_plus((ID *)dst->palette);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1237,7 +1332,9 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p)
|
|||
if (p->cavity_curve) {
|
||||
BKE_curvemapping_blend_write(writer, p->cavity_curve);
|
||||
}
|
||||
BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots);
|
||||
if (p->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_write(writer, p->brush_asset_reference);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p)
|
||||
|
@ -1250,17 +1347,13 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
|
|||
BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE);
|
||||
}
|
||||
|
||||
BLO_read_struct_array(reader, PaintToolSlot, p->tool_slots_len, &p->tool_slots);
|
||||
|
||||
/* Workaround for invalid data written in older versions. */
|
||||
const size_t expected_size = sizeof(PaintToolSlot) * p->tool_slots_len;
|
||||
if (p->tool_slots && MEM_allocN_len(p->tool_slots) < expected_size) {
|
||||
MEM_freeN(p->tool_slots);
|
||||
p->tool_slots = static_cast<PaintToolSlot *>(MEM_callocN(expected_size, "PaintToolSlot"));
|
||||
BLO_read_struct(reader, AssetWeakReference, &p->brush_asset_reference);
|
||||
if (p->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_read(reader, p->brush_asset_reference);
|
||||
}
|
||||
|
||||
p->paint_cursor = nullptr;
|
||||
BKE_paint_runtime_init(scene->toolsettings, p);
|
||||
paint_runtime_init(scene->toolsettings, p);
|
||||
}
|
||||
|
||||
bool paint_is_grid_face_hidden(const blender::BoundedBitSpan grid_hidden,
|
||||
|
@ -1994,9 +2087,9 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph,
|
|||
}
|
||||
}
|
||||
|
||||
void BKE_sculpt_toolsettings_data_ensure(Scene *scene)
|
||||
void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene)
|
||||
{
|
||||
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt);
|
||||
BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->sculpt);
|
||||
|
||||
Sculpt *sd = scene->toolsettings->sculpt;
|
||||
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tool Slot Initialization / Versioning
|
||||
*
|
||||
* These functions run to update old files (while versioning),
|
||||
* take care only to perform low-level functions here.
|
||||
* \{ */
|
||||
|
||||
void BKE_paint_toolslots_len_ensure(Paint *paint, int len)
|
||||
{
|
||||
/* Tool slots are 'uchar'. */
|
||||
BLI_assert(len <= UCHAR_MAX);
|
||||
if (paint->tool_slots_len < len) {
|
||||
paint->tool_slots = static_cast<PaintToolSlot *>(
|
||||
MEM_recallocN(paint->tool_slots, sizeof(*paint->tool_slots) * len));
|
||||
paint->tool_slots_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
static void paint_toolslots_init(Main *bmain, Paint *paint)
|
||||
{
|
||||
if (paint == nullptr) {
|
||||
return;
|
||||
}
|
||||
const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode);
|
||||
BLI_assert(paint->runtime.tool_offset && ob_mode);
|
||||
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush;
|
||||
brush = static_cast<Brush *>(brush->id.next))
|
||||
{
|
||||
if (brush->ob_mode & ob_mode) {
|
||||
const int slot_index = BKE_brush_tool_get(brush, paint);
|
||||
BKE_paint_toolslots_len_ensure(paint, slot_index + 1);
|
||||
if (paint->tool_slots[slot_index].brush == nullptr) {
|
||||
paint->tool_slots[slot_index].brush = brush;
|
||||
id_us_plus(&brush->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize runtime since this is called from versioning code.
|
||||
*/
|
||||
static void paint_toolslots_init_with_runtime(Main *bmain, ToolSettings *ts, Paint *paint)
|
||||
{
|
||||
if (paint == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Needed so #Paint_Runtime is updated when versioning. */
|
||||
BKE_paint_runtime_init(ts, paint);
|
||||
paint_toolslots_init(bmain, paint);
|
||||
}
|
||||
|
||||
void BKE_paint_toolslots_init_from_main(Main *bmain)
|
||||
{
|
||||
for (Scene *scene = static_cast<Scene *>(bmain->scenes.first); scene;
|
||||
scene = static_cast<Scene *>(scene->id.next))
|
||||
{
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->imapaint.paint);
|
||||
if (ts->sculpt) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->sculpt->paint);
|
||||
}
|
||||
if (ts->vpaint) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->vpaint->paint);
|
||||
}
|
||||
if (ts->wpaint) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->wpaint->paint);
|
||||
}
|
||||
if (ts->gp_paint) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_paint->paint);
|
||||
}
|
||||
if (ts->gp_vertexpaint) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_vertexpaint->paint);
|
||||
}
|
||||
if (ts->gp_sculptpaint) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_sculptpaint->paint);
|
||||
}
|
||||
if (ts->gp_weightpaint) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_weightpaint->paint);
|
||||
}
|
||||
if (ts->curves_sculpt) {
|
||||
paint_toolslots_init_with_runtime(bmain, ts, &ts->curves_sculpt->paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush)
|
||||
{
|
||||
const uint tool_offset = paint->runtime.tool_offset;
|
||||
UNUSED_VARS_NDEBUG(tool_offset);
|
||||
BLI_assert(tool_offset != 0);
|
||||
const int slot_index = BKE_brush_tool_get(brush, paint);
|
||||
BKE_paint_toolslots_len_ensure(paint, slot_index + 1);
|
||||
PaintToolSlot *tslot = &paint->tool_slots[slot_index];
|
||||
id_us_plus(&brush->id);
|
||||
if (tslot->brush) {
|
||||
id_us_min(&tslot->brush->id);
|
||||
}
|
||||
tslot->brush = brush;
|
||||
}
|
||||
|
||||
void BKE_paint_toolslots_brush_update(Paint *paint)
|
||||
{
|
||||
if (paint->brush == nullptr) {
|
||||
return;
|
||||
}
|
||||
BKE_paint_toolslots_brush_update_ex(paint, paint->brush);
|
||||
}
|
||||
|
||||
void BKE_paint_toolslots_brush_validate(Main *bmain, Paint *paint)
|
||||
{
|
||||
/* Clear slots with invalid slots or mode (unlikely but possible). */
|
||||
const uint tool_offset = paint->runtime.tool_offset;
|
||||
UNUSED_VARS_NDEBUG(tool_offset);
|
||||
const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode);
|
||||
BLI_assert(tool_offset && ob_mode);
|
||||
for (int i = 0; i < paint->tool_slots_len; i++) {
|
||||
PaintToolSlot *tslot = &paint->tool_slots[i];
|
||||
if (tslot->brush) {
|
||||
if ((i != BKE_brush_tool_get(tslot->brush, paint)) || (tslot->brush->ob_mode & ob_mode) == 0)
|
||||
{
|
||||
id_us_min(&tslot->brush->id);
|
||||
tslot->brush = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlikely but possible the active brush is not currently using a slot. */
|
||||
BKE_paint_toolslots_brush_update(paint);
|
||||
|
||||
/* Fill slots from brushes. */
|
||||
paint_toolslots_init(bmain, paint);
|
||||
}
|
||||
|
||||
Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index)
|
||||
{
|
||||
if (slot_index < paint->tool_slots_len) {
|
||||
PaintToolSlot *tslot = &paint->tool_slots[slot_index];
|
||||
return tslot->brush;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
|
@ -20,10 +20,12 @@
|
|||
#include "BLI_string_utils.hh"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_preferences.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
|
@ -399,5 +401,67 @@ int BKE_preferences_extension_repo_get_index(const UserDef *userdef,
|
|||
{
|
||||
return BLI_findindex(&userdef->extension_repos, repo);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #bUserAssetShelfSettings
|
||||
* \{ */
|
||||
|
||||
static bUserAssetShelfSettings *asset_shelf_settings_new(UserDef *userdef,
|
||||
const char *shelf_idname)
|
||||
{
|
||||
bUserAssetShelfSettings *settings = DNA_struct_default_alloc(bUserAssetShelfSettings);
|
||||
BLI_addtail(&userdef->asset_shelves_settings, settings);
|
||||
STRNCPY(settings->shelf_idname, shelf_idname);
|
||||
BLI_assert(BLI_listbase_is_empty(&settings->enabled_catalog_paths));
|
||||
return settings;
|
||||
}
|
||||
|
||||
static bUserAssetShelfSettings *asset_shelf_settings_ensure(UserDef *userdef,
|
||||
const char *shelf_idname)
|
||||
{
|
||||
if (bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_get(userdef,
|
||||
shelf_idname))
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
return asset_shelf_settings_new(userdef, shelf_idname);
|
||||
}
|
||||
|
||||
bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_get(const UserDef *userdef,
|
||||
const char *shelf_idname)
|
||||
{
|
||||
return static_cast<bUserAssetShelfSettings *>(
|
||||
BLI_findstring(&userdef->asset_shelves_settings,
|
||||
shelf_idname,
|
||||
offsetof(bUserAssetShelfSettings, shelf_idname)));
|
||||
}
|
||||
|
||||
bool BKE_preferences_asset_shelf_settings_is_catalog_path_enabled(const UserDef *userdef,
|
||||
const char *shelf_idname,
|
||||
const char *catalog_path)
|
||||
{
|
||||
const bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_get(userdef,
|
||||
shelf_idname);
|
||||
if (!settings) {
|
||||
return false;
|
||||
}
|
||||
return BKE_asset_catalog_path_list_has_path(settings->enabled_catalog_paths, catalog_path);
|
||||
}
|
||||
|
||||
bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *userdef,
|
||||
const char *shelf_idname,
|
||||
const char *catalog_path)
|
||||
{
|
||||
if (BKE_preferences_asset_shelf_settings_is_catalog_path_enabled(
|
||||
userdef, shelf_idname, catalog_path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bUserAssetShelfSettings *settings = asset_shelf_settings_ensure(userdef, shelf_idname);
|
||||
BKE_asset_catalog_path_list_add_path(settings->enabled_catalog_paths, catalog_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -588,27 +588,7 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
|
|||
SCENE_FOREACH_UNDO_RESTORE,
|
||||
reader,
|
||||
&paint_old->brush,
|
||||
IDWALK_CB_USER);
|
||||
|
||||
for (int i = 0; i < paint_old->tool_slots_len; i++) {
|
||||
/* This is a bit tricky.
|
||||
* - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so
|
||||
* this is equivalent to simply looping over slots from `paint`.
|
||||
* - In case we do `undo_restore`, we only want to consider the slots from the old one, since
|
||||
* those are the one we keep in the end.
|
||||
* + In case the new data has less valid slots, we feed in a dummy null pointer.
|
||||
* + In case the new data has more valid slots, the extra ones are ignored.
|
||||
*/
|
||||
brush_tmp = nullptr;
|
||||
brush_p = (paint && i < paint->tool_slots_len) ? &paint->tool_slots[i].brush : &brush_tmp;
|
||||
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data,
|
||||
brush_p,
|
||||
do_undo_restore,
|
||||
SCENE_FOREACH_UNDO_RESTORE,
|
||||
reader,
|
||||
&paint_old->tool_slots[i].brush,
|
||||
IDWALK_CB_USER);
|
||||
}
|
||||
IDWALK_CB_NOP);
|
||||
|
||||
Palette *palette_tmp = nullptr;
|
||||
Palette **palette_p = paint ? &paint->palette : &palette_tmp;
|
||||
|
@ -1005,6 +985,9 @@ static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data)
|
|||
if (scene->ed != nullptr) {
|
||||
SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data);
|
||||
}
|
||||
|
||||
/* TODO: Also handle `asset_weak_reference` here? Probably not, conceptually this is not a file
|
||||
* path. */
|
||||
}
|
||||
|
||||
static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address)
|
||||
|
|
|
@ -3375,6 +3375,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
|||
BLO_read_struct_list(reader, bUserScriptDirectory, &user->script_directories);
|
||||
BLO_read_struct_list(reader, bUserAssetLibrary, &user->asset_libraries);
|
||||
BLO_read_struct_list(reader, bUserExtensionRepo, &user->extension_repos);
|
||||
BLO_read_struct_list(reader, bUserAssetShelfSettings, &user->asset_shelves_settings);
|
||||
|
||||
LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) {
|
||||
keymap->modal_items = nullptr;
|
||||
|
@ -3422,6 +3423,10 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
|||
IDP_BlendDataRead(reader, &addon->prop);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bUserAssetShelfSettings *, shelf_settings, &user->asset_shelves_settings) {
|
||||
BKE_asset_catalog_path_list_blend_read_data(reader, shelf_settings->enabled_catalog_paths);
|
||||
}
|
||||
|
||||
/* XXX */
|
||||
user->uifonts.first = user->uifonts.last = nullptr;
|
||||
|
||||
|
|
|
@ -2554,7 +2554,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
brush->gpencil_tool = brush->gpencil_settings->brush_type;
|
||||
}
|
||||
}
|
||||
BKE_paint_toolslots_init_from_main(bmain);
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 280, 38)) {
|
||||
|
@ -2874,10 +2873,10 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
/* Ensure new Paint modes. */
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::GPencil);
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::WeightGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::GPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::WeightGPencil);
|
||||
|
||||
/* Set default Draw brush. */
|
||||
if (brush != nullptr) {
|
||||
|
@ -2918,9 +2917,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
/* Reset all grease pencil brushes. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
/* Ensure new Paint modes. */
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::WeightGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::WeightGPencil);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "DNA_movieclip_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include "BKE_armature.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curve.hh"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
|
@ -59,6 +61,7 @@
|
|||
#include "BKE_mesh_legacy_convert.hh"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_tracking.h"
|
||||
|
||||
|
@ -2022,6 +2025,33 @@ static bool seq_filter_bilinear_to_auto(Sequence *seq, void * /*user_data*/)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void update_paint_modes_for_brush_assets(Main &bmain)
|
||||
{
|
||||
/* Replace paint brushes with a reference to the default brush asset for that mode. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain.scenes) {
|
||||
BKE_paint_brush_set_default_references(scene->toolsettings);
|
||||
}
|
||||
|
||||
/* Replace persistent tool references with the new single builtin brush tool. */
|
||||
LISTBASE_FOREACH (WorkSpace *, workspace, &bmain.workspaces) {
|
||||
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
|
||||
if (tref->space_type != SPACE_VIEW3D) {
|
||||
continue;
|
||||
}
|
||||
if (!ELEM(tref->mode,
|
||||
CTX_MODE_SCULPT,
|
||||
CTX_MODE_SCULPT_CURVES,
|
||||
CTX_MODE_PAINT_TEXTURE,
|
||||
CTX_MODE_PAINT_VERTEX,
|
||||
CTX_MODE_PAINT_WEIGHT))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
STRNCPY(tref->idname, "builtin.brush");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void image_settings_avi_to_ffmpeg(Scene *scene)
|
||||
{
|
||||
if (ELEM(scene->r.im_format.imtype, R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG)) {
|
||||
|
@ -3236,6 +3266,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 24)) {
|
||||
update_paint_modes_for_brush_assets(*bmain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
|
@ -555,9 +556,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
}
|
||||
|
||||
/* Ensure new Paint modes. */
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(scene, PaintMode::WeightGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::WeightGPencil);
|
||||
|
||||
/* Enable cursor. */
|
||||
if (ts->gp_paint) {
|
||||
|
@ -908,4 +909,26 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Remove default brushes replaced by assets. Also remove outliner `treestore` that may point
|
||||
* to brushes. Normally the treestore is updated properly but it doesn't seem to update during
|
||||
* versioning code. It's not helpful anyway. */
|
||||
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) {
|
||||
if (space_link->spacetype == SPACE_OUTLINER) {
|
||||
SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(space_link);
|
||||
if (space_outliner->treestore) {
|
||||
BLI_mempool_destroy(space_outliner->treestore);
|
||||
space_outliner->treestore = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (Brush *, brush, &bmain->brushes) {
|
||||
BKE_id_delete(bmain, brush);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -950,6 +950,15 @@ void blo_do_versions_userdef(UserDef *userdef)
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting");
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a USER_VERSION_ATLEAST check.
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
|
||||
#include "MEM_guardedalloc.h" /* MEM_freeN */
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
#include "BKE_bpath.hh"
|
||||
#include "BKE_global.hh" /* For #Global `G`. */
|
||||
|
@ -934,6 +935,12 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
|
|||
LISTBASE_FOREACH (const bUserExtensionRepo *, repo_ref, &userdef->extension_repos) {
|
||||
BLO_write_struct(writer, bUserExtensionRepo, repo_ref);
|
||||
}
|
||||
LISTBASE_FOREACH (
|
||||
const bUserAssetShelfSettings *, shelf_settings, &userdef->asset_shelves_settings)
|
||||
{
|
||||
BLO_write_struct(writer, bUserAssetShelfSettings, shelf_settings);
|
||||
BKE_asset_catalog_path_list_blend_write(writer, shelf_settings->enabled_catalog_paths);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
|
||||
BLO_write_struct(writer, uiStyle, style);
|
||||
|
|
|
@ -34,6 +34,7 @@ set(SRC
|
|||
intern/asset_shelf.cc
|
||||
intern/asset_shelf_asset_view.cc
|
||||
intern/asset_shelf_catalog_selector.cc
|
||||
intern/asset_shelf_popup.cc
|
||||
intern/asset_shelf_regiondata.cc
|
||||
intern/asset_shelf_settings.cc
|
||||
intern/asset_temp_id_consumer.cc
|
||||
|
|
|
@ -17,9 +17,16 @@ struct bContextDataResult;
|
|||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
struct Main;
|
||||
struct SpaceType;
|
||||
struct uiBlock;
|
||||
struct RegionPollParams;
|
||||
struct wmWindowManager;
|
||||
|
||||
namespace blender {
|
||||
class StringRef;
|
||||
class StringRefNull;
|
||||
} // namespace blender
|
||||
|
||||
namespace blender::ed::asset::shelf {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -45,6 +52,7 @@ void region_on_user_resize(const ARegion *region);
|
|||
void region_listen(const wmRegionListenerParams *params);
|
||||
void region_layout(const bContext *C, ARegion *region);
|
||||
void region_draw(const bContext *C, ARegion *region);
|
||||
void region_on_poll_success(const bContext *C, ARegion *region);
|
||||
void region_blend_read_data(BlendDataReader *reader, ARegion *region);
|
||||
void region_blend_write(BlendWriter *writer, ARegion *region);
|
||||
int region_prefsizey();
|
||||
|
@ -57,6 +65,22 @@ void header_regiontype_register(ARegionType *region_type, const int space_type);
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Asset shelf type */
|
||||
|
||||
bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type);
|
||||
AssetShelfType *type_find_from_idname(const SpaceType &space_type, StringRefNull idname);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Asset shelf popup */
|
||||
|
||||
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type);
|
||||
void type_popup_unlink(const AssetShelfType &shelf_type);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
void type_unlink(const Main &bmain, const AssetShelfType &shelf_type);
|
||||
|
@ -64,6 +88,8 @@ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type);
|
|||
int tile_width(const AssetShelfSettings &settings);
|
||||
int tile_height(const AssetShelfSettings &settings);
|
||||
|
||||
AssetShelf *active_shelf_from_area(const ScrArea *area);
|
||||
|
||||
int context(const bContext *C, const char *member, bContextDataResult *result);
|
||||
|
||||
} // namespace blender::ed::asset::shelf
|
||||
|
|
|
@ -16,7 +16,8 @@ namespace blender::ed::asset {
|
|||
|
||||
bool id_type_is_non_experimental(const ID *id);
|
||||
#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \
|
||||
(FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | FILTER_ID_NT)
|
||||
(FILTER_ID_BR | FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | \
|
||||
FILTER_ID_NT)
|
||||
|
||||
/**
|
||||
* Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset,
|
||||
|
@ -39,6 +40,6 @@ int64_t types_supported_as_filter_flags();
|
|||
* Should start with a consonant, so usages can prefix it with "a" (not "an").
|
||||
*/
|
||||
#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING \
|
||||
"Material, Collection, Object, Pose Action, Node Group or World"
|
||||
"Material, Collection, Object, Brush, Pose Action, Node Group or World"
|
||||
|
||||
} // namespace blender::ed::asset
|
||||
|
|
|
@ -142,7 +142,7 @@ void AssetList::setup()
|
|||
/* Relevant bits from file_refresh(). */
|
||||
/* TODO pass options properly. */
|
||||
filelist_setrecursion(files, FILE_SELECT_MAX_RECURSIONS);
|
||||
filelist_setsorting(files, FILE_SORT_ALPHA, false);
|
||||
filelist_setsorting(files, FILE_SORT_ASSET_CATALOG, false);
|
||||
filelist_setlibrary(files, &library_ref_);
|
||||
filelist_setfilter_options(
|
||||
files,
|
||||
|
@ -272,6 +272,7 @@ void AssetList::clear(const bContext *C)
|
|||
filelist_readjob_stop(files, CTX_wm_manager(C));
|
||||
filelist_freelib(files);
|
||||
filelist_clear(files);
|
||||
filelist_tag_force_reset(files);
|
||||
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr);
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ static const asset_system::AssetRepresentation *get_local_asset_from_relative_id
|
|||
return matching_asset;
|
||||
}
|
||||
|
||||
static const asset_system::AssetRepresentation *find_asset_from_weak_ref(
|
||||
const asset_system::AssetRepresentation *find_asset_from_weak_ref(
|
||||
const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports)
|
||||
{
|
||||
if (weak_ref.asset_library_type == ASSET_LIBRARY_LOCAL) {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "AS_asset_catalog_path.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
@ -51,9 +52,7 @@ void send_redraw_notifier(const bContext &C)
|
|||
/** \name Shelf Type
|
||||
* \{ */
|
||||
|
||||
static bool asset_shelf_type_poll(const bContext &C,
|
||||
const SpaceType &space_type,
|
||||
const AssetShelfType *shelf_type)
|
||||
bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type)
|
||||
{
|
||||
if (!shelf_type) {
|
||||
return false;
|
||||
|
@ -70,7 +69,17 @@ static bool asset_shelf_type_poll(const bContext &C,
|
|||
return !shelf_type->poll || shelf_type->poll(&C, shelf_type);
|
||||
}
|
||||
|
||||
static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, AssetShelf &shelf)
|
||||
AssetShelfType *type_find_from_idname(const SpaceType &space_type, StringRefNull idname)
|
||||
{
|
||||
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
|
||||
if (idname == shelf_type->idname) {
|
||||
return shelf_type.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AssetShelfType *type_ensure(const SpaceType &space_type, AssetShelf &shelf)
|
||||
{
|
||||
if (shelf.type) {
|
||||
return shelf.type;
|
||||
|
@ -86,11 +95,12 @@ static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, Asse
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static AssetShelf *create_shelf_from_type(AssetShelfType &type)
|
||||
AssetShelf *create_shelf_from_type(AssetShelfType &type)
|
||||
{
|
||||
AssetShelf *shelf = MEM_new<AssetShelf>(__func__);
|
||||
*shelf = dna::shallow_zero_initialize();
|
||||
shelf->settings.preview_size = DEFAULT_TILE_SIZE;
|
||||
shelf->settings.preview_size = type.default_preview_size ? type.default_preview_size :
|
||||
DEFAULT_TILE_SIZE;
|
||||
shelf->settings.asset_library_reference = asset_system::all_library_reference();
|
||||
shelf->type = &type;
|
||||
shelf->preferred_row_count = 1;
|
||||
|
@ -133,19 +143,21 @@ static void activate_shelf(RegionAssetShelf &shelf_regiondata, AssetShelf &shelf
|
|||
*
|
||||
* The returned shelf is guaranteed to have its #AssetShelf.type pointer set.
|
||||
*
|
||||
* \param on_create: Function called when a new asset shelf is created (case 3).
|
||||
*
|
||||
* \return A non-owning pointer to the now active shelf. Might be null if no shelf is valid in
|
||||
* current context (all polls failed).
|
||||
*/
|
||||
static AssetShelf *update_active_shelf(const bContext &C,
|
||||
const SpaceType &space_type,
|
||||
RegionAssetShelf &shelf_regiondata)
|
||||
RegionAssetShelf &shelf_regiondata,
|
||||
FunctionRef<void(AssetShelf &new_shelf)> on_create)
|
||||
{
|
||||
/* Note: Don't access #AssetShelf.type directly, use #asset_shelf_type_ensure(). */
|
||||
|
||||
/* Case 1: */
|
||||
if (shelf_regiondata.active_shelf &&
|
||||
asset_shelf_type_poll(
|
||||
C, space_type, asset_shelf_type_ensure(space_type, *shelf_regiondata.active_shelf)))
|
||||
type_poll(C, space_type, type_ensure(space_type, *shelf_regiondata.active_shelf)))
|
||||
{
|
||||
/* Not a strong precondition, but if this is wrong something weird might be going on. */
|
||||
BLI_assert(shelf_regiondata.active_shelf == shelf_regiondata.shelves.first);
|
||||
|
@ -159,7 +171,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (asset_shelf_type_poll(C, space_type, asset_shelf_type_ensure(space_type, *shelf))) {
|
||||
if (type_poll(C, space_type, type_ensure(space_type, *shelf))) {
|
||||
/* Found a valid previously activated shelf, reactivate it. */
|
||||
activate_shelf(shelf_regiondata, *shelf);
|
||||
return shelf;
|
||||
|
@ -168,11 +180,14 @@ static AssetShelf *update_active_shelf(const bContext &C,
|
|||
|
||||
/* Case 3: */
|
||||
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
|
||||
if (asset_shelf_type_poll(C, space_type, shelf_type.get())) {
|
||||
if (type_poll(C, space_type, shelf_type.get())) {
|
||||
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
|
||||
BLI_addhead(&shelf_regiondata.shelves, new_shelf);
|
||||
/* Moves ownership to the regiondata. */
|
||||
activate_shelf(shelf_regiondata, *new_shelf);
|
||||
if (on_create) {
|
||||
on_create(*new_shelf);
|
||||
}
|
||||
return new_shelf;
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +231,7 @@ static bool asset_shelf_space_poll(const bContext *C, const SpaceLink *space_lin
|
|||
|
||||
/* Is there any asset shelf type registered that returns true for it's poll? */
|
||||
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type->asset_shelf_types) {
|
||||
if (asset_shelf_type_poll(*C, *space_type, shelf_type.get())) {
|
||||
if (type_poll(*C, *space_type, shelf_type.get())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -247,6 +262,9 @@ static void asset_shelf_region_listen(const wmRegionListenerParams *params)
|
|||
ED_region_tag_redraw(region);
|
||||
}
|
||||
break;
|
||||
case NC_ASSET:
|
||||
ED_region_tag_redraw(region);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,13 +281,13 @@ void region_listen(const wmRegionListenerParams *params)
|
|||
|
||||
void region_init(wmWindowManager *wm, ARegion *region)
|
||||
{
|
||||
if (!region->regiondata) {
|
||||
region->regiondata = MEM_cnew<RegionAssetShelf>("RegionAssetShelf");
|
||||
}
|
||||
RegionAssetShelf &shelf_regiondata = *RegionAssetShelf::get_from_asset_shelf_region(*region);
|
||||
/* Region-data should've been created by a previously called #region_before_redraw(). */
|
||||
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
|
||||
BLI_assert_msg(
|
||||
shelf_regiondata,
|
||||
"Region-data should've been created by a previously called `region_before_redraw()`.");
|
||||
|
||||
/* Active shelf is only set on draw, so this may be null! */
|
||||
AssetShelf *active_shelf = shelf_regiondata.active_shelf;
|
||||
AssetShelf *active_shelf = shelf_regiondata->active_shelf;
|
||||
|
||||
UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy);
|
||||
|
||||
|
@ -413,17 +431,12 @@ int region_prefsizey()
|
|||
|
||||
void region_layout(const bContext *C, ARegion *region)
|
||||
{
|
||||
const SpaceLink *space = CTX_wm_space_data(C);
|
||||
SpaceType *space_type = BKE_spacetype_from_id(space->spacetype);
|
||||
|
||||
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
|
||||
if (!shelf_regiondata) {
|
||||
/* Region-data should've been created by a previously called #region_init(). */
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
BLI_assert_msg(
|
||||
shelf_regiondata,
|
||||
"Region-data should've been created by a previously called `region_before_redraw()`.");
|
||||
|
||||
AssetShelf *active_shelf = update_active_shelf(*C, *space_type, *shelf_regiondata);
|
||||
const AssetShelf *active_shelf = shelf_regiondata->active_shelf;
|
||||
if (!active_shelf) {
|
||||
return;
|
||||
}
|
||||
|
@ -475,6 +488,28 @@ void region_draw(const bContext *C, ARegion *region)
|
|||
UI_view2d_scrollers_draw(®ion->v2d, nullptr);
|
||||
}
|
||||
|
||||
void region_on_poll_success(const bContext *C, ARegion *region)
|
||||
{
|
||||
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::ensure_from_asset_shelf_region(*region);
|
||||
if (!shelf_regiondata) {
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
update_active_shelf(
|
||||
*C, *area->type, *shelf_regiondata, /*on_create=*/[&](AssetShelf &new_shelf) {
|
||||
/* Update region visibility (`'DEFAULT_VISIBLE'` option). */
|
||||
const int old_flag = region->flag;
|
||||
SET_FLAG_FROM_TEST(region->flag,
|
||||
(new_shelf.type->flag & ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE) == 0,
|
||||
RGN_FLAG_HIDDEN);
|
||||
if (old_flag != region->flag) {
|
||||
ED_region_visibility_change_update(const_cast<bContext *>(C), area, region);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void header_region_listen(const wmRegionListenerParams *params)
|
||||
{
|
||||
asset_shelf_region_listen(params);
|
||||
|
@ -489,15 +524,6 @@ void header_region_init(wmWindowManager * /*wm*/, ARegion *region)
|
|||
|
||||
void header_region(const bContext *C, ARegion *region)
|
||||
{
|
||||
const SpaceLink *space = CTX_wm_space_data(C);
|
||||
SpaceType *space_type = BKE_spacetype_from_id(space->spacetype);
|
||||
const ARegion *main_shelf_region = BKE_area_find_region_type(CTX_wm_area(C),
|
||||
RGN_TYPE_ASSET_SHELF);
|
||||
|
||||
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
|
||||
*main_shelf_region);
|
||||
update_active_shelf(*C, *space_type, *shelf_regiondata);
|
||||
|
||||
ED_region_header_with_button_sections(C, region, uiButtonSectionsAlign::Bottom);
|
||||
}
|
||||
|
||||
|
@ -536,7 +562,7 @@ void region_blend_write(BlendWriter *writer, ARegion *region)
|
|||
/** \name Asset Shelf Context
|
||||
* \{ */
|
||||
|
||||
static AssetShelf *active_shelf_from_area(const ScrArea *area)
|
||||
AssetShelf *active_shelf_from_area(const ScrArea *area)
|
||||
{
|
||||
const ARegion *shelf_region = BKE_area_find_region_type(area, RGN_TYPE_ASSET_SHELF);
|
||||
if (!shelf_region) {
|
||||
|
@ -601,7 +627,7 @@ int context(const bContext *C, const char *member, bContextDataResult *result)
|
|||
/* XXX hack. Get the asset from the active item, but needs to be the file... */
|
||||
if (CTX_data_equals(member, "active_file")) {
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
const uiBut *but = UI_region_views_find_active_item_but(region);
|
||||
const uiBut *but = UI_region_views_find_mouse_over_but(CTX_wm_window(C), region);
|
||||
if (!but) {
|
||||
return CTX_RESULT_NO_DATA;
|
||||
}
|
||||
|
@ -668,9 +694,10 @@ static uiBut *add_tab_button(uiBlock &block, StringRefNull name)
|
|||
return but;
|
||||
}
|
||||
|
||||
static void add_catalog_tabs(AssetShelfSettings &shelf_settings, uiLayout &layout)
|
||||
static void add_catalog_tabs(AssetShelf &shelf, uiLayout &layout)
|
||||
{
|
||||
uiBlock *block = uiLayoutGetBlock(&layout);
|
||||
AssetShelfSettings &shelf_settings = shelf.settings;
|
||||
|
||||
/* "All" tab. */
|
||||
{
|
||||
|
@ -687,18 +714,17 @@ static void add_catalog_tabs(AssetShelfSettings &shelf_settings, uiLayout &layou
|
|||
uiItemS(&layout);
|
||||
|
||||
/* Regular catalog tabs. */
|
||||
settings_foreach_enabled_catalog_path(
|
||||
shelf_settings, [&](const asset_system::AssetCatalogPath &path) {
|
||||
uiBut *but = add_tab_button(*block, path.name());
|
||||
settings_foreach_enabled_catalog_path(shelf, [&](const asset_system::AssetCatalogPath &path) {
|
||||
uiBut *but = add_tab_button(*block, path.name());
|
||||
|
||||
UI_but_func_set(but, [&shelf_settings, path](bContext &C) {
|
||||
settings_set_active_catalog(shelf_settings, path);
|
||||
send_redraw_notifier(C);
|
||||
});
|
||||
UI_but_func_pushed_state_set(but, [&shelf_settings, path](const uiBut &) -> bool {
|
||||
return settings_is_active_catalog(shelf_settings, path);
|
||||
});
|
||||
});
|
||||
UI_but_func_set(but, [&shelf_settings, path](bContext &C) {
|
||||
settings_set_active_catalog(shelf_settings, path);
|
||||
send_redraw_notifier(C);
|
||||
});
|
||||
UI_but_func_pushed_state_set(but, [&shelf_settings, path](const uiBut &) -> bool {
|
||||
return settings_is_active_catalog(shelf_settings, path);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -724,9 +750,8 @@ static void asset_shelf_header_draw(const bContext *C, Header *header)
|
|||
uiItemS(layout);
|
||||
|
||||
PointerRNA shelf_ptr = active_shelf_ptr_from_context(C);
|
||||
AssetShelf *shelf = static_cast<AssetShelf *>(shelf_ptr.data);
|
||||
if (shelf) {
|
||||
add_catalog_tabs(shelf->settings, *layout);
|
||||
if (AssetShelf *shelf = static_cast<AssetShelf *>(shelf_ptr.data)) {
|
||||
add_catalog_tabs(*shelf, *layout);
|
||||
}
|
||||
|
||||
uiItemSpacer(layout);
|
||||
|
@ -788,6 +813,8 @@ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type_popup_unlink(shelf_type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -9,14 +9,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
struct ARegion;
|
||||
struct ARegionType;
|
||||
struct AssetLibraryReference;
|
||||
struct AssetShelf;
|
||||
struct AssetShelfType;
|
||||
struct AssetShelfSettings;
|
||||
struct bContext;
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
struct RegionAssetShelf;
|
||||
struct SpaceType;
|
||||
struct uiLayout;
|
||||
|
||||
namespace blender::asset_system {
|
||||
|
@ -39,6 +44,11 @@ AssetShelf *active_shelf_from_context(const bContext *C);
|
|||
|
||||
void send_redraw_notifier(const bContext &C);
|
||||
|
||||
AssetShelfType *type_ensure(const SpaceType &space_type, AssetShelf &shelf);
|
||||
AssetShelf *create_shelf_from_type(AssetShelfType &type);
|
||||
|
||||
void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf);
|
||||
|
||||
/**
|
||||
* Deep-copies \a shelf_regiondata into newly allocated memory. Must be freed using
|
||||
* #regiondata_free().
|
||||
|
@ -52,20 +62,24 @@ void regiondata_blend_read_data(BlendDataReader *reader, RegionAssetShelf **shel
|
|||
void settings_blend_write(BlendWriter *writer, const AssetShelfSettings &settings);
|
||||
void settings_blend_read_data(BlendDataReader *reader, AssetShelfSettings &settings);
|
||||
|
||||
void settings_clear_enabled_catalogs(AssetShelfSettings &settings);
|
||||
void settings_set_active_catalog(AssetShelfSettings &settings,
|
||||
const asset_system::AssetCatalogPath &path);
|
||||
void settings_set_all_catalog_active(AssetShelfSettings &settings);
|
||||
bool settings_is_active_catalog(const AssetShelfSettings &settings,
|
||||
const asset_system::AssetCatalogPath &path);
|
||||
bool settings_is_all_catalog_active(const AssetShelfSettings &settings);
|
||||
bool settings_is_catalog_path_enabled(const AssetShelfSettings &settings,
|
||||
/**
|
||||
* Clears the list of enabled catalogs in either the Preferences (if any) or the asset shelf
|
||||
* settings (if any), depending on the #ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS flag.
|
||||
*/
|
||||
void settings_clear_enabled_catalogs(AssetShelf &shelf);
|
||||
bool settings_is_catalog_path_enabled(const AssetShelf &shelf,
|
||||
const asset_system::AssetCatalogPath &path);
|
||||
void settings_set_catalog_path_enabled(AssetShelfSettings &settings,
|
||||
void settings_set_catalog_path_enabled(AssetShelf &shelf,
|
||||
const asset_system::AssetCatalogPath &path);
|
||||
|
||||
void settings_foreach_enabled_catalog_path(
|
||||
const AssetShelfSettings &settings,
|
||||
const AssetShelf &shelf,
|
||||
FunctionRef<void(const asset_system::AssetCatalogPath &catalog_path)> fn);
|
||||
|
||||
} // namespace blender::ed::asset::shelf
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "AS_asset_library.hh"
|
||||
#include "AS_asset_representation.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLI_fnmatch.h"
|
||||
|
@ -21,6 +22,7 @@
|
|||
|
||||
#include "ED_asset_handle.hh"
|
||||
#include "ED_asset_list.hh"
|
||||
#include "ED_asset_menu_utils.hh"
|
||||
#include "ED_asset_shelf.hh"
|
||||
|
||||
#include "UI_grid_view.hh"
|
||||
|
@ -38,16 +40,19 @@ namespace blender::ed::asset::shelf {
|
|||
class AssetView : public ui::AbstractGridView {
|
||||
const AssetLibraryReference library_ref_;
|
||||
const AssetShelf &shelf_;
|
||||
std::optional<AssetWeakReference> active_asset_;
|
||||
/** Copy of the filter string from #AssetShelfSettings, with extra '*' added to the beginning and
|
||||
* end of the string, for `fnmatch()` to work. */
|
||||
char search_string[sizeof(AssetShelfSettings::search_string) + 2] = "";
|
||||
std::optional<asset_system::AssetCatalogFilter> catalog_filter_ = std::nullopt;
|
||||
bool is_popup_ = false;
|
||||
|
||||
friend class AssetViewItem;
|
||||
friend class AssetDragController;
|
||||
|
||||
public:
|
||||
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf);
|
||||
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf, bool is_popup);
|
||||
~AssetView();
|
||||
|
||||
void build_items() override;
|
||||
bool begin_filtering(const bContext &C) const override;
|
||||
|
@ -68,6 +73,8 @@ class AssetViewItem : public ui::PreviewGridItem {
|
|||
void disable_asset_drag();
|
||||
void build_grid_tile(uiLayout &layout) const override;
|
||||
void build_context_menu(bContext &C, uiLayout &column) const override;
|
||||
void on_activate(bContext &C) override;
|
||||
std::optional<bool> should_be_active() const override;
|
||||
bool is_filtered_visible() const override;
|
||||
|
||||
std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
|
||||
|
@ -83,15 +90,27 @@ class AssetDragController : public ui::AbstractViewItemDragController {
|
|||
void *create_drag_data() const override;
|
||||
};
|
||||
|
||||
AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf)
|
||||
: library_ref_(library_ref), shelf_(shelf)
|
||||
AssetView::AssetView(const AssetLibraryReference &library_ref,
|
||||
const AssetShelf &shelf,
|
||||
const bool is_popup)
|
||||
: library_ref_(library_ref), shelf_(shelf), is_popup_(is_popup)
|
||||
{
|
||||
if (shelf.settings.search_string[0]) {
|
||||
BLI_strncpy_ensure_pad(
|
||||
search_string, shelf.settings.search_string, '*', sizeof(search_string));
|
||||
}
|
||||
if (shelf.type->get_active_asset) {
|
||||
if (const AssetWeakReference *weak_ref = shelf.type->get_active_asset(shelf.type)) {
|
||||
active_asset_ = *weak_ref;
|
||||
}
|
||||
else {
|
||||
active_asset_.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssetView::~AssetView() {}
|
||||
|
||||
void AssetView::build_items()
|
||||
{
|
||||
const asset_system::AssetLibrary *library = list::library_get_once_available(library_ref_);
|
||||
|
@ -192,16 +211,17 @@ void AssetViewItem::disable_asset_drag()
|
|||
|
||||
void AssetViewItem::build_grid_tile(uiLayout &layout) const
|
||||
{
|
||||
PointerRNA file_ptr = RNA_pointer_create(
|
||||
nullptr,
|
||||
&RNA_FileSelectEntry,
|
||||
/* XXX passing file pointer here, should be asset handle or asset representation. */
|
||||
const_cast<FileDirEntry *>(asset_.file_data));
|
||||
const AssetView &asset_view = reinterpret_cast<const AssetView &>(this->get_view());
|
||||
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
|
||||
|
||||
uiBlock *block = uiLayoutGetBlock(&layout);
|
||||
UI_but_context_ptr_set(
|
||||
block, reinterpret_cast<uiBut *>(view_item_but_), "active_file", &file_ptr);
|
||||
ui::PreviewGridItem::build_grid_tile(layout);
|
||||
wmOperatorType *ot = WM_operatortype_find(shelf_type.activate_operator.c_str(), true);
|
||||
PointerRNA op_props = PointerRNA_NULL;
|
||||
if (ot) {
|
||||
WM_operator_properties_create_ptr(&op_props, ot);
|
||||
asset::operator_asset_reference_props_set(*handle_get_representation(&asset_), op_props);
|
||||
}
|
||||
|
||||
ui::PreviewGridItem::build_grid_tile_button(layout, ot, &op_props);
|
||||
}
|
||||
|
||||
void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
|
||||
|
@ -214,6 +234,27 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
|
|||
}
|
||||
}
|
||||
|
||||
void AssetViewItem::on_activate(bContext & /*C*/)
|
||||
{
|
||||
const AssetView &asset_view = dynamic_cast<const AssetView &>(get_view());
|
||||
if (asset_view.is_popup_) {
|
||||
UI_popup_menu_close_from_but(reinterpret_cast<uiBut *>(view_item_button()));
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<bool> AssetViewItem::should_be_active() const
|
||||
{
|
||||
const AssetView &asset_view = dynamic_cast<const AssetView &>(get_view());
|
||||
if (!asset_view.active_asset_) {
|
||||
return false;
|
||||
}
|
||||
const asset_system::AssetRepresentation *asset = handle_get_representation(&asset_);
|
||||
AssetWeakReference weak_ref = asset->make_weak_reference();
|
||||
const bool matches = *asset_view.active_asset_ == weak_ref;
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
bool AssetViewItem::is_filtered_visible() const
|
||||
{
|
||||
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
|
||||
|
@ -255,7 +296,8 @@ void build_asset_view(uiLayout &layout,
|
|||
BLI_assert(tile_width != 0);
|
||||
BLI_assert(tile_height != 0);
|
||||
|
||||
std::unique_ptr asset_view = std::make_unique<AssetView>(library_ref, shelf);
|
||||
const bool is_popup = region.regiontype == RGN_TYPE_TEMPORARY;
|
||||
std::unique_ptr asset_view = std::make_unique<AssetView>(library_ref, shelf, is_popup);
|
||||
asset_view->set_catalog_filter(catalog_filter_from_shelf_settings(shelf.settings, *library));
|
||||
asset_view->set_tile_size(tile_width, tile_height);
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView {
|
|||
Item &build_catalog_items_recursive(ui::TreeViewOrItem &parent_view_item,
|
||||
const asset_system::AssetCatalogTreeItem &catalog_item) const
|
||||
{
|
||||
Item &view_item = parent_view_item.add_tree_item<Item>(catalog_item, shelf_settings_);
|
||||
Item &view_item = parent_view_item.add_tree_item<Item>(catalog_item, shelf_);
|
||||
|
||||
catalog_item.foreach_child(
|
||||
[&view_item, this](const asset_system::AssetCatalogTreeItem &child) {
|
||||
|
@ -92,12 +92,11 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView {
|
|||
char catalog_path_enabled_ = false;
|
||||
|
||||
public:
|
||||
Item(const asset_system::AssetCatalogTreeItem &catalog_item,
|
||||
AssetShelfSettings &shelf_settings)
|
||||
Item(const asset_system::AssetCatalogTreeItem &catalog_item, AssetShelf &shelf)
|
||||
: ui::BasicTreeViewItem(catalog_item.get_name()),
|
||||
catalog_item_(catalog_item),
|
||||
catalog_path_enabled_(
|
||||
settings_is_catalog_path_enabled(shelf_settings, catalog_item.catalog_path()))
|
||||
settings_is_catalog_path_enabled(shelf, catalog_item.catalog_path()))
|
||||
{
|
||||
disable_activatable();
|
||||
}
|
||||
|
@ -166,41 +165,46 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView {
|
|||
|
||||
void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs()
|
||||
{
|
||||
settings_clear_enabled_catalogs(shelf_settings_);
|
||||
settings_clear_enabled_catalogs(shelf_);
|
||||
foreach_item([this](ui::AbstractTreeViewItem &view_item) {
|
||||
const auto &selector_tree_item = dynamic_cast<AssetCatalogSelectorTree::Item &>(view_item);
|
||||
if (selector_tree_item.is_catalog_path_enabled()) {
|
||||
settings_set_catalog_path_enabled(shelf_settings_, selector_tree_item.catalog_path());
|
||||
settings_set_catalog_path_enabled(shelf_, selector_tree_item.catalog_path());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf)
|
||||
{
|
||||
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
|
||||
|
||||
PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, &shelf);
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE);
|
||||
if (shelf.settings.asset_library_reference.type != ASSET_LIBRARY_LOCAL) {
|
||||
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh");
|
||||
}
|
||||
}
|
||||
|
||||
static void catalog_selector_panel_draw(const bContext *C, Panel *panel)
|
||||
{
|
||||
const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
|
||||
AssetShelf *shelf = active_shelf_from_context(C);
|
||||
if (!shelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiLayout *layout = panel->layout;
|
||||
uiBlock *block = uiLayoutGetBlock(layout);
|
||||
|
||||
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
|
||||
library_selector_draw(C, layout, *shelf);
|
||||
|
||||
PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, shelf);
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE);
|
||||
if (library_ref->type != ASSET_LIBRARY_LOCAL) {
|
||||
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh");
|
||||
}
|
||||
|
||||
asset_system::AssetLibrary *library = list::library_get_once_available(*library_ref);
|
||||
asset_system::AssetLibrary *library = list::library_get_once_available(
|
||||
shelf->settings.asset_library_reference);
|
||||
if (!library) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiBlock *block = uiLayoutGetBlock(layout);
|
||||
ui::AbstractTreeView *tree_view = UI_block_add_view(
|
||||
*block,
|
||||
"asset catalog tree view",
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edasset
|
||||
*/
|
||||
|
||||
#include "asset_shelf.hh"
|
||||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
#include "UI_tree_view.hh"
|
||||
|
||||
#include "ED_asset_filter.hh"
|
||||
#include "ED_asset_list.hh"
|
||||
#include "ED_asset_shelf.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
namespace blender::ed::asset::shelf {
|
||||
|
||||
class StaticPopupShelves {
|
||||
public:
|
||||
Vector<AssetShelf *> popup_shelves;
|
||||
|
||||
~StaticPopupShelves()
|
||||
{
|
||||
for (AssetShelf *shelf : popup_shelves) {
|
||||
MEM_delete(shelf);
|
||||
}
|
||||
}
|
||||
|
||||
static Vector<AssetShelf *> &shelves()
|
||||
{
|
||||
static StaticPopupShelves storage;
|
||||
return storage.popup_shelves;
|
||||
}
|
||||
};
|
||||
|
||||
void type_popup_unlink(const AssetShelfType &shelf_type)
|
||||
{
|
||||
for (AssetShelf *shelf : StaticPopupShelves::shelves()) {
|
||||
if (shelf->type == &shelf_type) {
|
||||
shelf->type = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type)
|
||||
{
|
||||
const SpaceType *space_type = BKE_spacetype_from_id(shelf_type.space_type);
|
||||
|
||||
Vector<AssetShelf *> &popup_shelves = StaticPopupShelves::shelves();
|
||||
|
||||
for (AssetShelf *shelf : popup_shelves) {
|
||||
if (STREQ(shelf->idname, shelf_type.idname)) {
|
||||
if (type_poll(*C, *space_type, type_ensure(*space_type, *shelf))) {
|
||||
return shelf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_poll(*C, *space_type, &shelf_type)) {
|
||||
AssetShelf *new_shelf = create_shelf_from_type(shelf_type);
|
||||
popup_shelves.append(new_shelf);
|
||||
return new_shelf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class AssetCatalogTreeView : public ui::AbstractTreeView {
|
||||
AssetShelf &shelf_;
|
||||
asset_system::AssetCatalogTree catalog_tree_;
|
||||
|
||||
public:
|
||||
AssetCatalogTreeView(const asset_system::AssetLibrary &library, AssetShelf &shelf)
|
||||
: shelf_(shelf)
|
||||
{
|
||||
catalog_tree_ = build_filtered_catalog_tree(
|
||||
library,
|
||||
shelf_.settings.asset_library_reference,
|
||||
[this](const asset_system::AssetRepresentation &asset) {
|
||||
return (!shelf_.type->asset_poll || shelf_.type->asset_poll(shelf_.type, &asset));
|
||||
});
|
||||
}
|
||||
|
||||
void build_tree() override
|
||||
{
|
||||
if (catalog_tree_.is_empty()) {
|
||||
auto &item = this->add_tree_item<ui::BasicTreeViewItem>(RPT_("No applicable assets found"),
|
||||
ICON_INFO);
|
||||
item.disable_interaction();
|
||||
return;
|
||||
}
|
||||
|
||||
auto &all_item = this->add_tree_item<ui::BasicTreeViewItem>(IFACE_("All"));
|
||||
all_item.set_on_activate_fn([this](bContext &C, ui::BasicTreeViewItem &) {
|
||||
settings_set_all_catalog_active(shelf_.settings);
|
||||
send_redraw_notifier(C);
|
||||
});
|
||||
all_item.set_is_active_fn(
|
||||
[this]() { return settings_is_all_catalog_active(shelf_.settings); });
|
||||
all_item.uncollapse_by_default();
|
||||
|
||||
catalog_tree_.foreach_root_item([&, this](
|
||||
const asset_system::AssetCatalogTreeItem &catalog_item) {
|
||||
ui::BasicTreeViewItem &item = this->build_catalog_items_recursive(all_item, catalog_item);
|
||||
item.uncollapse_by_default();
|
||||
});
|
||||
}
|
||||
|
||||
ui::BasicTreeViewItem &build_catalog_items_recursive(
|
||||
ui::TreeViewOrItem &parent_view_item,
|
||||
const asset_system::AssetCatalogTreeItem &catalog_item) const
|
||||
{
|
||||
ui::BasicTreeViewItem &view_item = parent_view_item.add_tree_item<ui::BasicTreeViewItem>(
|
||||
catalog_item.get_name());
|
||||
|
||||
std::string catalog_path = catalog_item.catalog_path().str();
|
||||
view_item.set_on_activate_fn([this, catalog_path](bContext &C, ui::BasicTreeViewItem &) {
|
||||
settings_set_active_catalog(shelf_.settings, catalog_path);
|
||||
send_redraw_notifier(C);
|
||||
});
|
||||
view_item.set_is_active_fn([this, catalog_path]() {
|
||||
return settings_is_active_catalog(shelf_.settings, catalog_path);
|
||||
});
|
||||
|
||||
catalog_item.foreach_child(
|
||||
[&view_item, this](const asset_system::AssetCatalogTreeItem &child) {
|
||||
build_catalog_items_recursive(view_item, child);
|
||||
});
|
||||
|
||||
return view_item;
|
||||
}
|
||||
};
|
||||
|
||||
static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf)
|
||||
{
|
||||
const asset_system::AssetLibrary *library = list::library_get_once_available(
|
||||
shelf.settings.asset_library_reference);
|
||||
if (!library) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiBlock *block = uiLayoutGetBlock(&layout);
|
||||
ui::AbstractTreeView *tree_view = UI_block_add_view(
|
||||
*block,
|
||||
"asset shelf catalog tree view",
|
||||
std::make_unique<AssetCatalogTreeView>(*library, shelf));
|
||||
|
||||
ui::TreeViewBuilder::build_tree_view(*tree_view, layout);
|
||||
}
|
||||
|
||||
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type)
|
||||
{
|
||||
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
|
||||
UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER);
|
||||
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
|
||||
|
||||
AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type);
|
||||
if (!shelf) {
|
||||
BLI_assert_unreachable();
|
||||
return block;
|
||||
}
|
||||
|
||||
const uiStyle *style = UI_style_get_dpi();
|
||||
|
||||
const float pad = 0.2f * UI_UNIT_Y; /* UI_MENU_PADDING */
|
||||
uiLayout *layout = UI_block_layout(
|
||||
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, pad, 0, UI_UNIT_X * 40, 0, pad / 2, style);
|
||||
|
||||
PointerRNA library_ref_ptr = RNA_pointer_create(
|
||||
&CTX_wm_screen(C)->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference);
|
||||
uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr);
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, false);
|
||||
uiLayout *sub = uiLayoutRow(row, false);
|
||||
uiLayoutSetUnitsX(sub, 10);
|
||||
uiLayoutSetFixedSize(sub, true);
|
||||
uiLayout *catalogs_col = uiLayoutColumn(sub, false);
|
||||
library_selector_draw(C, catalogs_col, *shelf);
|
||||
catalog_tree_draw(*catalogs_col, *shelf);
|
||||
|
||||
uiLayout *asset_view_col = uiLayoutColumn(row, false);
|
||||
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::asset::shelf
|
|
@ -25,6 +25,19 @@ RegionAssetShelf *RegionAssetShelf::get_from_asset_shelf_region(const ARegion &r
|
|||
return static_cast<RegionAssetShelf *>(region.regiondata);
|
||||
}
|
||||
|
||||
RegionAssetShelf *RegionAssetShelf::ensure_from_asset_shelf_region(ARegion ®ion)
|
||||
{
|
||||
if (region.regiontype != RGN_TYPE_ASSET_SHELF) {
|
||||
/* Should only be called on main asset shelf region. */
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
if (!region.regiondata) {
|
||||
region.regiondata = MEM_cnew<RegionAssetShelf>("RegionAssetShelf");
|
||||
}
|
||||
return static_cast<RegionAssetShelf *>(region.regiondata);
|
||||
}
|
||||
|
||||
namespace blender::ed::asset::shelf {
|
||||
|
||||
RegionAssetShelf *regiondata_duplicate(const RegionAssetShelf *shelf_regiondata)
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "asset_shelf.hh"
|
||||
|
||||
using namespace blender;
|
||||
|
@ -45,18 +49,15 @@ AssetShelfSettings &AssetShelfSettings::operator=(const AssetShelfSettings &othe
|
|||
if (active_catalog_path) {
|
||||
active_catalog_path = BLI_strdup(other.active_catalog_path);
|
||||
}
|
||||
BLI_listbase_clear(&enabled_catalog_paths);
|
||||
BKE_asset_catalog_path_list_free(enabled_catalog_paths);
|
||||
enabled_catalog_paths = BKE_asset_catalog_path_list_duplicate(other.enabled_catalog_paths);
|
||||
|
||||
LISTBASE_FOREACH (LinkData *, catalog_path_item, &other.enabled_catalog_paths) {
|
||||
LinkData *new_path_item = BLI_genericNodeN(BLI_strdup((char *)catalog_path_item->data));
|
||||
BLI_addtail(&enabled_catalog_paths, new_path_item);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
AssetShelfSettings::~AssetShelfSettings()
|
||||
{
|
||||
shelf::settings_clear_enabled_catalogs(*this);
|
||||
BKE_asset_catalog_path_list_free(enabled_catalog_paths);
|
||||
MEM_delete(active_catalog_path);
|
||||
}
|
||||
|
||||
|
@ -66,32 +67,16 @@ void settings_blend_write(BlendWriter *writer, const AssetShelfSettings &setting
|
|||
{
|
||||
BLO_write_struct(writer, AssetShelfSettings, &settings);
|
||||
|
||||
LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) {
|
||||
BLO_write_struct(writer, LinkData, catalog_path_item);
|
||||
BLO_write_string(writer, (const char *)catalog_path_item->data);
|
||||
}
|
||||
|
||||
BKE_asset_catalog_path_list_blend_write(writer, settings.enabled_catalog_paths);
|
||||
BLO_write_string(writer, settings.active_catalog_path);
|
||||
}
|
||||
|
||||
void settings_blend_read_data(BlendDataReader *reader, AssetShelfSettings &settings)
|
||||
{
|
||||
BLO_read_struct_list(reader, LinkData, &settings.enabled_catalog_paths);
|
||||
LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) {
|
||||
BLO_read_string(reader, reinterpret_cast<char **>(&catalog_path_item->data));
|
||||
}
|
||||
BKE_asset_catalog_path_list_blend_read_data(reader, settings.enabled_catalog_paths);
|
||||
BLO_read_string(reader, &settings.active_catalog_path);
|
||||
}
|
||||
|
||||
void settings_clear_enabled_catalogs(AssetShelfSettings &settings)
|
||||
{
|
||||
LISTBASE_FOREACH_MUTABLE (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) {
|
||||
MEM_freeN(catalog_path_item->data);
|
||||
BLI_freelinkN(&settings.enabled_catalog_paths, catalog_path_item);
|
||||
}
|
||||
BLI_assert(BLI_listbase_is_empty(&settings.enabled_catalog_paths));
|
||||
}
|
||||
|
||||
void settings_set_active_catalog(AssetShelfSettings &settings,
|
||||
const asset_system::AssetCatalogPath &path)
|
||||
{
|
||||
|
@ -116,30 +101,76 @@ bool settings_is_all_catalog_active(const AssetShelfSettings &settings)
|
|||
return !settings.active_catalog_path || !settings.active_catalog_path[0];
|
||||
}
|
||||
|
||||
bool settings_is_catalog_path_enabled(const AssetShelfSettings &settings,
|
||||
const asset_system::AssetCatalogPath &path)
|
||||
static bool use_enabled_catalogs_from_prefs(const AssetShelf &shelf)
|
||||
{
|
||||
LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) {
|
||||
if (StringRef((const char *)catalog_path_item->data) == path.str()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return shelf.type && (shelf.type->flag & ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS);
|
||||
}
|
||||
|
||||
void settings_set_catalog_path_enabled(AssetShelfSettings &settings,
|
||||
static const ListBase *get_enabled_catalog_path_list(const AssetShelf &shelf)
|
||||
{
|
||||
if (use_enabled_catalogs_from_prefs(shelf)) {
|
||||
bUserAssetShelfSettings *pref_settings = BKE_preferences_asset_shelf_settings_get(
|
||||
&U, shelf.idname);
|
||||
return pref_settings ? &pref_settings->enabled_catalog_paths : nullptr;
|
||||
}
|
||||
return &shelf.settings.enabled_catalog_paths;
|
||||
}
|
||||
|
||||
static ListBase *get_enabled_catalog_path_list(AssetShelf &shelf)
|
||||
{
|
||||
return const_cast<ListBase *>(
|
||||
get_enabled_catalog_path_list(const_cast<const AssetShelf &>(shelf)));
|
||||
}
|
||||
|
||||
void settings_clear_enabled_catalogs(AssetShelf &shelf)
|
||||
{
|
||||
ListBase *enabled_catalog_paths = get_enabled_catalog_path_list(shelf);
|
||||
if (enabled_catalog_paths) {
|
||||
BKE_asset_catalog_path_list_free(*enabled_catalog_paths);
|
||||
BLI_assert(BLI_listbase_is_empty(enabled_catalog_paths));
|
||||
}
|
||||
}
|
||||
|
||||
bool settings_is_catalog_path_enabled(const AssetShelf &shelf,
|
||||
const asset_system::AssetCatalogPath &path)
|
||||
{
|
||||
const ListBase *enabled_catalog_paths = get_enabled_catalog_path_list(shelf);
|
||||
if (!enabled_catalog_paths) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return BKE_asset_catalog_path_list_has_path(*enabled_catalog_paths, path.c_str());
|
||||
}
|
||||
|
||||
void settings_set_catalog_path_enabled(AssetShelf &shelf,
|
||||
const asset_system::AssetCatalogPath &path)
|
||||
{
|
||||
char *path_copy = BLI_strdupn(path.c_str(), path.length());
|
||||
BLI_addtail(&settings.enabled_catalog_paths, BLI_genericNodeN(path_copy));
|
||||
if (use_enabled_catalogs_from_prefs(shelf)) {
|
||||
if (BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
&U, shelf.idname, path.c_str()))
|
||||
{
|
||||
U.runtime.is_dirty = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!BKE_asset_catalog_path_list_has_path(shelf.settings.enabled_catalog_paths, path.c_str()))
|
||||
{
|
||||
BKE_asset_catalog_path_list_add_path(shelf.settings.enabled_catalog_paths, path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void settings_foreach_enabled_catalog_path(
|
||||
const AssetShelfSettings &settings,
|
||||
const AssetShelf &shelf,
|
||||
FunctionRef<void(const asset_system::AssetCatalogPath &catalog_path)> fn)
|
||||
{
|
||||
LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) {
|
||||
fn(asset_system::AssetCatalogPath((char *)catalog_path_item->data));
|
||||
const ListBase *enabled_catalog_paths = get_enabled_catalog_path_list(shelf);
|
||||
if (!enabled_catalog_paths) {
|
||||
return;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const AssetCatalogPathLink *, path_link, enabled_catalog_paths) {
|
||||
fn(asset_system::AssetCatalogPath(path_link->path));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ bool id_type_is_non_experimental(const ID *id)
|
|||
{
|
||||
/* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and
|
||||
* #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */
|
||||
return ELEM(GS(id->name), ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT);
|
||||
return ELEM(GS(id->name), ID_BR, ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT);
|
||||
}
|
||||
|
||||
bool id_type_is_supported(const ID *id)
|
||||
|
|
|
@ -2200,7 +2200,7 @@ static int gpencil_brush_reset_all_exec(bContext *C, wmOperator * /*op*/)
|
|||
}
|
||||
}
|
||||
|
||||
BKE_paint_toolslots_brush_validate(bmain, paint);
|
||||
BKE_paint_brush_validate(bmain, paint);
|
||||
|
||||
/* Set Again the first brush of the mode. */
|
||||
Brush *deft_brush = gpencil_brush_get_first_by_mode(bmain, paint, mode, tool);
|
||||
|
|
|
@ -420,8 +420,8 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
if (mode == OB_MODE_PAINT_GPENCIL_LEGACY) {
|
||||
/* Be sure we have brushes and Paint settings.
|
||||
* Need Draw and Vertex (used for Tint). */
|
||||
BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint);
|
||||
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, false);
|
||||
|
||||
|
@ -434,7 +434,7 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, true);
|
||||
}
|
||||
BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint);
|
||||
BKE_paint_brush_validate(bmain, &ts->gp_paint->paint);
|
||||
}
|
||||
|
||||
if (ob->type == OB_GPENCIL_LEGACY) {
|
||||
|
@ -572,12 +572,12 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
|
||||
if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) {
|
||||
/* Be sure we have brushes. */
|
||||
BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint);
|
||||
|
||||
const bool reset_mode = (BKE_paint_brush(&ts->gp_sculptpaint->paint) == nullptr);
|
||||
BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode);
|
||||
|
||||
BKE_paint_toolslots_brush_validate(bmain, &ts->gp_sculptpaint->paint);
|
||||
BKE_paint_brush_validate(bmain, &ts->gp_sculptpaint->paint);
|
||||
}
|
||||
|
||||
/* setup other modes */
|
||||
|
@ -711,7 +711,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
if (mode == OB_MODE_WEIGHT_GPENCIL_LEGACY) {
|
||||
/* Be sure we have brushes. */
|
||||
Paint *weight_paint = BKE_paint_get_active_from_paintmode(scene, PaintMode::WeightGPencil);
|
||||
BKE_paint_ensure(ts, &weight_paint);
|
||||
BKE_paint_ensure(bmain, ts, &weight_paint);
|
||||
|
||||
if (ob->type == OB_GREASE_PENCIL) {
|
||||
ED_paint_cursor_start(weight_paint, grease_pencil_poll_weight_cursor);
|
||||
|
@ -720,7 +720,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
const bool reset_mode = (BKE_paint_brush(weight_paint) == nullptr);
|
||||
BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode);
|
||||
|
||||
BKE_paint_toolslots_brush_validate(bmain, weight_paint);
|
||||
BKE_paint_brush_validate(bmain, weight_paint);
|
||||
}
|
||||
|
||||
/* setup other modes */
|
||||
|
@ -829,13 +829,13 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
if (mode == OB_MODE_VERTEX_GPENCIL_LEGACY) {
|
||||
/* Be sure we have brushes.
|
||||
* Need Draw as well (used for Palettes). */
|
||||
BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint);
|
||||
|
||||
const bool reset_mode = (BKE_paint_brush(&ts->gp_vertexpaint->paint) == nullptr);
|
||||
BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode);
|
||||
|
||||
BKE_paint_toolslots_brush_validate(bmain, &ts->gp_vertexpaint->paint);
|
||||
BKE_paint_brush_validate(bmain, &ts->gp_vertexpaint->paint);
|
||||
|
||||
/* Ensure Palette by default. */
|
||||
BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C));
|
||||
|
|
|
@ -1216,12 +1216,8 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
|
|||
BKE_brush_gpencil_paint_presets(bmain, ts, true);
|
||||
}
|
||||
|
||||
/* Set Draw brush. */
|
||||
brush = BKE_paint_toolslots_brush_get(paint, 0);
|
||||
|
||||
BKE_brush_tool_set(brush, paint, 0);
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
tgpi->brush = brush;
|
||||
/* Set brush. */
|
||||
tgpi->brush = BKE_paint_brush(paint);
|
||||
|
||||
/* control points */
|
||||
tgpi->gpd->runtime.cp_points = static_cast<bGPDcontrolpoint *>(
|
||||
|
|
|
@ -1428,7 +1428,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob)
|
|||
Main *bmain = CTX_data_main(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
|
||||
BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
/* if not exist, create a new one */
|
||||
|
|
|
@ -44,6 +44,9 @@ void operator_asset_reference_props_set(const asset_system::AssetRepresentation
|
|||
PointerRNA &ptr);
|
||||
void operator_asset_reference_props_register(StructRNA &srna);
|
||||
|
||||
const asset_system::AssetRepresentation *find_asset_from_weak_ref(
|
||||
const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports);
|
||||
|
||||
/**
|
||||
* Load all asset libraries to find an asset from the #operator_asset_reference_props_register
|
||||
* properties. The loading happens in the background, so there may be no result immediately. In
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "UI_resources.hh"
|
||||
|
||||
struct bContext;
|
||||
struct PointerRNA;
|
||||
struct uiBlock;
|
||||
struct uiButViewItem;
|
||||
struct uiLayout;
|
||||
|
@ -204,6 +205,13 @@ class PreviewGridItem : public AbstractGridViewItem {
|
|||
|
||||
void build_grid_tile(uiLayout &layout) const override;
|
||||
|
||||
/**
|
||||
* \note: Takes ownership of the operator properies defined in \a op_props.
|
||||
*/
|
||||
void build_grid_tile_button(uiLayout &layout,
|
||||
const wmOperatorType *ot = nullptr,
|
||||
const PointerRNA *op_props = nullptr) const;
|
||||
|
||||
/**
|
||||
* Set a custom callback to execute when activating this view item. This way users don't have to
|
||||
* sub-class #PreviewGridItem, just to implement custom activation behavior (a common thing to
|
||||
|
|
|
@ -707,6 +707,18 @@ uiLayout *UI_popup_menu_layout(uiPopupMenu *pup);
|
|||
void UI_popup_menu_reports(bContext *C, ReportList *reports) ATTR_NONNULL();
|
||||
int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) ATTR_NONNULL(1, 2);
|
||||
|
||||
/**
|
||||
* If \a block is displayed in a popup menu, tag it for closing.
|
||||
* \param is_cancel: If set to true, the popup will be closed as being cancelled (e.g. when
|
||||
* pressing escape) as opposed to being handled successfully.
|
||||
*/
|
||||
void UI_popup_menu_close(const uiBlock *block, bool is_cancel = false);
|
||||
/**
|
||||
* Version of #UI_popup_menu_close() that can be called on a button contained in a popup menu
|
||||
* block. Convenience since the block may not be available.
|
||||
*/
|
||||
void UI_popup_menu_close_from_but(const uiBut *but, bool is_cancel = false);
|
||||
|
||||
/**
|
||||
* Allow setting menu return value from externals.
|
||||
* E.g. WM might need to do this for exiting files correctly.
|
||||
|
@ -1488,6 +1500,10 @@ uiBut *uiDefIconMenuBut(uiBlock *block,
|
|||
short height,
|
||||
const char *tip);
|
||||
|
||||
/**
|
||||
* Note that \a fun can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow
|
||||
* refreshing the popup. That is, redrawing the layout, potentially affecting the popup size.
|
||||
*/
|
||||
uiBut *uiDefBlockBut(uiBlock *block,
|
||||
uiBlockCreateFunc func,
|
||||
void *arg,
|
||||
|
@ -2703,6 +2719,9 @@ void uiTemplateAssetView(uiLayout *layout,
|
|||
const char *drag_opname,
|
||||
PointerRNA *r_drag_op_properties);
|
||||
|
||||
void uiTemplateAssetShelfPopover(
|
||||
uiLayout *layout, bContext *C, const char *asset_shelf_id, const char *name, const int icon);
|
||||
|
||||
void uiTemplateLightLinkingCollection(uiLayout *layout,
|
||||
uiLayout *context_layout,
|
||||
PointerRNA *ptr,
|
||||
|
@ -3394,4 +3413,4 @@ blender::ui::AbstractView *UI_region_view_find_at(const ARegion *region, const i
|
|||
blender::ui::AbstractViewItem *UI_region_views_find_item_at(const ARegion ®ion,
|
||||
const int xy[2]);
|
||||
blender::ui::AbstractViewItem *UI_region_views_find_active_item(const ARegion *region);
|
||||
uiBut *UI_region_views_find_active_item_but(const ARegion *region);
|
||||
uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
set(INC
|
||||
.
|
||||
../include
|
||||
../asset
|
||||
../../asset_system
|
||||
../../blenkernel
|
||||
../../blenloader
|
||||
|
@ -64,6 +65,7 @@ set(SRC
|
|||
regions/interface_regions.cc
|
||||
interface_string_search.cc
|
||||
interface_style.cc
|
||||
templates/interface_template_asset_shelf_popover.cc
|
||||
templates/interface_template_asset_view.cc
|
||||
templates/interface_template_attribute_search.cc
|
||||
templates/interface_template_bone_collection_tree.cc
|
||||
|
|
|
@ -415,6 +415,10 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
|
|||
STRNCPY(drawstr, idname);
|
||||
#endif
|
||||
}
|
||||
else if (but->tip_label_func) {
|
||||
/* The "quick tooltip" often contains a short string that can be used as a fallback. */
|
||||
drawstr = but->tip_label_func(but);
|
||||
}
|
||||
}
|
||||
ED_screen_user_menu_item_add_operator(
|
||||
&um->items,
|
||||
|
|
|
@ -2311,6 +2311,7 @@ static void ui_apply_but(
|
|||
switch (but_type) {
|
||||
case UI_BTYPE_BUT:
|
||||
case UI_BTYPE_DECORATOR:
|
||||
case UI_BTYPE_PREVIEW_TILE:
|
||||
ui_apply_but_BUT(C, but, data);
|
||||
break;
|
||||
case UI_BTYPE_TEXT:
|
||||
|
@ -11502,6 +11503,8 @@ static int ui_handle_menus_recursive(bContext *C,
|
|||
if (!menu->retvalue) {
|
||||
ui_handle_viewlist_items_hover(event, menu->region);
|
||||
}
|
||||
/* Handle mouse clicks on overlapping view item button. */
|
||||
ui_handle_view_item_event(C, event, but, menu->region);
|
||||
|
||||
if (do_towards_reinit) {
|
||||
ui_mouse_motion_towards_reinit(menu, event->xy);
|
||||
|
|
|
@ -977,6 +977,10 @@ uiBlock *ui_popup_block_refresh(bContext *C,
|
|||
ARegion *butregion,
|
||||
uiBut *but);
|
||||
|
||||
/**
|
||||
* Note that callbacks can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow
|
||||
* refreshing the popup. That is, redrawing the layout, potentially affecting the popup size.
|
||||
*/
|
||||
uiPopupBlockHandle *ui_popup_block_create(bContext *C,
|
||||
ARegion *butregion,
|
||||
uiBut *but,
|
||||
|
|
|
@ -504,7 +504,7 @@ void ui_rna_collection_search_update_fn(
|
|||
}
|
||||
|
||||
cis->index = items_list.size();
|
||||
cis->iconid = ICON_NONE;
|
||||
cis->iconid = visit_params.icon_id.value_or(ICON_NONE);
|
||||
cis->is_id = false;
|
||||
cis->name_prefix_offset = 0;
|
||||
cis->has_sep_char = visit_params.info.has_value();
|
||||
|
|
|
@ -762,4 +762,14 @@ bool UI_popup_block_name_exists(const bScreen *screen, const blender::StringRef
|
|||
return false;
|
||||
}
|
||||
|
||||
void UI_popup_menu_close(const uiBlock *block, const bool is_cancel)
|
||||
{
|
||||
UI_popup_menu_retval_set(block, is_cancel ? UI_RETURN_CANCEL : UI_RETURN_OK, true);
|
||||
}
|
||||
|
||||
void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel)
|
||||
{
|
||||
UI_popup_menu_close(but->block, is_cancel);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -885,6 +885,10 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C,
|
|||
uiBlock *block = ui_popup_block_refresh(C, handle, butregion, but);
|
||||
handle = block->handle;
|
||||
|
||||
if (block->flag & UI_BLOCK_KEEP_OPEN) {
|
||||
handle->can_refresh = true;
|
||||
}
|
||||
|
||||
/* keep centered on window resizing */
|
||||
if (block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) {
|
||||
type.listener = ui_block_region_popup_window_listener;
|
||||
|
|
|
@ -571,7 +571,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
|||
/* There are different kinds of shortcuts:
|
||||
*
|
||||
* - Direct access to the tool (as if the toolbar button is pressed).
|
||||
* - The key is bound to a brush type (not the exact brush name).
|
||||
* - The key is assigned to the operator itself
|
||||
* (bypassing the tool, executing the operator).
|
||||
*
|
||||
|
@ -579,36 +578,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
|||
*/
|
||||
std::string shortcut = UI_but_string_get_operator_keymap(*C, *but);
|
||||
|
||||
if (shortcut.empty()) {
|
||||
const PaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
|
||||
const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
|
||||
if (tool_attr != nullptr) {
|
||||
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
|
||||
const char *tool_id_lstrip = strrchr(tool_id, '.');
|
||||
const int tool_id_offset = tool_id_lstrip ? ((tool_id_lstrip - tool_id) + 1) : 0;
|
||||
const int i = RNA_enum_from_name(items, tool_id + tool_id_offset);
|
||||
|
||||
if (i != -1) {
|
||||
wmOperatorType *ot = WM_operatortype_find("paint.brush_select", true);
|
||||
PointerRNA op_props;
|
||||
WM_operator_properties_create_ptr(&op_props, ot);
|
||||
RNA_enum_set(&op_props, tool_attr, items[i].value);
|
||||
|
||||
/* Check for direct access to the tool. */
|
||||
if (std::optional<std::string> shortcut_brush = WM_key_event_operator_string(
|
||||
C,
|
||||
ot->idname,
|
||||
WM_OP_INVOKE_REGION_WIN,
|
||||
static_cast<IDProperty *>(op_props.data),
|
||||
true))
|
||||
{
|
||||
shortcut = *shortcut_brush;
|
||||
}
|
||||
WM_operator_properties_free(&op_props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shortcut.empty()) {
|
||||
/* Check for direct access to the tool. */
|
||||
if (std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edinterface
|
||||
*/
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
#include "UI_resources.hh"
|
||||
#include "interface_intern.hh"
|
||||
|
||||
#include "ED_asset_shelf.hh"
|
||||
|
||||
using namespace blender;
|
||||
|
||||
static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type)
|
||||
{
|
||||
AssetShelfType *shelf_type = reinterpret_cast<AssetShelfType *>(arg_shelf_type);
|
||||
return ed::asset::shelf::popup_block_create(C, region, shelf_type);
|
||||
}
|
||||
|
||||
void uiTemplateAssetShelfPopover(uiLayout *layout,
|
||||
bContext *C,
|
||||
const char *asset_shelf_id,
|
||||
const char *name,
|
||||
const BIFIconID icon)
|
||||
{
|
||||
const ScrArea *area = CTX_wm_area(C);
|
||||
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(*area->type,
|
||||
asset_shelf_id);
|
||||
if (!shelf_type) {
|
||||
RNA_warning("Asset shelf type not found: %s", asset_shelf_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype);
|
||||
const short width = [&]() -> short {
|
||||
if (use_big_size) {
|
||||
return UI_UNIT_X * 6;
|
||||
}
|
||||
return UI_UNIT_X * (name ? 7 : 1.6f);
|
||||
}();
|
||||
const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
|
||||
|
||||
uiBlock *block = uiLayoutGetBlock(layout);
|
||||
uiBut *but = uiDefBlockBut(
|
||||
block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset");
|
||||
ui_def_but_icon(but, icon, UI_HAS_ICON);
|
||||
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
|
||||
|
||||
if (ed::asset::shelf::type_poll(*C, *area->type, shelf_type) == false) {
|
||||
UI_but_flag_enable(but, UI_BUT_DISABLED);
|
||||
}
|
||||
}
|
|
@ -44,6 +44,8 @@
|
|||
#include "BLF_api.hh"
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_colorband.hh"
|
||||
|
@ -961,6 +963,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
|
|||
TemplateID *template_ui = (TemplateID *)arg_litem;
|
||||
PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
|
||||
ID *id = static_cast<ID *>(idptr.data);
|
||||
Main *id_main = BKE_main_from_id(CTX_data_main(C), id);
|
||||
const int event = POINTER_AS_INT(arg_event);
|
||||
const char *undo_push_label = nullptr;
|
||||
|
||||
|
@ -1011,12 +1014,12 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
|
|||
break;
|
||||
case UI_ID_LOCAL:
|
||||
if (id) {
|
||||
Main *bmain = CTX_data_main(C);
|
||||
if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
|
||||
template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
|
||||
template_id_liboverride_hierarchy_make(
|
||||
C, id_main, template_ui, &idptr, &undo_push_label);
|
||||
}
|
||||
else {
|
||||
if (BKE_lib_id_make_local(bmain, id, 0)) {
|
||||
if (BKE_lib_id_make_local(id_main, id, 0)) {
|
||||
BKE_id_newptr_and_tag_clear(id);
|
||||
|
||||
/* Reassign to get proper updates/notifiers. */
|
||||
|
@ -1032,12 +1035,12 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
|
|||
break;
|
||||
case UI_ID_OVERRIDE:
|
||||
if (id && ID_IS_OVERRIDE_LIBRARY(id)) {
|
||||
Main *bmain = CTX_data_main(C);
|
||||
if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
|
||||
template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
|
||||
template_id_liboverride_hierarchy_make(
|
||||
C, id_main, template_ui, &idptr, &undo_push_label);
|
||||
}
|
||||
else {
|
||||
BKE_lib_override_library_make_local(bmain, id);
|
||||
BKE_lib_override_library_make_local(id_main, id);
|
||||
/* Reassign to get proper updates/notifiers. */
|
||||
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
|
||||
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, nullptr);
|
||||
|
@ -1053,16 +1056,14 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
|
|||
|
||||
/* make copy */
|
||||
if (do_scene_obj) {
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
blender::ed::object::object_single_user_make(bmain, scene, (Object *)id);
|
||||
blender::ed::object::object_single_user_make(id_main, scene, (Object *)id);
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
DEG_relations_tag_update(bmain);
|
||||
DEG_relations_tag_update(id_main);
|
||||
}
|
||||
else {
|
||||
Main *bmain = CTX_data_main(C);
|
||||
id_single_user(C, id, &template_ui->ptr, template_ui->prop);
|
||||
DEG_relations_tag_update(bmain);
|
||||
DEG_relations_tag_update(id_main);
|
||||
}
|
||||
undo_push_label = "Make Single User";
|
||||
}
|
||||
|
@ -1766,10 +1767,15 @@ static void ui_template_id(uiLayout *layout,
|
|||
flag |= UI_ID_OPEN;
|
||||
}
|
||||
|
||||
Main *id_main = CTX_data_main(C);
|
||||
if (ptr->owner_id) {
|
||||
id_main = BKE_main_from_id(id_main, ptr->owner_id);
|
||||
}
|
||||
|
||||
StructRNA *type = RNA_property_pointer_type(ptr, prop);
|
||||
short idcode = RNA_type_to_ID_code(type);
|
||||
template_ui->idcode = idcode;
|
||||
template_ui->idlb = which_libbase(CTX_data_main(C), idcode);
|
||||
template_ui->idlb = which_libbase(id_main, idcode);
|
||||
|
||||
/* create UI elements for this template
|
||||
* - template_ID makes a copy of the template data and assigns it to the relevant buttons
|
||||
|
@ -6351,13 +6357,47 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C)
|
|||
}
|
||||
}
|
||||
|
||||
static void ui_template_status_info_warnings_messages(Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
std::string &warning_message,
|
||||
std::string ®ular_message,
|
||||
std::string &tooltip_message)
|
||||
{
|
||||
tooltip_message = "";
|
||||
char statusbar_info_flag = U.statusbar_flag;
|
||||
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
warning_message = ED_info_statusbar_string_ex(
|
||||
bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
|
||||
statusbar_info_flag &= ~STATUSBAR_SHOW_VERSION;
|
||||
|
||||
char writer_ver_str[12];
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
|
||||
tooltip_message += fmt::format(RPT_("File saved by newer Blender\n({}), expect loss of data"),
|
||||
writer_ver_str);
|
||||
}
|
||||
if (bmain->is_asset_repository) {
|
||||
if (!tooltip_message.empty()) {
|
||||
tooltip_message += "\n\n";
|
||||
}
|
||||
tooltip_message += RPT_(
|
||||
"This file is managed by the Blender asset system\n"
|
||||
"and is expected to contain a single asset data-block.\n"
|
||||
"Take care to avoid data loss when editing assets.");
|
||||
}
|
||||
|
||||
regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag);
|
||||
}
|
||||
|
||||
void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
if (!bmain->has_forward_compatibility_issues) {
|
||||
if (!BKE_main_has_issues(bmain)) {
|
||||
const char *status_info_txt = ED_info_statusbar_string(bmain, scene, view_layer);
|
||||
uiItemL(layout, status_info_txt, ICON_NONE);
|
||||
return;
|
||||
|
@ -6366,13 +6406,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
/* Blender version part is shown as warning area when there are forward compatibility issues with
|
||||
* currently loaded .blend file. */
|
||||
|
||||
const char *status_info_txt = ED_info_statusbar_string_ex(
|
||||
bmain, scene, view_layer, (U.statusbar_flag & ~STATUSBAR_SHOW_VERSION));
|
||||
uiItemL(layout, status_info_txt, ICON_NONE);
|
||||
std::string warning_message;
|
||||
std::string regular_message;
|
||||
std::string tooltip_message;
|
||||
ui_template_status_info_warnings_messages(
|
||||
bmain, scene, view_layer, warning_message, regular_message, tooltip_message);
|
||||
|
||||
status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
|
||||
|
||||
uiBut *but;
|
||||
uiItemL(layout, regular_message.c_str(), ICON_NONE);
|
||||
|
||||
const uiStyle *style = UI_style_get();
|
||||
uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
|
||||
|
@ -6380,25 +6420,26 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
eUIEmbossType previous_emboss = UI_block_emboss_get(block);
|
||||
|
||||
UI_fontstyle_set(&style->widgetlabel);
|
||||
int width = int(
|
||||
BLF_width(style->widgetlabel.uifont_id, status_info_txt, strlen(status_info_txt)));
|
||||
width = max_ii(width, int(10 * UI_SCALE_FAC));
|
||||
const int width = max_ii(int(BLF_width(style->widgetlabel.uifont_id,
|
||||
warning_message.c_str(),
|
||||
warning_message.length())),
|
||||
int(10 * UI_SCALE_FAC));
|
||||
|
||||
UI_block_align_begin(block);
|
||||
|
||||
/* Background for icon. */
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_ROUNDBOX,
|
||||
0,
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
UI_UNIT_X + (6 * UI_SCALE_FAC),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
"");
|
||||
uiBut *but = uiDefBut(block,
|
||||
UI_BTYPE_ROUNDBOX,
|
||||
0,
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
UI_UNIT_X + (6 * UI_SCALE_FAC),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
"");
|
||||
/* UI_BTYPE_ROUNDBOX's bg color is set in but->col. */
|
||||
UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col);
|
||||
|
||||
|
@ -6423,14 +6464,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
UI_block_align_end(block);
|
||||
UI_block_emboss_set(block, UI_EMBOSS_NONE);
|
||||
|
||||
/* The report icon itself. */
|
||||
static char compat_error_msg[256];
|
||||
char writer_ver_str[12];
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
|
||||
SNPRINTF(compat_error_msg,
|
||||
RPT_("File saved by newer Blender\n(%s), expect loss of data"),
|
||||
writer_ver_str);
|
||||
/* Tool tips have to be static currently.
|
||||
* FIXME This is a horrible requirement from uiBut, should probably just store an std::string for
|
||||
* the tooltip as well? */
|
||||
static char tooltip_static_storage[256];
|
||||
BLI_strncpy(tooltip_static_storage, tooltip_message.c_str(), sizeof(tooltip_static_storage));
|
||||
|
||||
/* The warning icon itself. */
|
||||
but = uiDefIconBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
|
@ -6442,23 +6482,25 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
compat_error_msg);
|
||||
tooltip_static_storage);
|
||||
UI_GetThemeColorType4ubv(TH_INFO_WARNING_TEXT, SPACE_INFO, but->col);
|
||||
but->col[3] = 255; /* This theme color is RBG only, so have to set alpha here. */
|
||||
|
||||
/* The report message. */
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
status_info_txt,
|
||||
UI_UNIT_X,
|
||||
0,
|
||||
short(width + UI_UNIT_X),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
compat_error_msg);
|
||||
/* The warning message, if any. */
|
||||
if (!warning_message.empty()) {
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
warning_message.c_str(),
|
||||
UI_UNIT_X,
|
||||
0,
|
||||
short(width + UI_UNIT_X),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
tooltip_static_storage);
|
||||
}
|
||||
|
||||
UI_block_emboss_set(block, previous_emboss);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "interface_intern.hh"
|
||||
|
||||
|
@ -202,7 +204,6 @@ GridViewItemDropTarget::GridViewItemDropTarget(AbstractGridView &view) : view_(v
|
|||
* side(s) as well.
|
||||
*/
|
||||
class BuildOnlyVisibleButtonsHelper {
|
||||
const View2D &v2d_;
|
||||
const AbstractGridView &grid_view_;
|
||||
const GridViewStyle &style_;
|
||||
const int cols_per_row_ = 0;
|
||||
|
@ -219,30 +220,34 @@ class BuildOnlyVisibleButtonsHelper {
|
|||
void fill_layout_after_visible(uiBlock &block) const;
|
||||
|
||||
private:
|
||||
IndexRange get_visible_range() const;
|
||||
IndexRange get_visible_range(const View2D &v2d) const;
|
||||
void add_spacer_button(uiBlock &block, int row_count) const;
|
||||
};
|
||||
|
||||
BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d,
|
||||
const AbstractGridView &grid_view,
|
||||
const int cols_per_row)
|
||||
: v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
|
||||
: grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
|
||||
{
|
||||
visible_items_range_ = this->get_visible_range();
|
||||
visible_items_range_ = this->get_visible_range(v2d);
|
||||
}
|
||||
|
||||
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const
|
||||
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range(const View2D &v2d) const
|
||||
{
|
||||
if ((v2d.flag & V2D_IS_INIT) == 0) {
|
||||
return IndexRange(0, grid_view_.get_item_count_filtered());
|
||||
}
|
||||
|
||||
int first_idx_in_view = 0;
|
||||
|
||||
const float scroll_ofs_y = std::abs(v2d_.cur.ymax - v2d_.tot.ymax);
|
||||
const float scroll_ofs_y = std::abs(v2d.cur.ymax - v2d.tot.ymax);
|
||||
if (!IS_EQF(scroll_ofs_y, 0)) {
|
||||
const int scrolled_away_rows = int(scroll_ofs_y) / style_.tile_height;
|
||||
|
||||
first_idx_in_view = scrolled_away_rows * cols_per_row_;
|
||||
}
|
||||
|
||||
const int view_height = BLI_rcti_size_y(&v2d_.mask);
|
||||
const int view_height = BLI_rcti_size_y(&v2d.mask);
|
||||
const int count_rows_in_view = std::max(view_height / style_.tile_height, 1);
|
||||
const int max_items_in_view = (count_rows_in_view + 1) * cols_per_row_;
|
||||
|
||||
|
@ -405,23 +410,42 @@ PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int prev
|
|||
{
|
||||
}
|
||||
|
||||
void PreviewGridItem::build_grid_tile(uiLayout &layout) const
|
||||
void PreviewGridItem::build_grid_tile_button(uiLayout &layout,
|
||||
const wmOperatorType *ot,
|
||||
const PointerRNA *op_props) const
|
||||
{
|
||||
const GridViewStyle &style = this->get_view().get_style();
|
||||
uiBlock *block = uiLayoutGetBlock(&layout);
|
||||
|
||||
uiBut *but = uiDefBut(block,
|
||||
uiBut *but;
|
||||
if (ot) {
|
||||
but = uiDefButO_ptr(block,
|
||||
UI_BTYPE_PREVIEW_TILE,
|
||||
0,
|
||||
const_cast<wmOperatorType *>(ot),
|
||||
WM_OP_INVOKE_REGION_WIN,
|
||||
hide_label_ ? "" : label,
|
||||
0,
|
||||
0,
|
||||
style.tile_width,
|
||||
style.tile_height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
"");
|
||||
but->opptr = MEM_new<PointerRNA>(__func__, *op_props);
|
||||
}
|
||||
else {
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_PREVIEW_TILE,
|
||||
0,
|
||||
hide_label_ ? "" : label,
|
||||
0,
|
||||
0,
|
||||
style.tile_width,
|
||||
style.tile_height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
"");
|
||||
}
|
||||
|
||||
/* Draw icons that are not previews or images as normal icons with a fixed icon size. Otherwise
|
||||
* they will be upscaled to the button size. Should probably be done by the widget code. */
|
||||
const int is_preview_flag = (BKE_icon_is_preview(preview_icon_id) ||
|
||||
|
@ -436,6 +460,11 @@ void PreviewGridItem::build_grid_tile(uiLayout &layout) const
|
|||
but->emboss = UI_EMBOSS_NONE;
|
||||
}
|
||||
|
||||
void PreviewGridItem::build_grid_tile(uiLayout &layout) const
|
||||
{
|
||||
this->build_grid_tile_button(layout);
|
||||
}
|
||||
|
||||
void PreviewGridItem::set_on_activate_fn(ActivateFn fn)
|
||||
{
|
||||
activate_fn_ = fn;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <variant>
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
|
@ -201,9 +202,9 @@ ui::AbstractViewItem *UI_region_views_find_active_item(const ARegion *region)
|
|||
return item_but->view_item;
|
||||
}
|
||||
|
||||
uiBut *UI_region_views_find_active_item_but(const ARegion *region)
|
||||
uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region)
|
||||
{
|
||||
return ui_view_item_find_active(region);
|
||||
return ui_view_item_find_mouse_over(region, win->eventstate->xy);
|
||||
}
|
||||
|
||||
namespace blender::ui {
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "BKE_anim_data.hh"
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_blender_copybuffer.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
|
@ -821,22 +822,26 @@ void MATERIAL_OT_new(wmOperatorType *ot)
|
|||
|
||||
static int new_texture_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Tex *tex = static_cast<Tex *>(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
PointerRNA ptr;
|
||||
PropertyRNA *prop;
|
||||
|
||||
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
|
||||
|
||||
Main *id_main = CTX_data_main(C);
|
||||
if (ptr.owner_id) {
|
||||
id_main = BKE_main_from_id(id_main, ptr.owner_id);
|
||||
}
|
||||
|
||||
/* add or copy texture */
|
||||
Tex *tex = static_cast<Tex *>(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data);
|
||||
if (tex) {
|
||||
tex = (Tex *)BKE_id_copy(bmain, &tex->id);
|
||||
tex = (Tex *)BKE_id_copy(id_main, &tex->id);
|
||||
}
|
||||
else {
|
||||
tex = BKE_texture_add(bmain, DATA_("Texture"));
|
||||
tex = BKE_texture_add(id_main, DATA_("Texture"));
|
||||
}
|
||||
|
||||
/* hook into UI */
|
||||
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
|
||||
|
||||
if (prop) {
|
||||
/* when creating new ID blocks, use is already 1, but RNA
|
||||
* pointer use also increases user, so this compensates it */
|
||||
|
|
|
@ -2021,6 +2021,9 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar
|
|||
area_azone_init(win, screen, area);
|
||||
|
||||
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
|
||||
if (region->flag & RGN_FLAG_POLL_FAILED) {
|
||||
continue;
|
||||
}
|
||||
region_evaulate_visibility(region);
|
||||
|
||||
/* region size may have changed, init does necessary adjustments */
|
||||
|
|
|
@ -755,6 +755,9 @@ static void screen_regions_poll(bContext *C, const wmWindow *win, bScreen *scree
|
|||
if (region_poll(C, screen, area, region) == false) {
|
||||
region->flag |= RGN_FLAG_POLL_FAILED;
|
||||
}
|
||||
else if (region->type && region->type->on_poll_success) {
|
||||
region->type->on_poll_success(C, region);
|
||||
}
|
||||
|
||||
if (old_region_flag != region->flag) {
|
||||
any_changed = true;
|
||||
|
|
|
@ -4,8 +4,12 @@
|
|||
|
||||
set(INC
|
||||
../include
|
||||
../asset
|
||||
../uvedit
|
||||
../../asset_system
|
||||
../../blenkernel
|
||||
../../blenlib
|
||||
../../blenloader
|
||||
../../blentranslation
|
||||
../../bmesh
|
||||
../../draw
|
||||
|
|
|
@ -282,7 +282,8 @@ static void curves_sculptmode_enter(bContext *C)
|
|||
wmMsgBus *mbus = CTX_wm_message_bus(C);
|
||||
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt);
|
||||
BKE_paint_ensure(
|
||||
CTX_data_main(C), scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt);
|
||||
CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt;
|
||||
|
||||
ob->mode = OB_MODE_SCULPT_CURVES;
|
||||
|
|
|
@ -901,7 +901,7 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain,
|
|||
|
||||
BKE_paint_init(bmain, scene, PaintMode::Texture3D, PAINT_CURSOR_TEXTURE_PAINT);
|
||||
|
||||
BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint);
|
||||
BKE_paint_brush_validate(bmain, &imapaint->paint);
|
||||
|
||||
if (U.glreslimit != 0) {
|
||||
BKE_image_free_all_gputextures(bmain);
|
||||
|
|
|
@ -555,7 +555,8 @@ void get_brush_alpha_data(const Scene *scene,
|
|||
|
||||
void init_stroke(Depsgraph *depsgraph, Object *ob);
|
||||
void init_session_data(const ToolSettings *ts, Object *ob);
|
||||
void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode);
|
||||
void init_session(
|
||||
Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode);
|
||||
|
||||
Vector<PBVHNode *> pbvh_gather_generic(Object *ob, VPaint *wp, Brush *brush);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -214,10 +214,11 @@ void init_stroke(Depsgraph *depsgraph, Object *ob)
|
|||
}
|
||||
|
||||
/* Toggle operator for turning vertex paint mode on or off (copied from sculpt.cc) */
|
||||
void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode)
|
||||
void init_session(
|
||||
Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode)
|
||||
{
|
||||
/* Create persistent sculpt mode data */
|
||||
BKE_sculpt_toolsettings_data_ensure(scene);
|
||||
BKE_sculpt_toolsettings_data_ensure(bmain, scene);
|
||||
|
||||
BLI_assert(ob->sculpt == nullptr);
|
||||
ob->sculpt = MEM_new<SculptSession>(__func__);
|
||||
|
@ -321,7 +322,7 @@ void mode_enter_generic(
|
|||
const PaintMode paint_mode = PaintMode::Vertex;
|
||||
ED_mesh_color_ensure(mesh, nullptr);
|
||||
|
||||
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint);
|
||||
BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->vpaint);
|
||||
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
|
||||
ED_paint_cursor_start(paint, vertex_paint_poll);
|
||||
BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT);
|
||||
|
@ -329,7 +330,7 @@ void mode_enter_generic(
|
|||
else if (mode_flag == OB_MODE_WEIGHT_PAINT) {
|
||||
const PaintMode paint_mode = PaintMode::Weight;
|
||||
|
||||
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint);
|
||||
BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->wpaint);
|
||||
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
|
||||
ED_paint_cursor_start(paint, weight_paint_poll);
|
||||
BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT);
|
||||
|
@ -351,7 +352,7 @@ void mode_enter_generic(
|
|||
BKE_sculptsession_free(ob);
|
||||
}
|
||||
|
||||
vwpaint::init_session(depsgraph, scene, ob, mode_flag);
|
||||
vwpaint::init_session(bmain, depsgraph, scene, ob, mode_flag);
|
||||
|
||||
/* Flush object mode. */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
|
@ -576,24 +577,28 @@ void last_stroke_update(Scene *scene, const float location[3])
|
|||
/* -------------------------------------------------------------------- */
|
||||
void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Brush *cur_brush = BKE_paint_brush(paint);
|
||||
|
||||
/* Switch to the blur (smooth) brush if possible. */
|
||||
/* Note: used for both vertexpaint and weightpaint, VPAINT_TOOL_BLUR & WPAINT_TOOL_BLUR are the
|
||||
* same, see comments for eBrushVertexPaintTool & eBrushWeightPaintTool. */
|
||||
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, WPAINT_TOOL_BLUR);
|
||||
BKE_paint_brush_set_essentials(bmain,
|
||||
paint,
|
||||
(paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT) ? "Blur Weight" :
|
||||
"Blur Vertex");
|
||||
Brush *smooth_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (!smooth_brush) {
|
||||
BKE_paint_brush_set(paint, cur_brush);
|
||||
CLOG_WARN(&LOG, "Switching to the blur (smooth) brush not possible, corresponding brush not");
|
||||
cache->saved_active_brush_name[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
Brush *cur_brush = paint->brush;
|
||||
int cur_brush_size = BKE_brush_size_get(scene, cur_brush);
|
||||
|
||||
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
|
||||
|
||||
BKE_paint_brush_set(paint, smooth_brush);
|
||||
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
|
||||
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
|
||||
BKE_curvemapping_init(smooth_brush->curve);
|
||||
|
@ -839,7 +844,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob);
|
||||
BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint);
|
||||
BKE_paint_brush_validate(bmain, &ts->vpaint->paint);
|
||||
}
|
||||
|
||||
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
|
|
@ -1646,7 +1646,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob);
|
||||
BKE_paint_toolslots_brush_validate(bmain, &ts->wpaint->paint);
|
||||
BKE_paint_brush_validate(bmain, &ts->wpaint->paint);
|
||||
}
|
||||
|
||||
/* Prepare armature posemode. */
|
||||
|
|
|
@ -4200,8 +4200,9 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss)
|
|||
|
||||
static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Brush *cur_brush = paint->brush;
|
||||
Brush *cur_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (cur_brush->sculpt_tool == SCULPT_TOOL_MASK) {
|
||||
cache->saved_mask_brush_tool = cur_brush->mask_tool;
|
||||
|
@ -4220,8 +4221,11 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
|
|||
}
|
||||
|
||||
/* Switch to the smooth brush if possible. */
|
||||
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, SCULPT_TOOL_SMOOTH);
|
||||
BKE_paint_brush_set_essentials(bmain, paint, "Smooth");
|
||||
Brush *smooth_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (!smooth_brush) {
|
||||
BKE_paint_brush_set(paint, cur_brush);
|
||||
CLOG_WARN(&LOG, "Switching to the smooth brush not possible, corresponding brush not");
|
||||
cache->saved_active_brush_name[0] = '\0';
|
||||
return;
|
||||
|
@ -4231,7 +4235,6 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
|
|||
|
||||
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
|
||||
|
||||
BKE_paint_brush_set(paint, smooth_brush);
|
||||
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
|
||||
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
|
||||
BKE_curvemapping_init(smooth_brush->curve);
|
||||
|
|
|
@ -280,7 +280,7 @@ namespace blender::ed::sculpt_paint {
|
|||
static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
|
||||
{
|
||||
/* Create persistent sculpt mode data. */
|
||||
BKE_sculpt_toolsettings_data_ensure(scene);
|
||||
BKE_sculpt_toolsettings_data_ensure(bmain, scene);
|
||||
|
||||
/* Create sculpt mode session data. */
|
||||
if (ob->sculpt != nullptr) {
|
||||
|
@ -527,7 +527,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports);
|
||||
BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint);
|
||||
BKE_paint_brush_validate(bmain, &ts->sculpt->paint);
|
||||
|
||||
if (ob->mode & mode_flag) {
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
|
|
|
@ -567,6 +567,53 @@ static int compare_extension(void *user_data, const void *a1, const void *a2)
|
|||
return compare_apply_inverted(compare_tiebreaker(entry1, entry2), sort_data);
|
||||
}
|
||||
|
||||
static int compare_asset_catalog(void *user_data, const void *a1, const void *a2)
|
||||
{
|
||||
const FileListInternEntry *entry1 = static_cast<const FileListInternEntry *>(a1);
|
||||
const FileListInternEntry *entry2 = static_cast<const FileListInternEntry *>(a2);
|
||||
const FileSortData *sort_data = static_cast<const FileSortData *>(user_data);
|
||||
|
||||
if (entry1->asset && !entry2->asset) {
|
||||
return 1;
|
||||
}
|
||||
if (!entry1->asset && entry2->asset) {
|
||||
return -1;
|
||||
}
|
||||
if (!entry1->asset && !entry2->asset) {
|
||||
if (int ret = compare_direntry_generic(entry1, entry2); ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return compare_apply_inverted(compare_tiebreaker(entry1, entry2), sort_data);
|
||||
}
|
||||
|
||||
const asset_system::AssetLibrary &asset_library1 = entry1->asset->owner_asset_library();
|
||||
const asset_system::AssetLibrary &asset_library2 = entry2->asset->owner_asset_library();
|
||||
|
||||
const asset_system::AssetCatalog *catalog1 = asset_library1.catalog_service().find_catalog(
|
||||
entry1->asset->get_metadata().catalog_id);
|
||||
const asset_system::AssetCatalog *catalog2 = asset_library2.catalog_service().find_catalog(
|
||||
entry2->asset->get_metadata().catalog_id);
|
||||
|
||||
/* Always keep assets without catalog last. */
|
||||
if (catalog1 && !catalog2) {
|
||||
return 1;
|
||||
}
|
||||
if (!catalog1 && catalog2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (catalog1 && catalog2) {
|
||||
const int order = BLI_strcasecmp_natural(catalog1->path.name().c_str(),
|
||||
catalog2->path.name().c_str());
|
||||
if (order) {
|
||||
return compare_apply_inverted(order, sort_data);
|
||||
}
|
||||
}
|
||||
|
||||
return compare_apply_inverted(compare_tiebreaker(entry1, entry2), sort_data);
|
||||
}
|
||||
|
||||
void filelist_sort(FileList *filelist)
|
||||
{
|
||||
if (filelist->flags & FL_NEED_SORTING) {
|
||||
|
@ -585,6 +632,9 @@ void filelist_sort(FileList *filelist)
|
|||
case FILE_SORT_EXTENSION:
|
||||
sort_cb = compare_extension;
|
||||
break;
|
||||
case FILE_SORT_ASSET_CATALOG:
|
||||
sort_cb = compare_asset_catalog;
|
||||
break;
|
||||
case FILE_SORT_DEFAULT:
|
||||
default:
|
||||
BLI_assert(0);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
|
@ -1323,7 +1324,6 @@ static Image *image_open_single(Main *bmain,
|
|||
|
||||
static int image_open_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ImageUser *iuser = nullptr;
|
||||
|
@ -1338,9 +1338,16 @@ static int image_open_exec(bContext *C, wmOperator *op)
|
|||
image_open_init(C, op);
|
||||
}
|
||||
|
||||
ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, use_udim);
|
||||
ImageOpenData *iod = static_cast<ImageOpenData *>(op->customdata);
|
||||
|
||||
Main *id_main = CTX_data_main(C);
|
||||
if (iod->pprop.ptr.owner_id) {
|
||||
id_main = BKE_main_from_id(id_main, iod->pprop.ptr.owner_id);
|
||||
}
|
||||
|
||||
ListBase ranges = ED_image_filesel_detect_sequences(id_main, op, use_udim);
|
||||
LISTBASE_FOREACH (ImageFrameRange *, range, &ranges) {
|
||||
Image *ima_range = image_open_single(bmain, op, range, use_multiview);
|
||||
Image *ima_range = image_open_single(id_main, op, range, use_multiview);
|
||||
|
||||
/* take the first image */
|
||||
if ((ima == nullptr) && ima_range) {
|
||||
|
@ -1357,9 +1364,6 @@ static int image_open_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* hook into UI */
|
||||
ImageOpenData *iod = static_cast<ImageOpenData *>(op->customdata);
|
||||
|
||||
if (iod->pprop.prop) {
|
||||
/* when creating new ID blocks, use is already 1, but RNA
|
||||
* pointer use also increases user, so this compensates it */
|
||||
|
@ -1375,7 +1379,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
else if (area && area->spacetype == SPACE_IMAGE) {
|
||||
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
|
||||
ED_space_image_set(bmain, sima, ima, false);
|
||||
ED_space_image_set(id_main, sima, ima, false);
|
||||
iuser = &sima->iuser;
|
||||
}
|
||||
else {
|
||||
|
@ -1415,9 +1419,9 @@ static int image_open_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
/* XXX BKE_packedfile_unpack_image frees image buffers */
|
||||
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
|
||||
ED_preview_kill_jobs(CTX_wm_manager(C), id_main);
|
||||
|
||||
BKE_image_signal(bmain, ima, iuser, IMA_SIGNAL_RELOAD);
|
||||
BKE_image_signal(id_main, ima, iuser, IMA_SIGNAL_RELOAD);
|
||||
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
|
||||
|
||||
MEM_freeN(op->customdata);
|
||||
|
@ -2530,7 +2534,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
|
|||
{
|
||||
SpaceImage *sima;
|
||||
Image *ima;
|
||||
Main *bmain;
|
||||
Main *id_main;
|
||||
PropertyRNA *prop;
|
||||
char name_buffer[MAX_ID_NAME - 2];
|
||||
const char *name;
|
||||
|
@ -2538,9 +2542,14 @@ static int image_new_exec(bContext *C, wmOperator *op)
|
|||
int width, height, floatbuf, gen_type, alpha;
|
||||
int stereo3d;
|
||||
|
||||
ImageNewData *data = image_new_init(C, op);
|
||||
|
||||
/* retrieve state */
|
||||
sima = CTX_wm_space_image(C);
|
||||
bmain = CTX_data_main(C);
|
||||
id_main = CTX_data_main(C);
|
||||
if (data->pprop.ptr.owner_id) {
|
||||
id_main = BKE_main_from_id(id_main, data->pprop.ptr.owner_id);
|
||||
}
|
||||
|
||||
prop = RNA_struct_find_property(op->ptr, "name");
|
||||
RNA_property_string_get(op->ptr, prop, name_buffer);
|
||||
|
@ -2564,7 +2573,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
|
|||
color[3] = 1.0f;
|
||||
}
|
||||
|
||||
ima = BKE_image_add_generated(bmain,
|
||||
ima = BKE_image_add_generated(id_main,
|
||||
width,
|
||||
height,
|
||||
name,
|
||||
|
@ -2582,8 +2591,6 @@ static int image_new_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
/* hook into UI */
|
||||
ImageNewData *data = image_new_init(C, op);
|
||||
|
||||
if (data->pprop.prop) {
|
||||
/* when creating new ID blocks, use is already 1, but RNA
|
||||
* pointer use also increases user, so this compensates it */
|
||||
|
@ -2594,7 +2601,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
|
|||
RNA_property_update(C, &data->pprop.ptr, data->pprop.prop);
|
||||
}
|
||||
else if (sima) {
|
||||
ED_space_image_set(bmain, sima, ima, false);
|
||||
ED_space_image_set(id_main, sima, ima, false);
|
||||
}
|
||||
else {
|
||||
/* #BKE_image_add_generated creates one user by default, remove it if image is not linked to
|
||||
|
@ -2602,7 +2609,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
|
|||
id_us_min(&ima->id);
|
||||
}
|
||||
|
||||
BKE_image_signal(bmain, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE);
|
||||
BKE_image_signal(id_main, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE);
|
||||
|
||||
WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima);
|
||||
|
||||
|
|
|
@ -1200,6 +1200,7 @@ void ED_spacetype_image()
|
|||
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_ASSET_SHELF | ED_KEYMAP_FRAMES;
|
||||
art->duplicate = asset::shelf::region_duplicate;
|
||||
art->free = asset::shelf::region_free;
|
||||
art->on_poll_success = asset::shelf::region_on_poll_success;
|
||||
art->listener = asset::shelf::region_listen;
|
||||
art->poll = asset::shelf::regions_poll;
|
||||
art->snap_size = asset::shelf::region_snap;
|
||||
|
|
|
@ -2209,6 +2209,7 @@ void ED_spacetype_view3d()
|
|||
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_ASSET_SHELF | ED_KEYMAP_FRAMES;
|
||||
art->duplicate = asset::shelf::region_duplicate;
|
||||
art->free = asset::shelf::region_free;
|
||||
art->on_poll_success = asset::shelf::region_on_poll_success;
|
||||
art->listener = asset::shelf::region_listen;
|
||||
art->poll = asset::shelf::regions_poll;
|
||||
art->snap_size = asset::shelf::region_snap;
|
||||
|
|
|
@ -1000,6 +1000,15 @@ enum {
|
|||
* RESET_NEVER
|
||||
*/
|
||||
LIB_TAG_NOT_ALLOCATED = 1 << 26,
|
||||
/**
|
||||
* ID is part of an asset #Main separate from the regular main database.
|
||||
*
|
||||
* RESET_NEVER
|
||||
*
|
||||
* Datablocks like this can not be linked to and from datablocks in regular main.
|
||||
* They should stay isolated from each other.
|
||||
*/
|
||||
LIB_TAG_ASSET_MAIN = 1 << 27,
|
||||
|
||||
/* ------------------------------------------------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -174,6 +174,12 @@ typedef struct AssetWeakReference {
|
|||
AssetWeakReference &operator=(AssetWeakReference &&);
|
||||
~AssetWeakReference();
|
||||
|
||||
friend bool operator==(const AssetWeakReference &a, const AssetWeakReference &b);
|
||||
friend bool operator!=(const AssetWeakReference &a, const AssetWeakReference &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/**
|
||||
* See AssetRepresentation::make_weak_reference().
|
||||
*/
|
||||
|
@ -199,3 +205,8 @@ typedef struct AssetWeakReference {
|
|||
typedef struct AssetHandle {
|
||||
const struct FileDirEntry *file_data;
|
||||
} AssetHandle;
|
||||
|
||||
struct AssetCatalogPathLink {
|
||||
struct AssetCatalogPathLink *next, *prev;
|
||||
char *path;
|
||||
};
|
||||
|
|
|
@ -899,27 +899,26 @@ typedef struct TimeMarker {
|
|||
|
||||
typedef struct Paint_Runtime {
|
||||
/** Avoid having to compare with scene pointer everywhere. */
|
||||
unsigned int tool_offset;
|
||||
unsigned int initialized;
|
||||
unsigned short ob_mode;
|
||||
char _pad[2];
|
||||
} Paint_Runtime;
|
||||
|
||||
/** We might want to store other things here. */
|
||||
typedef struct PaintToolSlot {
|
||||
struct Brush *brush;
|
||||
} PaintToolSlot;
|
||||
|
||||
/** Paint Tool Base. */
|
||||
typedef struct Paint {
|
||||
/**
|
||||
* The active brush. Possibly null. Possibly stored in a separate #Main data-base and not user-
|
||||
* counted.
|
||||
* //TODO: Or always stored in a separate #Main?
|
||||
*/
|
||||
struct Brush *brush;
|
||||
|
||||
/**
|
||||
* Each tool has its own active brush,
|
||||
* The currently active tool is defined by the current 'brush'.
|
||||
* A weak asset reference to the #brush, if not NULL.
|
||||
* Used to attempt restoring the active brush from the AssetLibrary system, typically on
|
||||
* file load.
|
||||
*/
|
||||
struct PaintToolSlot *tool_slots;
|
||||
int tool_slots_len;
|
||||
char _pad1[4];
|
||||
struct AssetWeakReference *brush_asset_reference;
|
||||
|
||||
struct Palette *palette;
|
||||
/** Cavity curve. */
|
||||
|
|
|
@ -799,7 +799,7 @@ typedef struct AssetShelfSettings {
|
|||
|
||||
AssetLibraryReference asset_library_reference;
|
||||
|
||||
ListBase enabled_catalog_paths; /* #LinkData */
|
||||
ListBase enabled_catalog_paths; /* #AssetCatalogPathLink */
|
||||
/** If not set (null or empty string), all assets will be displayed ("All" catalog behavior). */
|
||||
const char *active_catalog_path;
|
||||
|
||||
|
@ -833,6 +833,7 @@ typedef struct AssetShelf {
|
|||
|
||||
AssetShelfSettings settings;
|
||||
|
||||
/** Only for the permanent asset shelf regions, not asset shelves in temporary popups. */
|
||||
short preferred_row_count;
|
||||
char _pad[6];
|
||||
} AssetShelf;
|
||||
|
@ -855,6 +856,8 @@ typedef struct RegionAssetShelf {
|
|||
AssetShelf *active_shelf; /* Non-owning. */
|
||||
#ifdef __cplusplus
|
||||
static RegionAssetShelf *get_from_asset_shelf_region(const ARegion ®ion);
|
||||
/** Creates the asset shelf region data if necessary, and returns it. */
|
||||
static RegionAssetShelf *ensure_from_asset_shelf_region(ARegion ®ion);
|
||||
#endif
|
||||
} RegionAssetShelf;
|
||||
|
||||
|
|
|
@ -985,6 +985,8 @@ enum eFileSortType {
|
|||
FILE_SORT_EXTENSION = 2,
|
||||
FILE_SORT_TIME = 3,
|
||||
FILE_SORT_SIZE = 4,
|
||||
/* Assets: Sort by catalog. Within each catalog, assets will be sorted by name. */
|
||||
FILE_SORT_ASSET_CATALOG = 5,
|
||||
};
|
||||
|
||||
/** #SpaceFile.tags */
|
||||
|
|
|
@ -39,4 +39,16 @@
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name bUserExtensionRepo Struct
|
||||
* \{ */
|
||||
|
||||
#define _DNA_DEFAULT_bUserAssetShelfSettings \
|
||||
{ \
|
||||
.shelf_idname = {'\0'}, \
|
||||
.enabled_catalog_paths = {NULL, NULL}, \
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* clang-format on */
|
||||
|
|
|
@ -754,6 +754,20 @@ typedef struct bUserScriptDirectory {
|
|||
char dir_path[768]; /* FILE_MAXDIR */
|
||||
} bUserScriptDirectory;
|
||||
|
||||
/**
|
||||
* Settings for an asset shelf, stored in the Preferences. Most settings are still stored in the
|
||||
* asset shelf instance in #AssetShelfSettings. This is just for the options that should be shared
|
||||
* as Preferences.
|
||||
*/
|
||||
typedef struct bUserAssetShelfSettings {
|
||||
struct bUserAssetShelfSettings *next, *prev;
|
||||
|
||||
/** Identifier that matches the #AssetShelfType.idname of the shelf these settings apply to. */
|
||||
char shelf_idname[64]; /* MAX_NAME */
|
||||
|
||||
ListBase enabled_catalog_paths; /* #AssetCatalogPathLink */
|
||||
} bUserAssetShelfSettings;
|
||||
|
||||
/**
|
||||
* Main user preferences data, typically accessed from #U.
|
||||
* See: #BKE_blendfile_userdef_from_defaults & #BKE_blendfile_userdef_read.
|
||||
|
@ -891,6 +905,7 @@ typedef struct UserDef {
|
|||
struct ListBase asset_libraries;
|
||||
/** #bUserExtensionRepo */
|
||||
struct ListBase extension_repos;
|
||||
struct ListBase asset_shelves_settings; /* #bUserAssetShelfSettings */
|
||||
|
||||
char keyconfigstr[64];
|
||||
|
||||
|
|
|
@ -238,6 +238,7 @@ SDNA_DEFAULT_DECL_STRUCT(Tex);
|
|||
/* DNA_userdef_types.h */
|
||||
SDNA_DEFAULT_DECL_STRUCT(bUserAssetLibrary);
|
||||
SDNA_DEFAULT_DECL_STRUCT(bUserExtensionRepo);
|
||||
SDNA_DEFAULT_DECL_STRUCT(bUserAssetShelfSettings);
|
||||
|
||||
/* DNA_view3d_defaults.h */
|
||||
SDNA_DEFAULT_DECL_STRUCT(View3D);
|
||||
|
@ -517,6 +518,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
|
|||
SDNA_DEFAULT_DECL_EX(WalkNavigation, UserDef.walk_navigation),
|
||||
SDNA_DEFAULT_DECL(bUserAssetLibrary),
|
||||
SDNA_DEFAULT_DECL(bUserExtensionRepo),
|
||||
SDNA_DEFAULT_DECL(bUserAssetShelfSettings),
|
||||
|
||||
/* DNA_view3d_defaults.h */
|
||||
SDNA_DEFAULT_DECL(View3D),
|
||||
|
|
|
@ -545,6 +545,7 @@ struct StringPropertySearchVisitParams {
|
|||
std::string text;
|
||||
/** Additional information to display. */
|
||||
std::optional<std::string> info;
|
||||
std::optional<int> icon_id;
|
||||
};
|
||||
|
||||
enum eStringPropertySearchFlag {
|
||||
|
|
|
@ -1269,14 +1269,30 @@ static char *rna_def_property_set_func(
|
|||
else {
|
||||
rna_print_data_get(f, dp);
|
||||
|
||||
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
|
||||
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
|
||||
|
||||
if (prop->flag & PROP_ID_SELF_CHECK) {
|
||||
/* No pointers to self allowed. */
|
||||
rna_print_id_get(f, dp);
|
||||
fprintf(f, " if (id == value.data) {\n");
|
||||
fprintf(f, " return;\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
|
||||
if (type && (type->flag & STRUCT_ID)) {
|
||||
/* Can only assign pointers between datablocks in the same main database. */
|
||||
fprintf(f, " if (value.data) {\n");
|
||||
fprintf(f, " Main *owner_main = BKE_main_from_id(G_MAIN, ptr->owner_id);\n");
|
||||
fprintf(f, " Main *value_main = BKE_main_from_id(G_MAIN, (ID *)value.data);\n");
|
||||
fprintf(f, " if (owner_main && owner_main != value_main) {\n");
|
||||
fprintf(f, " return;\n");
|
||||
fprintf(f, " }\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
|
||||
if (prop->flag & PROP_ID_REFCOUNT) {
|
||||
/* Perform reference counting. */
|
||||
fprintf(f, "\n if (data->%s) {\n", dp->dnaname);
|
||||
fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname);
|
||||
fprintf(f, " }\n");
|
||||
|
@ -1284,14 +1300,11 @@ static char *rna_def_property_set_func(
|
|||
fprintf(f, " id_us_plus((ID *)value.data);\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
else {
|
||||
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
|
||||
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
|
||||
if (type && (type->flag & STRUCT_ID)) {
|
||||
fprintf(f, " if (value.data) {\n");
|
||||
fprintf(f, " id_lib_extern((ID *)value.data);\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
else if (type && (type->flag & STRUCT_ID)) {
|
||||
/* Still mark linked data as used if not reference counting. */
|
||||
fprintf(f, " if (value.data) {\n");
|
||||
fprintf(f, " id_lib_extern((ID *)value.data);\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
|
||||
fprintf(f, " *(void **)&data->%s = value.data;\n", dp->dnaname);
|
||||
|
@ -4880,6 +4893,7 @@ static void rna_generate(BlenderRNA *brna, FILE *f, const char *filename, const
|
|||
fprintf(f, "#include \"BLI_utildefines.h\"\n\n");
|
||||
|
||||
fprintf(f, "#include \"BKE_context.hh\"\n");
|
||||
fprintf(f, "#include \"BKE_global.hh\"\n");
|
||||
fprintf(f, "#include \"BKE_lib_id.hh\"\n");
|
||||
fprintf(f, "#include \"BKE_main.hh\"\n");
|
||||
fprintf(f, "#include \"BKE_report.hh\"\n");
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_icons.h"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main_namemap.hh"
|
||||
|
@ -289,10 +290,9 @@ int rna_ID_name_length(PointerRNA *ptr)
|
|||
void rna_ID_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
ID *id = (ID *)ptr->data;
|
||||
BLI_assert(BKE_id_is_in_global_main(id));
|
||||
BLI_assert(!ID_IS_LINKED(id));
|
||||
Main *id_main = BKE_main_from_id(G_MAIN, id);
|
||||
|
||||
BKE_libblock_rename(G_MAIN, id, value);
|
||||
BKE_libblock_rename(id_main, id, value);
|
||||
|
||||
if (GS(id->name) == ID_OB) {
|
||||
Object *ob = (Object *)id;
|
||||
|
@ -300,6 +300,8 @@ void rna_ID_name_set(PointerRNA *ptr, const char *value)
|
|||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: this does not update immediately in the asset shelf. */
|
||||
}
|
||||
|
||||
static int rna_ID_name_editable(const PointerRNA *ptr, const char **r_info)
|
||||
|
@ -325,7 +327,7 @@ static int rna_ID_name_editable(const PointerRNA *ptr, const char **r_info)
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
else if (!BKE_id_is_in_global_main(id)) {
|
||||
else if (BKE_main_from_id(G_MAIN, id, true) == nullptr) {
|
||||
if (r_info) {
|
||||
*r_info = N_("Datablocks not in global Main data-base cannot be renamed");
|
||||
}
|
||||
|
@ -1166,16 +1168,20 @@ bool rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assi
|
|||
{
|
||||
ID *id = ptr->owner_id;
|
||||
short *totcol = BKE_id_material_len_p(id);
|
||||
Material *mat_id = (Material *)assign_ptr->owner_id;
|
||||
if (totcol && (key >= 0 && key < *totcol)) {
|
||||
BLI_assert(BKE_id_is_in_global_main(id));
|
||||
BLI_assert(BKE_id_is_in_global_main(&mat_id->id));
|
||||
BKE_id_material_assign(G_MAIN, id, mat_id, key + 1);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Material *mat = (Material *)assign_ptr->owner_id;
|
||||
if (!(totcol && (key >= 0 && key < *totcol))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Main *id_main = BKE_main_from_id(G_MAIN, id);
|
||||
if (mat) {
|
||||
if (id_main != BKE_main_from_id(G_MAIN, &mat->id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_id_material_assign(id_main, id, mat, key + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rna_IDMaterials_append_id(ID *id, Main *bmain, Material *ma)
|
||||
|
@ -1226,8 +1232,8 @@ static void rna_IDMaterials_clear_id(ID *id, Main *bmain)
|
|||
static void rna_Library_filepath_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
Library *lib = (Library *)ptr->data;
|
||||
BLI_assert(BKE_id_is_in_global_main(&lib->id));
|
||||
BKE_library_filepath_set(G_MAIN, lib, value);
|
||||
Main *id_main = BKE_main_from_id(G_MAIN, &lib->id);
|
||||
BKE_library_filepath_set(id_main, lib, value);
|
||||
}
|
||||
|
||||
/* ***** ImagePreview ***** */
|
||||
|
@ -2315,6 +2321,14 @@ static void rna_def_ID(BlenderRNA *brna)
|
|||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Is Indirect", "Is this ID block linked indirectly");
|
||||
|
||||
prop = RNA_def_property(srna, "is_asset_library_data", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "tag", LIB_TAG_ASSET_MAIN);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Asset Library Data",
|
||||
"This data-block is part of an asset library blend file, not the blend "
|
||||
"file opened for editing");
|
||||
|
||||
prop = RNA_def_property(srna, "library", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, nullptr, "lib");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
|
|
@ -2144,7 +2144,8 @@ static bool rna_property_editable_do(const PointerRNA *ptr,
|
|||
return false;
|
||||
}
|
||||
if (ID_IS_OVERRIDE_LIBRARY(id)) {
|
||||
const bool is_liboverride_system = BKE_lib_override_library_is_system_defined(G_MAIN, id);
|
||||
Main *main = BKE_main_from_id(G_MAIN, id);
|
||||
const bool is_liboverride_system = BKE_lib_override_library_is_system_defined(main, id);
|
||||
if (!RNA_property_overridable_get(ptr, prop_orig)) {
|
||||
if (r_info != nullptr && (*r_info)[0] == '\0') {
|
||||
*r_info = N_("Can't edit this property from an override data-block");
|
||||
|
@ -2389,7 +2390,11 @@ bool RNA_property_update_check(PropertyRNA *prop)
|
|||
|
||||
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop);
|
||||
Main *main = CTX_data_main(C);
|
||||
if (ptr->owner_id) {
|
||||
main = BKE_main_from_id(main, ptr->owner_id);
|
||||
}
|
||||
rna_property_update(C, main, CTX_data_scene(C), ptr, prop);
|
||||
}
|
||||
|
||||
void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)
|
||||
|
|
|
@ -791,28 +791,36 @@ static void rna_EditBone_name_set(PointerRNA *ptr, const char *value)
|
|||
{
|
||||
bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
EditBone *ebone = (EditBone *)ptr->data;
|
||||
char oldname[sizeof(ebone->name)], newname[sizeof(ebone->name)];
|
||||
|
||||
Main *main = BKE_main_from_id(G_MAIN, &arm->id);
|
||||
if (main == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
char oldname[sizeof(ebone->name)], newname[sizeof(ebone->name)];
|
||||
/* need to be on the stack */
|
||||
STRNCPY_UTF8(newname, value);
|
||||
STRNCPY(oldname, ebone->name);
|
||||
|
||||
BLI_assert(BKE_id_is_in_global_main(&arm->id));
|
||||
ED_armature_bone_rename(G_MAIN, arm, oldname, newname);
|
||||
ED_armature_bone_rename(main, arm, oldname, newname);
|
||||
}
|
||||
|
||||
static void rna_Bone_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
Bone *bone = (Bone *)ptr->data;
|
||||
char oldname[sizeof(bone->name)], newname[sizeof(bone->name)];
|
||||
|
||||
Main *main = BKE_main_from_id(G_MAIN, &arm->id);
|
||||
if (main == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
char oldname[sizeof(bone->name)], newname[sizeof(bone->name)];
|
||||
/* need to be on the stack */
|
||||
STRNCPY_UTF8(newname, value);
|
||||
STRNCPY(oldname, bone->name);
|
||||
|
||||
BLI_assert(BKE_id_is_in_global_main(&arm->id));
|
||||
ED_armature_bone_rename(G_MAIN, arm, oldname, newname);
|
||||
ED_armature_bone_rename(main, arm, oldname, newname);
|
||||
}
|
||||
|
||||
static void rna_EditBone_connected_check(EditBone *ebone)
|
||||
|
|
|
@ -86,18 +86,23 @@ static void rna_Image_source_set(PointerRNA *ptr, int value)
|
|||
{
|
||||
Image *ima = (Image *)ptr->owner_id;
|
||||
|
||||
if (value != ima->source) {
|
||||
ima->source = value;
|
||||
BLI_assert(BKE_id_is_in_global_main(&ima->id));
|
||||
BKE_image_signal(G_MAIN, ima, nullptr, IMA_SIGNAL_SRC_CHANGE);
|
||||
if (ima->source == IMA_SRC_TILED) {
|
||||
BKE_image_signal(G_MAIN, ima, nullptr, IMA_SIGNAL_RELOAD);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ima->id, 0);
|
||||
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS | ID_RECALC_SOURCE);
|
||||
DEG_relations_tag_update(G_MAIN);
|
||||
if (value == ima->source) {
|
||||
return;
|
||||
}
|
||||
ima->source = value;
|
||||
|
||||
Main *main = BKE_main_from_id(G_MAIN, &ima->id);
|
||||
if (main == nullptr) {
|
||||
return;
|
||||
}
|
||||
BKE_image_signal(main, ima, nullptr, IMA_SIGNAL_SRC_CHANGE);
|
||||
if (ima->source == IMA_SRC_TILED) {
|
||||
BKE_image_signal(main, ima, nullptr, IMA_SIGNAL_RELOAD);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ima->id, 0);
|
||||
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS | ID_RECALC_SOURCE);
|
||||
DEG_relations_tag_update(main);
|
||||
}
|
||||
|
||||
static void rna_Image_reload_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
|
||||
|
|
|
@ -127,6 +127,17 @@ static void rna_Main_ID_remove(Main *bmain,
|
|||
id->name + 2);
|
||||
return;
|
||||
}
|
||||
if (bmain != BKE_main_from_id(bmain, id)) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"%s '%s' is part of a different main database and should be removed from there",
|
||||
BKE_idtype_idcode_to_name(GS(id->name)),
|
||||
id->name + 2);
|
||||
return;
|
||||
}
|
||||
/* TODO: this will not clear pointers from regular main to this asset.
|
||||
* Those probably should not exist, and be purely runtime lookups? */
|
||||
|
||||
if (do_unlink) {
|
||||
BKE_id_delete(bmain, id);
|
||||
RNA_POINTER_INVALIDATE(id_ptr);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue