UI: preset popover buttons in panel headers.

Moves the preset into a menu for the panel header, so it can be changed
without opening the panel and takes up less space. Two remaining issues:

* For long lists the add new preset button can be scrolled off screen.
* We should support showing the name of the chosen preset in the panel
  header, but the current preset system does not support detecting which
  preset is used.

Differential Revision: https://developer.blender.org/D3366
This commit is contained in:
Brecht Van Lommel 2018-04-27 13:50:26 +02:00
parent 1664ccb675
commit 7a10cfe7fe
20 changed files with 291 additions and 172 deletions

View File

@ -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_header_preset(self, context):
CYCLES_MT_integrator_presets.draw_panel_header(self.layout)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
cscene = scene.cycles
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
pass
class CYCLES_RENDER_PT_light_paths_max_bounces(CyclesButtonsPanel, Panel):

View File

@ -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"

Before

Width:  |  Height:  |  Size: 4.4 MiB

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

Binary file not shown.

View File

@ -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):

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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()

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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()

View File

@ -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
@ -285,14 +289,6 @@ class CLIP_PT_tracking_settings(CLIP_PT_tracking_panel, Panel):
layout = self.layout
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)
@ -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):

View File

@ -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):

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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");