Sculpt: Brush settings panel editor

* Moved brush settings (in sculpt mode) to
  (for now) a new properties editor tab.
* Brush settings can now be individually configured
  to show up in the workspace buttons.
* Brush settings can also be reordered.
* The new brush tab has a "preview" subpanel
  to preview the workspace settings layout.
  This is where settings are reordered.
This commit is contained in:
Joseph Eagar 2021-09-22 18:26:25 -07:00
parent 0e2ec88e6b
commit 36d5b6e959
15 changed files with 411 additions and 23 deletions

View File

@ -97,7 +97,7 @@ _modules = [
"space_view3d_toolbar",
# XXX, keep last so panels show after all other tool options.
"properties_workspace",
"properties_workspace"
]
import bpy

View File

@ -29,6 +29,7 @@ channel_name_map = {
"autosmooth_fset_slide": "fset_slide",
"topology_rake_factor": "topology_rake"
};
expand_channels = {"direction"}
class UnifiedPaintPanel:
# subclass must set
@ -109,12 +110,15 @@ class UnifiedPaintPanel:
@staticmethod
def channel_unified(layout, context, brush, prop_name, icon='NONE', pressure=True, text=None,
slider=False, header=False, expand=None, toolsettings_only=False):
slider=False, header=False, show_reorder=False, expand=None, toolsettings_only=False, ui_editing=True):
""" Generalized way of adding brush options to the UI,
along with their pen pressure setting and global toggle, if they exist. """
ch = brush.channels.channels[prop_name]
finalch = ch
if prop_name in expand_channels:
expand = True
l1 = layout
#if ch.ui_expanded:
@ -161,6 +165,21 @@ class UnifiedPaintPanel:
path = "tool_settings.sculpt.channels.channels[\"%s\"]" % ch.idname
else:
path = "tool_settings.sculpt.brush.channels.channels[\"%s\"]" % ch.idname
if show_reorder:
props = row.operator("brush.change_channel_order", text="", icon="TRIA_UP")
props.channel = ch.idname
props.filterkey = "show_in_workspace"
props.direction = -1
props = row.operator("brush.change_channel_order", text="", icon="TRIA_DOWN")
props.filterkey = "show_in_workspace"
props.channel = ch.idname
props.direction = 1
if ui_editing:
row.prop(ch, "show_in_workspace", text="", icon="HIDE_OFF")
#row.prop(ch, "ui_order", text="")
if ch.type == "BITMASK":
row.label(text=text)
@ -186,6 +205,8 @@ class UnifiedPaintPanel:
if pressure:
row.prop(finalch.mappings["PRESSURE"], "enabled", text="", icon="STYLUS_PRESSURE")
#if pressure_name:
# row.prop(brush, pressure_name, text="")
@ -202,6 +223,9 @@ class UnifiedPaintPanel:
if ch.type == "BITMASK" or ch.type == "BOOL":
return
if not ui_editing and not show_reorder:
return
row.prop(ch, "ui_expanded", text="", icon="TRIA_DOWN" if ch.ui_expanded else "TRIA_RIGHT")
if ch.ui_expanded:
@ -1075,9 +1099,6 @@ def brush_settings(layout, context, brush, popover=False):
layout.row().prop(brush, "mask_tool", expand=True)
layout.template_curve_mapping(brush, "pressure_size_curve")
layout.template_curve_mapping(brush, "pressure_strength_curve", brush=True)
# End sculpt_tool interface.
# 3D and 2D Texture Paint Mode.
@ -1226,6 +1247,91 @@ def brush_shared_settings(layout, context, brush, popover=False):
"direction", expand=True)
#layout.row().prop(brush, "direction", expand=True)
from bpy.types import Operator
from bpy.props import IntProperty, StringProperty
def get_ui_channels(channels, filterkeys=["show_in_workspace"]):
ret = []
for ch in channels:
ok = len(filterkeys) == 0
for key in filterkeys:
if getattr(ch, key):
ok = True
break
if ok:
ret.append(ch)
ret.sort(key = lambda x: x.ui_order)
return ret
class ReorderBrushChannel(Operator):
"""Tooltip"""
bl_idname = "brush.change_channel_order"
bl_label = "Change Channel Order"
bl_options = {"UNDO"}
direction : IntProperty()
channel : StringProperty()
filterkey : StringProperty()
@classmethod
def poll(cls, context):
return context.mode == "SCULPT" and context.tool_settings.sculpt.brush
def execute(self, context):
ts = context.tool_settings
brush = ts.sculpt.brush
channels = brush.channels.channels
if self.channel not in channels:
print("bad channel ", self.channel)
return {'CANCELLED'}
uinames = get_ui_channels(channels, [self.filterkey])
uinames = set(map(lambda x: x.idname, uinames))
channel = channels[self.channel]
channels = list(channels)
channels.sort(key = lambda x: x.ui_order)
i = channels.index(channel)
i2 = i + self.direction
print("ORDERING", i, i2, self.direction, i2 < 0 or i2 >= len(channels))
if i2 < 0 or i2 >= len(channels):
return {'CANCELLED'}
while i2 >= 0 and i2 < len(channels) and channels[i2].idname not in uinames:
i2 += self.direction
i2 = min(max(i2, 0), len(channels)-1)
tmp = channels[i2]
channels[i2] = channels[i]
channels[i] = tmp
#ensure ui_order is 1-to-1
for i, ch in enumerate(channels):
ch.ui_order = i
print(ch.idname, i)
return {'FINISHED'}
def brush_settings_channels(layout, context, brush, ui_editing=False, popover=False, filterkey="show_in_workspace"):
channels = get_ui_channels(brush.channels.channels, [filterkey])
for ch in channels:
UnifiedPaintPanel.channel_unified(
layout.column(),
context,
brush,
ch.idname, show_reorder = ui_editing, expand=False, ui_editing=False)
def brush_settings_advanced(layout, context, brush, popover=False):
"""Draw advanced brush settings for Sculpt, Texture/Vertex/Weight Paint modes."""
@ -1709,6 +1815,7 @@ def brush_basic_gpencil_vertex_settings(layout, _context, brush, *, compact=Fals
classes = (
VIEW3D_MT_tools_projectpaint_clone,
ReorderBrushChannel
)
if __name__ == "__main__": # only for live edit.

View File

@ -17,7 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
from bpy.types import Menu, Panel, UIList, WindowManager
from bpy.types import Menu, Panel, UIList, WindowManager, SpaceProperties
from bl_ui.properties_grease_pencil_common import (
GreasePencilSculptOptionsPanel,
GreasePencilDisplayPanel,
@ -37,6 +37,7 @@ from bl_ui.properties_paint_common import (
brush_mask_texture_settings,
brush_settings,
brush_settings_advanced,
brush_settings_channels,
draw_color_settings,
)
from bl_ui.utils import PresetPanel
@ -378,7 +379,9 @@ class VIEW3D_PT_tools_brush_select(Panel, View3DPaintBrushPanel, BrushSelectPane
bl_label = "Brushes"
# TODO, move to space_view3d.py
def is_brush_editor(context):
return type(context.space_data) == SpaceProperties and context.space_data.context == "BRUSH_EDITOR"
class VIEW3D_PT_tools_brush_settings(Panel, View3DPaintBrushPanel):
bl_context = ".paint_common"
bl_label = "Brush Settings"
@ -386,7 +389,11 @@ class VIEW3D_PT_tools_brush_settings(Panel, View3DPaintBrushPanel):
@classmethod
def poll(cls, context):
settings = cls.paint_settings(context)
return settings and settings.brush is not None
ok = settings and settings.brush is not None
ok = ok and not (context.mode == "SCULPT" and not is_brush_editor(context))
return ok
def draw(self, context):
layout = self.layout
@ -399,6 +406,54 @@ class VIEW3D_PT_tools_brush_settings(Panel, View3DPaintBrushPanel):
brush_settings(layout.column(), context, brush, popover=self.is_popover)
class VIEW3D_PT_tools_brush_settings_channels(Panel, View3DPaintBrushPanel):
bl_context = ".paint_common"
bl_label = "Brush Settings"
@classmethod
def poll(cls, context):
settings = cls.paint_settings(context)
ok = settings and settings.brush is not None
ok = ok and context.mode == "SCULPT" and not is_brush_editor(context)
return ok
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
settings = self.paint_settings(context)
brush = settings.brush
brush_settings_channels(layout.column(), context, brush, popover=self.is_popover)
class VIEW3D_PT_tools_brush_settings_channels_preview(Panel, View3DPaintBrushPanel):
bl_context = ".paint_common"
bl_parent_id = "VIEW3D_PT_tools_brush_settings"
bl_label = "Settings Preview"
@classmethod
def poll(cls, context):
settings = cls.paint_settings(context)
ok = settings and settings.brush is not None
ok = ok and context.mode == "SCULPT" and is_brush_editor(context)
return ok
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
settings = self.paint_settings(context)
brush = settings.brush
brush_settings_channels(layout.column(), context, brush, ui_editing=True, popover=self.is_popover)
class VIEW3D_PT_tools_brush_settings_advanced(Panel, View3DPaintBrushPanel):
bl_context = ".paint_common"
@ -756,7 +811,7 @@ class VIEW3D_PT_tools_brush_falloff_normal(View3DPaintPanel, Panel):
# TODO, move to space_view3d.py
class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
bl_context = ".brush_editor"
bl_label = "Dyntopo (Advanced)"
#bl_options = {'DEFAULT_CLOSED'}
bl_ui_units_x = 12
@ -764,7 +819,11 @@ class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
@classmethod
def poll(cls, context):
paint_settings = cls.paint_settings(context)
return (context.sculpt_object and context.tool_settings.sculpt and paint_settings)
ok = is_brush_editor(context)
ok = ok and (context.sculpt_object and context.tool_settings.sculpt and paint_settings)
return ok
def draw_header(self, context):
pass
@ -2307,6 +2366,8 @@ classes = (
VIEW3D_PT_slots_projectpaint,
VIEW3D_PT_tools_brush_select,
VIEW3D_PT_tools_brush_settings,
VIEW3D_PT_tools_brush_settings_channels,
VIEW3D_PT_tools_brush_settings_channels_preview,
VIEW3D_PT_tools_brush_color,
VIEW3D_PT_tools_brush_swatches,
VIEW3D_PT_tools_brush_settings_advanced,

View File

@ -101,7 +101,7 @@ typedef struct BrushEnumDef {
} BrushEnumDef;
typedef struct BrushChannelType {
char name[64], idname[64], tooltip[512];
char name[128], idname[64], tooltip[512];
float min, max, soft_min, soft_max;
BrushMappingPreset mappings;
@ -274,6 +274,7 @@ bool BKE_brush_mapping_ensure_write(BrushMapping *mp);
void BKE_brush_channelset_apply_mapping(BrushChannelSet *chset, BrushMappingData *mapdata);
void BKE_brush_check_toolsettings(struct Sculpt *sd);
void BKE_brush_channelset_ui_init(struct Brush *brush, int tool);
/*
set up static type checker for BRUSHSET_XXX macros

View File

@ -321,6 +321,7 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id)
}
else {
BKE_brush_builtin_create(brush, brush->sculpt_tool);
BKE_brush_channelset_ui_init(brush, brush->sculpt_tool);
}
if (brush->dyntopo.radius_scale == 0.0f) {
@ -1799,6 +1800,7 @@ void BKE_brush_sculpt_reset(Brush *br)
// BKE_brush_debug_print_state(br);
BKE_brush_builtin_create(br, br->sculpt_tool);
BrushChannel *ch;
for (ch = (BrushChannel *)br->channels->channels.first; ch; ch = ch->next) {
@ -1808,6 +1810,8 @@ void BKE_brush_sculpt_reset(Brush *br)
BKE_brush_channel_init(ch, def);
}
BKE_brush_channelset_ui_init(br, br->sculpt_tool);
brush_defaults(br);
BKE_brush_curve_preset(br, CURVE_PRESET_SMOOTH);
BKE_brush_default_input_curves_set(br);

View File

@ -1,4 +1,19 @@
// static name checking stuff
/* Builtin brush channels are defined in this file.
When adding a new channel, the following other
places in rna_engine_codebase are relevent:
- brush_settings_map: use to convert to/from settings structures in Brush
- dyntopo_settings_map: same as above but for DynTopoSettings
- brush_flags_map: used to convert bitflags
- BKE_brush_builtin_patch(): adds missing channels to channelsets
- BKE_brush_builtin_create(): adds channels to brushes based on brush type (Brush->sculpt_tool)
- BKE_brush_check_toolsettings: adds missing channels to tool settings
- BKE_brush_channelset_ui_init: Configures UI visibility for channels based on brush type
*/
/* static name checking stuff */
#if defined(BRUSH_CHANNEL_DEFINE_TYPES) || defined(BRUSH_CHANNEL_DEFINE_EXTERNAL)
# ifdef MAKE_FLOAT
# undef MAKE_FLOAT
@ -199,6 +214,56 @@ MAKE_BOOL(use_multiplane_scrape_dynamic, "Dynamic Mode", "The angle between the
MAKE_BOOL(show_multiplane_scrape_planes_preview, "Show Cursor Preview", "Preview the scrape planes in the cursor during the stroke", true)
MAKE_FLOAT(multiplane_scrape_angle, "Plane Angle", "Angle between the planes of the crease", 60.0f, 0.0f, 160.0f)
MAKE_BOOL(use_persistent, "Persistent", "Sculpt on a persistent layer of the mesh", false)
MAKE_ENUM(cloth_deform_type, "Deformation", "Deformation type that is used in the brush", BRUSH_CLOTH_DEFORM_DRAG, _({
{BRUSH_CLOTH_DEFORM_DRAG, "DRAG", 0, "Drag", ""},
{BRUSH_CLOTH_DEFORM_PUSH, "PUSH", 0, "Push", ""},
{BRUSH_CLOTH_DEFORM_PINCH_POINT, "PINCH_POINT", 0, "Pinch Point", ""},
{BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR,
"PINCH_PERPENDICULAR",
0,
"Pinch Perpendicular",
""},
{BRUSH_CLOTH_DEFORM_INFLATE, "INFLATE", 0, "Inflate", ""},
{BRUSH_CLOTH_DEFORM_GRAB, "GRAB", 0, "Grab", ""},
{BRUSH_CLOTH_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""},
{BRUSH_CLOTH_DEFORM_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""},
{BRUSH_CLOTH_DEFORM_ELASTIC_DRAG, "ELASTIC", 0, "Elastic Drag", ""},
{-1}
}))
MAKE_ENUM(cloth_simulation_area_type, "Simulation Area", "Part of the mesh that is going to be simulated when the stroke is active", BRUSH_CLOTH_SIMULATION_AREA_LOCAL, _({
{BRUSH_CLOTH_SIMULATION_AREA_LOCAL,
"LOCAL",
0,
"Local",
"Simulates only a specific area around the brush limited by a fixed radius"},
{BRUSH_CLOTH_SIMULATION_AREA_GLOBAL, "GLOBAL", 0, "Global", "Simulates the entire mesh"},
{BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC,
"DYNAMIC",
0,
"Dynamic",
"The active simulation area moves with the brush"},
{-1}
}))
MAKE_ENUM(cloth_force_falloff_type, "Force Falloff", "Shape used in the brush to apply force to the cloth",
BRUSH_CLOTH_FORCE_FALLOFF_RADIAL, _({
{BRUSH_CLOTH_FORCE_FALLOFF_RADIAL, "RADIAL", 0, "Radial", ""},
{BRUSH_CLOTH_FORCE_FALLOFF_PLANE, "PLANE", 0, "Plane", ""},
{-1}
}))
MAKE_FLOAT(cloth_mass, "Cloth Mass", "Mass of each simulation particle", 1.0f, 0.0f, 2.0f)
MAKE_FLOAT(cloth_damping, "Cloth Damping", "How much the applied forces are propagated through the cloth", 0.01f, 0.01f, 1.0f)
MAKE_FLOAT(cloth_sim_limit, "Simulation Limit",
"Factor added relative to the size of the radius to limit the cloth simulation effects", 2.5f, 0.1f, 10.0f)
MAKE_FLOAT(cloth_sim_falloff, "Simulation Falloff",
"Area to apply deformation falloff to the effects of the simulation", 0.75f, 0.0f, 1.0f)
MAKE_FLOAT(cloth_constraint_softbody_strength, "Soft Body Plasticity",
"How much the cloth preserves the original shape, acting as a soft body", 0.0f, 0.0f, 1.0f)
MAKE_BOOL(use_frontface, "Use Front-Face", "Brush only affects vertexes that face the viewer", false)
/* clang-format on */
#if defined(BRUSH_CHANNEL_DEFINE_TYPES) || defined(BRUSH_CHANNEL_DEFINE_EXTERNAL)
# ifdef MAKE_FLOAT

View File

@ -243,6 +243,7 @@ ATTR_NO_OPT void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def)
strcpy(ch->idname, def->idname);
ch->flag = def->flag;
ch->fvalue = def->fvalue;
ch->ivalue = def->ivalue;

View File

@ -162,7 +162,6 @@ BrushChannelType brush_builtin_channels[] = {
#include "brush_channel_define.h"
};
//BRUSH_INVERT_TO_SCRAPE_FILL
/* clang-format on */
const int brush_builtin_channel_len = ARRAY_SIZE(brush_builtin_channels);
@ -263,6 +262,14 @@ static BrushSettingsMap brush_settings_map[] = {
DEF(secondary_rgb, secondary_color, FLOAT3, FLOAT3)
DEF(vcol_boundary_factor, vcol_boundary_factor, FLOAT, FLOAT)
DEF(vcol_boundary_exponent, vcol_boundary_exponent, FLOAT, FLOAT)
DEF(cloth_deform_type, cloth_deform_type, INT, INT)
DEF(cloth_simulation_area_type, cloth_simulation_area_type, INT, INT)
DEF(cloth_force_falloff_type, cloth_force_falloff_type, INT, INT)
DEF(cloth_mass, cloth_mass, FLOAT, FLOAT)
DEF(cloth_damping, cloth_damping, FLOAT, FLOAT)
DEF(cloth_sim_limit, cloth_sim_limit, FLOAT, FLOAT)
DEF(cloth_sim_falloff, cloth_sim_falloff, FLOAT, FLOAT)
DEF(cloth_constraint_softbody_strength, cloth_constraint_softbody_strength, FLOAT, FLOAT)
};
static const int brush_settings_map_len = ARRAY_SIZE(brush_settings_map);
@ -316,6 +323,8 @@ BrushFlagMap brush_flags_map[] = {
DEF(flag, invert_to_scrape_fill, BRUSH_INVERT_TO_SCRAPE_FILL)
DEF(flag2, use_multiplane_scrape_dynamic, BRUSH_MULTIPLANE_SCRAPE_DYNAMIC)
DEF(flag2, show_multiplane_scrape_planes_preview, BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW)
DEF(flag, use_persistent, BRUSH_PERSISTENT)
DEF(flag, use_frontface, BRUSH_FRONTFACE)
};
int brush_flags_map_len = ARRAY_SIZE(brush_flags_map);
@ -598,8 +607,11 @@ void BKE_brush_builtin_patch(Brush *brush, int tool)
namestack_push(__func__);
bool setup_ui = false;
if (!brush->channels) {
brush->channels = BKE_brush_channelset_create();
setup_ui = true;
}
BrushChannelSet *chset = brush->channels;
@ -670,12 +682,105 @@ void BKE_brush_builtin_patch(Brush *brush, int tool)
}
case SCULPT_TOOL_SLIDE_RELAX:
ADDCH(slide_deform_type);
break;
case SCULPT_TOOL_CLOTH:
ADDCH(cloth_mass);
ADDCH(cloth_damping);
ADDCH(cloth_sim_limit);
ADDCH(cloth_sim_falloff);
ADDCH(cloth_deform_type);
ADDCH(cloth_force_falloff_type);
ADDCH(cloth_simulation_area_type);
ADDCH(cloth_deform_type);
break;
}
if (setup_ui) {
BKE_brush_channelset_ui_init(brush, tool);
}
namestack_pop();
}
ATTR_NO_OPT void BKE_brush_channelset_ui_init(Brush *brush, int tool)
{
namestack_push(__func__);
BrushChannelSet *chset = brush->channels;
#ifdef SHOWHDR
# undef SHOWHDR
#endif
#ifdef SHOWWRK
# undef SHOWPROPS
#endif
#ifdef SHOWALL
# undef SHOWALL
#endif
#ifdef SETFLAG_SAFE
# undef SETFLAG_SAFE
#endif
BrushChannel *ch;
#define SETFLAG_SAFE(idname, flag1) \
if (ch = BRUSHSET_LOOKUP(chset, idname)) \
ch->flag |= (flag1)
#define SHOWHDR(idname) SETFLAG_SAFE(idname, BRUSH_CHANNEL_SHOW_IN_HEADER)
#define SHOWWRK(idname) SETFLAG_SAFE(idname, BRUSH_CHANNEL_SHOW_IN_WORKSPACE)
#define SHOWALL(idname) \
SETFLAG_SAFE(idname, BRUSH_CHANNEL_SHOW_IN_WORKSPACE | BRUSH_CHANNEL_SHOW_IN_HEADER)
SHOWALL(radius);
SHOWALL(strength);
SHOWALL(color);
SHOWALL(secondary_color);
SHOWALL(accumulate);
SHOWWRK(use_frontface);
SHOWWRK(autosmooth);
SHOWWRK(topology_rake);
SHOWWRK(normal_radius_factor);
SHOWWRK(hardness);
switch (tool) {
case SCULPT_TOOL_SCRAPE:
case SCULPT_TOOL_FILL:
SHOWWRK(plane_offset);
SHOWWRK(invert_to_scrape_fill);
SHOWWRK(area_radius_factor);
break;
case SCULPT_TOOL_CLAY:
case SCULPT_TOOL_CLAY_STRIPS:
case SCULPT_TOOL_CLAY_THUMB:
case SCULPT_TOOL_FLATTEN:
SHOWWRK(plane_offset);
break;
case SCULPT_TOOL_MULTIPLANE_SCRAPE:
SHOWWRK(plane_offset);
SHOWWRK(use_multiplane_scrape_dynamic);
SHOWWRK(multiplane_scrape_angle);
break;
case SCULPT_TOOL_LAYER:
SHOWWRK(use_persistent);
break;
case SCULPT_TOOL_CLOTH:
SHOWWRK(cloth_deform_type);
SHOWWRK(cloth_force_falloff_type);
SHOWWRK(cloth_simulation_area_type);
break;
}
#undef SHOWALL
#undef SHOWHDR
#undef SHOWPROPS
namestack_pop();
}
ATTR_NO_OPT void BKE_brush_builtin_create(Brush *brush, int tool)
{
namestack_push(__func__);
@ -787,6 +892,10 @@ ATTR_NO_OPT void BKE_brush_builtin_create(Brush *brush, int tool)
GETCH(strength)->fvalue = 1.0;
GETCH(dyntopo_disabled)->ivalue = true;
break;
case SCULPT_TOOL_CLOTH:
GETCH(radius)->mappings[BRUSH_MAPPING_PRESSURE].flag &= ~BRUSH_MAPPING_ENABLED;
GETCH(strength)->mappings[BRUSH_MAPPING_PRESSURE].flag &= ~BRUSH_MAPPING_ENABLED;
break;
default: {
// implement me!
// BKE_brush_channelset_free(chset);
@ -796,6 +905,8 @@ ATTR_NO_OPT void BKE_brush_builtin_create(Brush *brush, int tool)
}
namestack_pop();
BKE_brush_channelset_ui_init(brush, tool);
}
void BKE_brush_init_toolsettings(Sculpt *sd)

View File

@ -572,7 +572,7 @@ static bool buttons_context_path(
path->len++;
}
/* No pinned root, use scene as initial root. */
else if (mainb != BCONTEXT_TOOL) {
else if (!ELEM(mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) {
RNA_id_pointer_create(&scene->id, &path->ptr[0]);
path->len++;
@ -613,6 +613,7 @@ static bool buttons_context_path(
case BCONTEXT_COLLECTION: /* This is for Line Art collection flags */
found = buttons_context_path_collection(C, path, window);
break;
case BCONTEXT_BRUSH_EDITOR:
case BCONTEXT_TOOL:
found = true;
break;
@ -871,7 +872,7 @@ int /*eContextResult*/ buttons_context(const bContext *C,
return CTX_RESULT_MEMBER_NOT_FOUND;
}
if (sbuts->mainb == BCONTEXT_TOOL) {
if (ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) {
return CTX_RESULT_MEMBER_NOT_FOUND;
}
@ -1184,7 +1185,7 @@ int /*eContextResult*/ buttons_context(const bContext *C,
static bool buttons_panel_context_poll(const bContext *C, PanelType *UNUSED(pt))
{
SpaceProperties *sbuts = CTX_wm_space_properties(C);
return sbuts->mainb != BCONTEXT_TOOL;
return !ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR);
}
static void buttons_panel_context_draw(const bContext *C, Panel *panel)

View File

@ -178,6 +178,10 @@ int ED_buttons_tabs_list(SpaceProperties *sbuts, short *context_tabs_array)
context_tabs_array[length] = BCONTEXT_TOOL;
length++;
}
if (sbuts->pathflag & (1 << BCONTEXT_BRUSH_EDITOR)) {
context_tabs_array[length] = BCONTEXT_BRUSH_EDITOR;
length++;
}
if (length != 0) {
context_tabs_array[length] = -1;
length++;
@ -305,6 +309,8 @@ static const char *buttons_main_region_context_string(const short mainb)
return "bone_constraint";
case BCONTEXT_TOOL:
return "tool";
case BCONTEXT_BRUSH_EDITOR:
return "brush_editor";
}
/* All the cases should be handled. */
@ -359,7 +365,7 @@ static bool property_search_for_context(const bContext *C, ARegion *region, Spac
{
const char *contexts[2] = {buttons_main_region_context_string(sbuts->mainb), NULL};
if (sbuts->mainb == BCONTEXT_TOOL) {
if (sbuts->mainb == BCONTEXT_TOOL || sbuts->mainb == BCONTEXT_BRUSH_EDITOR) {
return false;
}
@ -372,7 +378,7 @@ static void property_search_move_to_next_tab_with_results(SpaceProperties *sbuts
const int tabs_len)
{
/* As long as all-tab search in the tool is disabled in the tool context, don't move from it. */
if (sbuts->mainb == BCONTEXT_TOOL) {
if (sbuts->mainb == BCONTEXT_TOOL || sbuts->mainb == BCONTEXT_BRUSH_EDITOR) {
return;
}
@ -513,6 +519,9 @@ static void buttons_main_region_layout(const bContext *C, ARegion *region)
if (sbuts->mainb == BCONTEXT_TOOL) {
ED_view3d_buttons_region_layout_ex(C, region, "Tool");
}
else if (sbuts->mainb == BCONTEXT_BRUSH_EDITOR) {
ED_view3d_buttons_region_layout_ex(C, region, "Tool");
}
else {
buttons_main_region_layout_properties(C, sbuts, region);
}
@ -597,7 +606,7 @@ static void buttons_header_region_message_subscribe(const wmRegionMessageSubscri
WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw);
}
if (sbuts->mainb == BCONTEXT_TOOL) {
if (ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) {
WM_msg_subscribe_rna_anon_prop(mbus, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
}
}
@ -786,6 +795,7 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
case NC_BRUSH:
buttons_area_redraw(area, BCONTEXT_TEXTURE);
buttons_area_redraw(area, BCONTEXT_TOOL);
buttons_area_redraw(area, BCONTEXT_BRUSH_EDITOR);
sbuts->preview = 1;
break;
case NC_TEXTURE:

View File

@ -1351,9 +1351,16 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
case CTX_MODE_POSE:
ARRAY_SET_ITEMS(contexts, ".posemode");
break;
case CTX_MODE_SCULPT:
ARRAY_SET_ITEMS(contexts, ".paint_common", ".sculpt_mode");
case CTX_MODE_SCULPT: {
SpaceProperties *sbuts = CTX_wm_space_properties(C);
if (sbuts && sbuts->mainb == BCONTEXT_BRUSH_EDITOR) {
ARRAY_SET_ITEMS(contexts, ".paint_common", ".brush_editor");
}
else {
ARRAY_SET_ITEMS(contexts, ".paint_common", ".sculpt_mode");
}
break;
}
case CTX_MODE_PAINT_WEIGHT:
ARRAY_SET_ITEMS(contexts, ".paint_common", ".weightpaint");
break;

View File

@ -53,7 +53,8 @@ typedef struct BrushChannel {
float vector[4];
BrushMapping mappings[5]; // should always be BRUSH_MAPPING_MAX
int type, flag;
short type, ui_order;
int flag;
} BrushChannel;
typedef struct BrushChannelSet {
@ -95,7 +96,9 @@ enum {
BRUSH_CHANNEL_NO_MAPPINGS = 1 << 2,
BRUSH_CHANNEL_UI_EXPANDED = 1 << 3,
BRUSH_CHANNEL_APPLY_MAPPING_TO_ALPHA = 1 << 4,
BRUSH_CHANNEL_COLOR = 1 << 5
BRUSH_CHANNEL_COLOR = 1 << 5,
BRUSH_CHANNEL_SHOW_IN_WORKSPACE = 1 << 6,
BRUSH_CHANNEL_SHOW_IN_HEADER = 1 << 7
};
// BrushChannelType->type

View File

@ -230,6 +230,7 @@ typedef enum eSpaceButtons_Context {
BCONTEXT_SHADERFX = 15,
BCONTEXT_OUTPUT = 16,
BCONTEXT_COLLECTION = 17,
BCONTEXT_BRUSH_EDITOR = 18,
/* Keep last. */
BCONTEXT_TOT,

View File

@ -378,6 +378,11 @@ void RNA_def_brush_channel(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Value", "Current value");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
prop = RNA_def_property(srna, "ui_order", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, "BrushChannel", "ui_order");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Ordering", "Order of brush channel in panels and the header");
prop = RNA_def_property(srna, "int_value", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, "BrushChannel", "ivalue");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
@ -421,6 +426,16 @@ void RNA_def_brush_channel(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Inherit", "Inherit from scene defaults");
prop = RNA_def_property(srna, "show_in_header", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, "BrushChannel", "flag", BRUSH_CHANNEL_SHOW_IN_HEADER);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "In Header", "Show in header");
prop = RNA_def_property(srna, "show_in_workspace", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, "BrushChannel", "flag", BRUSH_CHANNEL_SHOW_IN_WORKSPACE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "In Workspace", "Show in workspace");
prop = RNA_def_property(srna, "is_color", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, "BrushChannel", "flag", BRUSH_CHANNEL_COLOR);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);

View File

@ -458,6 +458,7 @@ const EnumPropertyItem rna_enum_clip_editor_mode_items[] = {
* but helps for context-less access (e.g. doc, i18n...). */
static const EnumPropertyItem buttons_context_items[] = {
{BCONTEXT_TOOL, "TOOL", ICON_TOOL_SETTINGS, "Tool", "Active Tool and Workspace settings"},
{BCONTEXT_BRUSH_EDITOR, "BRUSH_EDITOR", ICON_BRUSH_DATA, "Brush", "Brush Settings"},
{BCONTEXT_SCENE, "SCENE", ICON_SCENE_DATA, "Scene", "Scene Properties"},
{BCONTEXT_RENDER, "RENDER", ICON_SCENE, "Render", "Render Properties"},
{BCONTEXT_OUTPUT, "OUTPUT", ICON_OUTPUT, "Output", "Output Properties"},