diff options
20 files changed, 291 insertions, 172 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index c514ec1704e..c5eafe9ebfb 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -18,6 +18,7 @@ import bpy from bpy_extras.node_utils import find_node_input, find_output_node +from bl_operators.presets import PresetMenu from bpy.types import ( Panel, @@ -26,20 +27,20 @@ from bpy.types import ( ) -class CYCLES_MT_sampling_presets(Menu): +class CYCLES_MT_sampling_presets(PresetMenu): bl_label = "Sampling Presets" preset_subdir = "cycles/sampling" preset_operator = "script.execute_preset" + preset_add_operator = "render.cycles_sampling_preset_add" COMPAT_ENGINES = {'CYCLES'} - draw = Menu.draw_preset -class CYCLES_MT_integrator_presets(Menu): +class CYCLES_MT_integrator_presets(PresetMenu): bl_label = "Integrator Presets" preset_subdir = "cycles/integrator" preset_operator = "script.execute_preset" + preset_add_operator = "render.cycles_integrator_preset_add" COMPAT_ENGINES = {'CYCLES'} - draw = Menu.draw_preset class CyclesButtonsPanel: @@ -144,6 +145,9 @@ class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel): bl_label = "Sampling" bl_options = {'DEFAULT_CLOSED'} + def draw_header_preset(self, context): + CYCLES_MT_sampling_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout layout.use_property_split = False @@ -151,11 +155,6 @@ class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel): scene = context.scene cscene = scene.cycles - row = layout.row(align=True) - row.menu("CYCLES_MT_sampling_presets", text=bpy.types.CYCLES_MT_sampling_presets.bl_label) - row.operator("render.cycles_sampling_preset_add", text="", icon="ZOOMIN") - row.operator("render.cycles_sampling_preset_add", text="", icon="ZOOMOUT").remove_active = True - layout.use_property_split = True layout.prop(cscene, "progressive") @@ -315,17 +314,11 @@ class CYCLES_RENDER_PT_light_paths(CyclesButtonsPanel, Panel): bl_label = "Light Paths" bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - scene = context.scene - cscene = scene.cycles + def draw_header_preset(self, context): + CYCLES_MT_integrator_presets.draw_panel_header(self.layout) - row = layout.row(align=True) - row.menu("CYCLES_MT_integrator_presets", text=bpy.types.CYCLES_MT_integrator_presets.bl_label) - row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMIN") - row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMOUT").remove_active = True + def draw(self, context): + pass class CYCLES_RENDER_PT_light_paths_max_bounces(CyclesButtonsPanel, Panel): diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 4d5376913ab..d389831e668 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -31910,6 +31910,15 @@ id="radialGradient16215" xlink:href="#linearGradient18134" inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient27277-1-8" + id="linearGradient18811" + gradientUnits="userSpaceOnUse" + x1="4.1933641" + y1="199.12067" + x2="17.16466" + y2="211.01585" /> </defs> <sodipodi:namedview id="base" @@ -92657,6 +92666,61 @@ y="75.5" /> </g> </g> + <g + transform="translate(461.71013,377.29483)" + style="display:inline;enable-background:new" + id="ICON_SOLO_OFF-7"> + <rect + y="198.9792" + x="4.9506397" + height="16" + width="16" + id="rect23018-5-4-5" + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" /> + <g + transform="matrix(0.94058502,0,0,0.94058502,0.9128606,12.74924)" + id="g56716-3" /> + </g> + <g + transform="translate(461.99301,376.87052)" + style="display:inline;enable-background:new" + id="ICON_SOLO_OFF-1"> + <rect + y="198.9792" + x="4.9506397" + height="16" + width="16" + id="rect23018-5-4-2" + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" /> + <g + transform="matrix(0.94058502,0,0,0.94058502,0.9128606,12.74924)" + id="g56716-7"> + <path + sodipodi:type="star" + style="fill:url(#linearGradient18811);fill-opacity:1.0;stroke:#000000;stroke-width:0.96882826;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.80000001" + id="path15855-0" + sodipodi:sides="5" + sodipodi:cx="13.700194" + sodipodi:cy="207.20645" + sodipodi:r1="7.1873641" + sodipodi:r2="3.3158474" + sodipodi:arg1="0.94697287" + sodipodi:arg2="1.5618338" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="m 17.898641,213.04008 -4.168729,-2.51791 -4.280439,2.47993 1.106473,-4.74277 -3.6812884,-3.3046 4.8525664,-0.41328 2.005278,-4.52229 1.892578,4.48735 4.920618,0.50967 -3.682889,3.18662 z" + inkscape:transform-center-x="-0.010954063" + inkscape:transform-center-y="-0.74285516" + transform="matrix(1.0972098,0,0,1.0975406,-2.0923019,-19.740595)" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:1.06316817;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:0.51147538" + d="m 12.931855,202.51514 -1.201334,2.70994 c -0.137665,0.32193 -0.454082,0.55986 -0.800889,0.60222 l -2.9032248,0.26765 2.2358168,1.97391 c 0.261321,0.2395 0.380487,0.62447 0.300333,0.97022 l -0.667408,2.81032" + id="path15869-9" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccc" /> + </g> + </g> </g> <g inkscape:groupmode="layer" diff --git a/release/datafiles/blender_icons16/icon16_preset.dat b/release/datafiles/blender_icons16/icon16_preset.dat Binary files differnew file mode 100644 index 00000000000..52026e95a1a --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_preset.dat diff --git a/release/datafiles/blender_icons32/icon32_preset.dat b/release/datafiles/blender_icons32/icon32_preset.dat Binary files differnew file mode 100644 index 00000000000..57d5d2536f1 --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_preset.dat diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 3a9704b12e9..6cca60bd49e 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -856,7 +856,8 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): def path_menu(self, searchpaths, operator, *, props_default=None, prop_filepath="filepath", - filter_ext=None, filter_path=None, display_name=None): + filter_ext=None, filter_path=None, display_name=None, + add_operator=None): """ Populate a menu from a list of paths. @@ -902,12 +903,16 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): files.sort() + col = layout.column(align=True) + for f, filepath in files: # Intentionally pass the full path to 'display_name' callback, # since the callback may want to use part a directory in the name. - props = layout.operator( + row = col.row(align=True) + name = display_name(filepath) if display_name else bpy.path.display_name(f) + props = row.operator( operator, - text=display_name(filepath) if display_name else bpy.path.display_name(f), + text=name, translate=False, ) @@ -919,6 +924,25 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): if operator == "script.execute_preset": props.menu_idname = self.bl_idname + if add_operator: + props = row.operator(add_operator, text="", icon='ZOOMOUT') + props.name = name + props.remove_name = True + + if add_operator: + wm = bpy.data.window_managers[0] + + layout.separator() + row = layout.row() + + sub = row.row() + sub.emboss = 'NORMAL' + sub.prop(wm, "preset_name", text="") + + props = row.operator(add_operator, text="", icon='ZOOMIN') + props.name = wm.preset_name + + def draw_preset(self, context): """ Define these on the subclass: @@ -926,16 +950,19 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): - preset_subdir (string) Optionally: + - preset_add_operator (string) - preset_extensions (set of strings) - preset_operator_defaults (dict of keyword args) """ import bpy ext_valid = getattr(self, "preset_extensions", {".py", ".xml"}) props_default = getattr(self, "preset_operator_defaults", None) + add_operator = getattr(self, "preset_add_operator", None) self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator, props_default=props_default, - filter_ext=lambda ext: ext.lower() in ext_valid) + filter_ext=lambda ext: ext.lower() in ext_valid, + add_operator=add_operator) @classmethod def draw_collapsible(cls, context, layout): diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index deca40909ae..074177bc4b7 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -19,9 +19,15 @@ # <pep8 compliant> import bpy -from bpy.types import Menu, Operator +from bpy.types import Menu, Operator, Panel, WindowManager from bpy.props import StringProperty, BoolProperty +# For preset popover menu +WindowManager.preset_name = StringProperty( + name="Preset Name", + description="Name for new preset", + default="New Preset" +) class AddPresetBase: """Base preset class, only for subclassing @@ -40,6 +46,10 @@ class AddPresetBase: maxlen=64, options={'SKIP_SAVE'}, ) + remove_name = BoolProperty( + default=False, + options={'HIDDEN', 'SKIP_SAVE'}, + ) remove_active = BoolProperty( default=False, options={'HIDDEN', 'SKIP_SAVE'}, @@ -48,6 +58,7 @@ class AddPresetBase: # needed for mix-ins order = [ "name", + "remove_name", "remove_active", ] @@ -85,11 +96,17 @@ class AddPresetBase: else: ext = ".py" - if not self.remove_active: - name = self.name.strip() + name = self.name.strip() + if not (self.remove_name or self.remove_active): + if not name: return {'FINISHED'} + # Reset preset name + wm = bpy.data.window_managers[0] + if name == wm.preset_name: + wm.preset_name = 'New Preset' + filename = self.as_filename(name) target_path = os.path.join("presets", self.preset_subdir) @@ -155,15 +172,16 @@ class AddPresetBase: preset_menu_class.bl_label = bpy.path.display_name(filename) else: - preset_active = preset_menu_class.bl_label + if self.remove_active: + name = preset_menu_class.bl_label # fairly sloppy but convenient. - filepath = bpy.utils.preset_find(preset_active, + filepath = bpy.utils.preset_find(name, self.preset_subdir, ext=ext) if not filepath: - filepath = bpy.utils.preset_find(preset_active, + filepath = bpy.utils.preset_find(name, self.preset_subdir, display_name=True, ext=ext) @@ -194,7 +212,7 @@ class AddPresetBase: self.name = self.as_filename(self.name.strip()) def invoke(self, context, event): - if not self.remove_active: + if not (self.remove_active or self.remove_name): wm = context.window_manager return wm.invoke_props_dialog(self) else: @@ -241,6 +259,40 @@ class ExecutePreset(Operator): return {'FINISHED'} +class PresetMenu(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'HEADER' + bl_label = "Presets" + path_menu = Menu.path_menu + + @classmethod + def draw_panel_header(cls, layout): + layout.emboss = 'NONE' + layout.popover(cls.bl_space_type, + cls.bl_region_type, + cls.__name__, + icon='PRESET', + text='') + + @classmethod + def draw_menu(cls, layout, text=None): + if text == None: + text = cls.bl_label + + layout.popover(cls.bl_space_type, + cls.bl_region_type, + cls.__name__, + icon='PRESET', + text=text) + + def draw(self, context): + layout = self.layout + layout.emboss = 'PULLDOWN_MENU' + layout.operator_context = 'EXEC_DEFAULT' + + Menu.draw_preset(self, context) + + class AddPresetRender(AddPresetBase, Operator): """Add or remove a Render Preset""" bl_idname = "render.preset_add" @@ -385,35 +437,6 @@ class AddPresetHairDynamics(AddPresetBase, Operator): ] -class AddPresetSunSky(AddPresetBase, Operator): - """Add or remove a Sky & Atmosphere Preset""" - bl_idname = "lamp.sunsky_preset_add" - bl_label = "Add Sunsky Preset" - preset_menu = "LAMP_MT_sunsky_presets" - - preset_defines = [ - "sky = bpy.context.lamp.sky" - ] - - preset_values = [ - "sky.atmosphere_extinction", - "sky.atmosphere_inscattering", - "sky.atmosphere_turbidity", - "sky.backscattered_light", - "sky.horizon_brightness", - "sky.spread", - "sky.sun_brightness", - "sky.sun_intensity", - "sky.sun_size", - "sky.sky_blend", - "sky.sky_blend_type", - "sky.sky_color_space", - "sky.sky_exposure", - ] - - preset_subdir = "sunsky" - - class AddPresetInteraction(AddPresetBase, Operator): """Add or remove an Application Interaction Preset""" bl_idname = "wm.interaction_preset_add" @@ -665,7 +688,6 @@ classes = ( AddPresetOperator, AddPresetRender, AddPresetSafeAreas, - AddPresetSunSky, AddPresetTrackingCamera, AddPresetTrackingSettings, AddPresetTrackingTrackColor, diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 132a226f287..22c7964173b 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -20,6 +20,7 @@ import bpy from bpy.types import Panel, Menu from rna_prop_ui import PropertyPanel +from bl_operators.presets import PresetMenu class CameraButtonsPanel: @@ -33,20 +34,20 @@ class CameraButtonsPanel: return context.camera and (engine in cls.COMPAT_ENGINES) -class CAMERA_MT_presets(Menu): +class CAMERA_MT_presets(PresetMenu): bl_label = "Camera Presets" preset_subdir = "camera" preset_operator = "script.execute_preset" + preset_add_operator = "camera.preset_add" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} - draw = Menu.draw_preset -class SAFE_AREAS_MT_presets(Menu): +class SAFE_AREAS_MT_presets(PresetMenu): bl_label = "Camera Presets" preset_subdir = "safe_areas" preset_operator = "script.execute_preset" + preset_add_operator = "safe_areas.preset_add" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} - draw = Menu.draw_preset class DATA_PT_context_camera(CameraButtonsPanel, Panel): @@ -185,17 +186,14 @@ class DATA_PT_camera(CameraButtonsPanel, Panel): bl_label = "Camera" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} + def draw_header_preset(self, context): + CAMERA_MT_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout cam = context.camera - row = layout.row(align=True) - - row.menu("CAMERA_MT_presets", text=bpy.types.CAMERA_MT_presets.bl_label) - row.operator("camera.preset_add", text="", icon='ZOOMIN') - row.operator("camera.preset_add", text="", icon='ZOOMOUT').remove_active = True - layout.use_property_split = True col = layout.column() @@ -410,6 +408,9 @@ class DATA_PT_camera_safe_areas(CameraButtonsPanel, Panel): self.layout.prop(cam, "show_safe_areas", text="") + def draw_header_preset(self, context): + SAFE_AREAS_MT_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout safe_data = context.scene.safe_areas @@ -430,13 +431,6 @@ def draw_display_safe_settings(layout, safe_data, settings): layout.use_property_split = True - row = layout.row(align=True) - row.menu("SAFE_AREAS_MT_presets", text=bpy.types.SAFE_AREAS_MT_presets.bl_label) - row.operator("safe_areas.preset_add", text="", icon='ZOOMIN') - row.operator("safe_areas.preset_add", text="", icon='ZOOMOUT').remove_active = True - - layout.separator() - col = layout.column() col.active = show_safe_areas diff --git a/release/scripts/startup/bl_ui/properties_data_lamp.py b/release/scripts/startup/bl_ui/properties_data_lamp.py index 28f1b60e468..d613967584c 100644 --- a/release/scripts/startup/bl_ui/properties_data_lamp.py +++ b/release/scripts/startup/bl_ui/properties_data_lamp.py @@ -22,14 +22,6 @@ from bpy.types import Menu, Panel from rna_prop_ui import PropertyPanel -class LAMP_MT_sunsky_presets(Menu): - bl_label = "Sun & Sky Presets" - preset_subdir = "sunsky" - preset_operator = "script.execute_preset" - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} - draw = Menu.draw_preset - - class DataButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -352,7 +344,6 @@ class DATA_PT_custom_props_lamp(DataButtonsPanel, PropertyPanel, Panel): classes = ( - LAMP_MT_sunsky_presets, DATA_PT_context_lamp, DATA_PT_preview, DATA_PT_lamp, diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index ebfa5ec27d0..1e08b8e4b46 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -21,6 +21,7 @@ import bpy from bpy.types import Panel, Menu from rna_prop_ui import PropertyPanel from bpy.app.translations import pgettext_iface as iface_ +from bl_operators.presets import PresetMenu from .properties_physics_common import ( point_cache_ui, @@ -82,12 +83,12 @@ class PARTICLE_MT_specials(Menu): layout.operator("particle.duplicate_particle_system") -class PARTICLE_MT_hair_dynamics_presets(Menu): +class PARTICLE_MT_hair_dynamics_presets(PresetMenu): bl_label = "Hair Dynamics Presets" preset_subdir = "hair_dynamics" preset_operator = "script.execute_preset" + preset_add_operator = "particle.hair_dynamics_preset_add" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} - draw = Menu.draw_preset class ParticleButtonsPanel: @@ -340,6 +341,16 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel): psys = context.particle_system self.layout.prop(psys, "use_hair_dynamics", text="") + def draw_header_preset(self, context): + psys = context.particle_system + + if not psys.cloth: + return + + layout = self.layout + layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False + PARTICLE_MT_hair_dynamics_presets.draw_panel_header(layout) + def draw(self, context): layout = self.layout @@ -355,11 +366,6 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel): layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False - row = layout.row(align=True) - row.menu("PARTICLE_MT_hair_dynamics_presets", text=bpy.types.PARTICLE_MT_hair_dynamics_presets.bl_label) - row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMIN') - row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMOUT').remove_active = True - layout.use_property_split = True layout.separator() diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py index f450bc61635..32343959a64 100644 --- a/release/scripts/startup/bl_ui/properties_physics_cloth.py +++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py @@ -19,6 +19,7 @@ # <pep8 compliant> import bpy from bpy.types import Menu, Panel +from bl_operators.presets import PresetMenu from .properties_physics_common import ( point_cache_ui, @@ -30,11 +31,11 @@ def cloth_panel_enabled(md): return md.point_cache.is_baked is False -class CLOTH_MT_presets(Menu): +class CLOTH_MT_presets(PresetMenu): bl_label = "Cloth Presets" preset_subdir = "cloth" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "cloth.preset_add" class PhysicButtonsPanel: @@ -52,6 +53,9 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel): bl_label = "Cloth" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} + def draw_header_preset(self, context): + CLOTH_MT_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout @@ -63,16 +67,6 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel): split = layout.split(percentage=0.25) - col = split.column() - - split.label(text="Presets:") - sub = split.row(align=True) - sub.menu("CLOTH_MT_presets", text=bpy.types.CLOTH_MT_presets.bl_label) - sub.operator("cloth.preset_add", text="", icon='ZOOMIN') - sub.operator("cloth.preset_add", text="", icon='ZOOMOUT').remove_active = True - - split = layout.split(percentage=0.25) - split.label(text="Quality:") split.prop(cloth, "quality", text="Steps") diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index 93f1d28e536..6b3dd349f0d 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -20,13 +20,14 @@ import bpy from bpy.types import Panel, Menu from bpy.app.translations import pgettext_iface as iface_ +from bl_operators.presets import PresetMenu -class FLUID_MT_presets(Menu): +class FLUID_MT_presets(PresetMenu): bl_label = "Fluid Presets" preset_subdir = "fluid" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "fluid.preset_add" class PhysicButtonsPanel: @@ -240,11 +241,7 @@ class PHYSICS_PT_domain_gravity(PhysicButtonsPanel, Panel): col.prop(fluid, "simulation_scale", text="Meters") col = split.column() - col.label(text="Viscosity Presets:") - sub = col.row(align=True) - sub.menu("FLUID_MT_presets", text=bpy.types.FLUID_MT_presets.bl_label) - sub.operator("fluid.preset_add", text="", icon='ZOOMIN') - sub.operator("fluid.preset_add", text="", icon='ZOOMOUT').remove_active = True + FLUID_MT_presets.draw_menu(col, text="Viscosity Presets") sub = col.column(align=True) sub.prop(fluid, "viscosity_base", text="Base") diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 17026537718..3af16910c5b 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -20,20 +20,20 @@ # <pep8 compliant> import bpy from bpy.types import Menu, Panel, UIList +from bl_operators.presets import PresetMenu -class RENDER_MT_presets(Menu): +class RENDER_MT_presets(PresetMenu): bl_label = "Render Presets" preset_subdir = "render" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "render.preset_add" -class RENDER_MT_ffmpeg_presets(Menu): +class RENDER_MT_ffmpeg_presets(PresetMenu): bl_label = "FFMPEG Presets" preset_subdir = "ffmpeg" preset_operator = "script.python_file_run" - draw = Menu.draw_preset class RENDER_MT_framerate_presets(Menu): @@ -83,6 +83,9 @@ class RENDER_PT_dimensions(RenderButtonsPanel, Panel): _frame_rate_args_prev = None _preset_class = None + def draw_header_preset(self, context): + RENDER_MT_presets.draw_panel_header(self.layout) + @staticmethod def _draw_framerate_label(*args): # avoids re-creating text string each draw @@ -131,11 +134,6 @@ class RENDER_PT_dimensions(RenderButtonsPanel, Panel): scene = context.scene rd = scene.render - row = layout.row(align=True) - row.menu("RENDER_MT_presets", text=bpy.types.RENDER_MT_presets.bl_label) - row.operator("render.preset_add", text="", icon='ZOOMIN') - row.operator("render.preset_add", text="", icon='ZOOMOUT').remove_active = True - col = layout.column(align=True) col.prop(rd, "resolution_x", text="Resolution X") col.prop(rd, "resolution_y", text="Y") @@ -297,6 +295,9 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel): bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} + def draw_header_preset(self, context): + RENDER_MT_ffmpeg_presets.draw_panel_header(self.layout) + @classmethod def poll(cls, context): rd = context.scene.render @@ -308,8 +309,6 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel): rd = context.scene.render ffmpeg = rd.ffmpeg - layout.menu("RENDER_MT_ffmpeg_presets", text="Presets") - split = layout.split() split.prop(rd.ffmpeg, "format") split.prop(ffmpeg, "use_autosplit") diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index bcffe9795cc..9c5cf3a4d6d 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -25,6 +25,7 @@ from bpy.types import ( ) from rna_prop_ui import PropertyPanel +from bl_operators.presets import PresetMenu from .properties_physics_common import ( point_cache_ui, @@ -32,12 +33,12 @@ from .properties_physics_common import ( ) -class SCENE_MT_units_length_presets(Menu): +class SCENE_MT_units_length_presets(PresetMenu): """Unit of measure for properties that use length values""" bl_label = "Unit Presets" preset_subdir = "units_length" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "scene.units_length_preset_add" class SCENE_UL_keying_set_paths(UIList): @@ -81,16 +82,14 @@ class SCENE_PT_unit(SceneButtonsPanel, Panel): bl_label = "Units" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} + def draw_header_preset(self, context): + SCENE_MT_units_length_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout unit = context.scene.unit_settings - row = layout.row(align=True) - row.menu("SCENE_MT_units_length_presets", text=SCENE_MT_units_length_presets.bl_label) - row.operator("scene.units_length_preset_add", text="", icon='ZOOMIN') - row.operator("scene.units_length_preset_add", text="", icon='ZOOMOUT').remove_active = True - layout.use_property_split = True col = layout.column() diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index a865eab1dbe..91e725b451d 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -21,6 +21,7 @@ import bpy from bpy.types import Panel, Header, Menu, UIList from bpy.app.translations import pgettext_iface as iface_ +from bl_operators.presets import PresetMenu from .properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, @@ -277,6 +278,9 @@ class CLIP_PT_tracking_settings(CLIP_PT_tracking_panel, Panel): bl_label = "Tracking Settings" bl_category = "Track" + def draw_header_preset(self, context): + CLIP_MT_tracking_settings_presets.draw_panel_header(self.layout) + def draw(self, context): sc = context.space_data @@ -286,14 +290,6 @@ class CLIP_PT_tracking_settings(CLIP_PT_tracking_panel, Panel): col = layout.column() row = col.row(align=True) - label = CLIP_MT_tracking_settings_presets.bl_label - row.menu('CLIP_MT_tracking_settings_presets', text=label) - row.operator("clip.tracking_settings_preset_add", - text="", icon='ZOOMIN') - row.operator("clip.tracking_settings_preset_add", - text="", icon='ZOOMOUT').remove_active = True - - row = col.row(align=True) row.prop(settings, "use_default_red_channel", text="R", toggle=True) row.prop(settings, "use_default_green_channel", @@ -625,12 +621,8 @@ class CLIP_PT_track(CLIP_PT_tracking_panel, Panel): layout.separator() row = layout.row(align=True) - label = bpy.types.CLIP_MT_track_color_presets.bl_label - row.menu('CLIP_MT_track_color_presets', text=label) + CLIP_MT_track_color_presets.draw_menu(row, 'Color Presets') row.menu('CLIP_MT_track_color_specials', text="", icon='DOWNARROW_HLT') - row.operator("clip.track_color_preset_add", text="", icon='ZOOMIN') - row.operator("clip.track_color_preset_add", - text="", icon='ZOOMOUT').remove_active = True row = layout.row() row.prop(act_track, "use_custom_color") @@ -720,19 +712,15 @@ class CLIP_PT_tracking_camera(Panel): return False + def draw_header_preset(self, context): + CLIP_MT_camera_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout sc = context.space_data clip = sc.clip - row = layout.row(align=True) - label = bpy.types.CLIP_MT_camera_presets.bl_label - row.menu('CLIP_MT_camera_presets', text=label) - row.operator("clip.camera_preset_add", text="", icon='ZOOMIN') - row.operator("clip.camera_preset_add", text="", - icon='ZOOMOUT').remove_active = True - col = layout.column(align=True) col.label(text="Sensor:") col.prop(clip.tracking.camera, "sensor_width", text="Width") @@ -1431,28 +1419,28 @@ class CLIP_MT_tracking_specials(Menu): text="Unlock Tracks").action = 'UNLOCK' -class CLIP_MT_camera_presets(Menu): +class CLIP_MT_camera_presets(PresetMenu): """Predefined tracking camera intrinsics""" bl_label = "Camera Presets" preset_subdir = "tracking_camera" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "clip.camera_preset_add" -class CLIP_MT_track_color_presets(Menu): +class CLIP_MT_track_color_presets(PresetMenu): """Predefined track color""" bl_label = "Color Presets" preset_subdir = "tracking_track_color" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "clip.track_color_preset_add" -class CLIP_MT_tracking_settings_presets(Menu): +class CLIP_MT_tracking_settings_presets(PresetMenu): """Predefined tracking settings""" bl_label = "Tracking Presets" preset_subdir = "tracking_settings" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "clip.tracking_settings_preset_add" class CLIP_MT_track_color_specials(Menu): diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 1b6baa7d3d9..7c9c47f8eb8 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -21,6 +21,7 @@ import bpy import nodeitems_utils from bpy.types import Header, Menu, Panel from bpy.app.translations import pgettext_iface as iface_ +from bl_operators.presets import PresetMenu from .properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, @@ -289,12 +290,12 @@ class NODE_MT_node(Menu): layout.operator("node.read_fullsamplelayers") -class NODE_MT_node_color_presets(Menu): +class NODE_MT_node_color_presets(PresetMenu): """Predefined node color""" bl_label = "Color Presets" preset_subdir = "node_color" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "node.node_color_preset_add" class NODE_MT_node_color_specials(Menu): @@ -373,6 +374,9 @@ class NODE_PT_active_node_color(Panel): node = context.active_node self.layout.prop(node, "use_custom_color", text="") + def draw_header_preset(self, context): + NODE_MT_node_color_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout node = context.active_node @@ -380,13 +384,8 @@ class NODE_PT_active_node_color(Panel): layout.enabled = node.use_custom_color row = layout.row() - col = row.column() - col.menu("NODE_MT_node_color_presets") - col.prop(node, "color", text="") - col = row.column(align=True) - col.operator("node.node_color_preset_add", text="", icon='ZOOMIN').remove_active = False - col.operator("node.node_color_preset_add", text="", icon='ZOOMOUT').remove_active = True - col.menu("NODE_MT_node_color_specials", text="", icon='DOWNARROW_HLT') + row.prop(node, "color", text="") + row.menu("NODE_MT_node_color_specials", text="", icon='DOWNARROW_HLT') class NODE_PT_active_node_properties(Panel): diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 4270e6eb1b4..1b42ce97940 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -216,6 +216,8 @@ typedef struct PanelType { int (*poll)(const struct bContext *C, struct PanelType *pt); /* draw header (optional) */ void (*draw_header)(const struct bContext *C, struct Panel *pa); + /* draw header preset (optional) */ + void (*draw_header_preset)(const struct bContext *C, struct Panel *pa); /* draw entirely, view changes should be handled here */ void (*draw)(const struct bContext *C, struct Panel *pa); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 3bc1255d23f..659f6c97696 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -113,9 +113,7 @@ DEF_ICON(FILE_TICK) DEF_ICON(QUIT) DEF_ICON(URL) DEF_ICON(RECOVER_LAST) -#ifndef DEF_ICON_BLANK_SKIP - DEF_ICON(BLANK038) -#endif +DEF_ICON(PRESET) DEF_ICON(FULLSCREEN_ENTER) DEF_ICON(FULLSCREEN_EXIT) DEF_ICON(BLANK1) // Not actually blank - this is used all over the place diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index e769d367b45..fb14ca745c6 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -168,11 +168,17 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v block->my = handle->prev_my; } - /* Prefer popover from header to be positioned into the editor. */ if (!slideout) { ScrArea *sa = CTX_wm_area(C); - if (sa && ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { - ARegion *ar = CTX_wm_region(C); + ARegion *ar = CTX_wm_region(C); + + if (ar && ar->panels.first) { + /* For regions with panels, prefer to open to top so we can + * see the values of the buttons below changing. */ + UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); + } + else if (sa && ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { + /* Prefer popover from header to be positioned into the editor. */ if (ar && ar->regiontype == RGN_TYPE_HEADER) { UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 478afa64939..02ce64bc915 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1861,6 +1861,21 @@ static void ed_panel_draw(const bContext *C, /* bad fixed values */ int xco, yco, h = 0; + if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { + /* for preset menu */ + panel->layout = UI_block_layout( + block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, + 0, (UI_UNIT_Y * 1.1f) + style->panelspace, UI_UNIT_Y, 1, 0, style); + + pt->draw_header_preset(C, panel); + + int headerend = w - UI_UNIT_X; + + UI_block_layout_resolve(block, &xco, &yco); + UI_block_translate(block, headerend - xco, 0); + panel->layout = NULL; + } + if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { int labelx, labely; UI_panel_label_offset(block, &labelx, &labely); diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index ebee502515f..8083ae35dc1 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -166,6 +166,24 @@ static void panel_draw_header(const bContext *C, Panel *pnl) RNA_parameter_list_free(&list); } +static void panel_draw_header_preset(const bContext *C, Panel *pnl) +{ + extern FunctionRNA rna_Panel_draw_header_preset_func; + + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(&CTX_wm_screen(C)->id, pnl->type->ext.srna, pnl, &ptr); + func = &rna_Panel_draw_header_preset_func; + + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + pnl->type->ext.call((bContext *)C, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + static void rna_Panel_unregister(Main *UNUSED(bmain), StructRNA *type) { ARegionType *art; @@ -199,7 +217,7 @@ static StructRNA *rna_Panel_register( PanelType *pt, *parent = NULL, dummypt = {NULL}; Panel dummypanel = {NULL}; PointerRNA dummyptr; - int have_function[3]; + int have_function[4]; /* setup dummy panel & panel type to store static properties in */ dummypanel.type = &dummypt; @@ -267,6 +285,7 @@ static StructRNA *rna_Panel_register( pt->poll = (have_function[0]) ? panel_poll : NULL; pt->draw = (have_function[1]) ? panel_draw : NULL; pt->draw_header = (have_function[2]) ? panel_draw_header : NULL; + pt->draw_header_preset = (have_function[3]) ? panel_draw_header_preset : NULL; /* XXX use "no header" flag for some ordering of panels until we have real panel ordering */ if (pt->flag & PNL_NO_HEADER) { @@ -1058,6 +1077,12 @@ static void rna_def_panel(BlenderRNA *brna) parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + func = RNA_def_function(srna, "draw_header_preset", NULL); + RNA_def_function_ui_description(func, "Draw UI elements for presets in the panel's header"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + prop = RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "UILayout"); RNA_def_property_ui_text(prop, "Layout", "Defines the structure of the panel in the UI"); |